一、为何会丢包
经过博主长期使用的经验所得,使用UDP协议在互联网上传输数据时,存在一定的丢包率。尤其是在晚间繁忙时段,丢包率更为明显,那么我们如何进行优化来降低丢包率呢?
二、如何解决
- 服务器端与客户端同时进行校验,在一定时间内如果发现有丢包时,发送一条新的消息通知对方重新发送该条消息
- 服务器端与客户端在发送消息时,每次都将一条消息发送多次
我们先来看下第一种方案:这种方案属于最传统的方式,不过该方法有一个缺点。当网络状况非常差时,发出去的请求重发消息也可能会丢失,因此对端可能会再次请求丢失的那个请求重发的报文。与TCP类似,这将导致整个网络变的更差。
让我们来看下第二种方案:对于每一条消息,两端都进行了多次的发送,理论上整体带宽将被降低。在网络状况不好的情况下,假设一条消息我们发送5次,实际上并不会这么巧合的这5次都被丢包了。因此我更倾向于这个方案,在实际测试过程中我们发现带宽并没有降低,反而下载文件的速度比以前更稳定了。
三、如何实现
- 首先修改最底层的发包函数write_c
ssize_t write_c(client_t* client, const void* buf, size_t count) { unsigned char i; unsigned char successed = 0; for (i = 0; i < qtun->multi_send; ++i) { if (qtun->use_udp) { if (sendto(qtun->remotefd, buf, (int)count, 0, (struct sockaddr*)&client->addr, sizeof(client->addr)) >= 0) ++successed; } else { const char* ptr = buf; size_t left = count; while (left) { ssize_t written = write(client->fd, ptr, (unsigned int)left); if (written == 0) return 0; else if (written == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) continue; return -1; } ptr += written; left -= written; } ++successed; } } return count; }
这里我们根据配置文件中的multi_send参数来决定要发送多少次,在TCP模式中multi_send参数总是为1
- 我们定义一个msg_state_t结构来保存每一个收到的消息
typedef struct { unsigned int ident; unsigned short idx; unsigned char used; } msg_state_t;
- 在client_t结构中我们总是记录MSG_MAX_TTL条收到的消息id
msg_state_t recv_msgs[MSG_MAX_TTL];
- 在收到一条消息时,我们需要优先检查对应client_t结构中的recv_msg表内是否已记录了该消息的序号,如果该表中已包含了该消息的序号,则需要将该消息丢掉
四、增强安全性
为了增强安全性,在新版本中qtun默认禁止局域网内与其他主机通信,通过在配置文件中指定use_local_forward标志来开机局域网内的通信。具体的实现方法为:在server_process函数中检查数据包的源地址与目的地址是否都为当前局域网内的,如果都是当前局域网内的数据包,则直接进行丢包
if (!qtun->use_local_forward && check_ip_by_mask(ipHdr->saddr, qtun->localip, qtun->netmask) && check_ip_by_mask(ipHdr->daddr, qtun->localip, qtun->netmask)) { return; }
五、完整代码
完整代码可到step16中查看
版本号:1.1.0
日期:2015-06-13
时间: 2024-10-01 07:44:37