UDP丢包问题分析

主要丢包缘由

一、接收端处理时间过长致使丢包:调用recv方法接收端收到数据后,处理数据消耗一些时间,处理完后再次调用recv方法,在这
二次调用间隔里,发过来的包可能丢失。对于这种状况能够修改接收端,将包接收后存入一个缓冲区,而后迅速返回继续recv。
二、发送的包巨大丢包:虽然send方法会帮你作大包切割成小包发送的事情,但包太大也不行。例如超过50K的一个udp包,不切割
直接经过send方法发送也会致使这个包丢失。这种状况须要切割成小包再逐个send。
三、发送的包较大,超过接受者缓存致使丢包:包超过mtu size数倍,几个大的udp包可能会超过接收者的缓冲,致使丢包。这种
状况能够设置socket接收缓冲。之前遇到过这种问题,我把接收缓冲设置成64K就解决了。
int nRecvBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
四、发送的包频率太快:虽然每一个包的大小都小于mtu size 可是频率太快,例如40多个mut size的包连续发送中间不sleep,也有
可能致使丢包。这种状况也有时能够经过设置socket接收缓冲解决,但有时解决不了。因此在发送频率过快的时候仍是考虑sleep
一下吧。
五、局域网内不丢包,公网上丢包。这个问题可经过切割小包并sleep发送解决。若是流量太大,这个办法也不灵了。
总之,udp丢包老是不可避免的,如上述方法还没法解决: 要么减少流量,要么换tcp协议传输,要么作丢包重传的工做。

具体问题分析

发送频率太高致使丢包

不少人会不理解发送速度过快为何会产生丢包,缘由就是UDP的SendTo不会形成线程阻塞,也就是说,UDP的SentTo不会像TCP中
的SendTo那样,直到数据彻底发送才会return回调用函数,它不保证当执行下一条语句时数据是否被发送。(SendTo方法是异步
的)这样,若是要发送的数据 过多或者过大,那么在缓冲区满的那个瞬间要发送的报文就颇有可能被丢失。至于对“过快”的解释,
做者这样说:“A few packets a second are not an issue; hundreds or thousands may be an issue.”(一秒钟几个数据
包不算什么,可是一秒钟成百上千的数据包就很差办了)。 要解决接收方丢包的问题很简单,首先要保证程序执行后立刻开始监听
(若是数据包不肯定何时发过来的话),其次,要在收到一个数据包后最短的时间内从新回到监听状态,其间要尽可能避免复杂的
操做(比较好的解决办法是使用多线程回调机制)。

报文过大丢包

至于报文过大的问题,能够经过控制报文大小来解决,使得每一个报文的长度小于MTU。以太网的MTU一般是1500 bytes,其余一些
诸如拨号链接的网络MTU值为1280 bytes,若是使用speaking这样很可贵到MTU的网络,那么最好将报文长度控制在1280 bytes以
下。

发送方丢包

发送方丢包:内部缓冲区(internal buffers)已满,而且发送速度过快(即发送两个报文之间的间隔太短);  
接收方丢包:Socket未开始监听;  虽然UDP的报文长度最大能够达到64 kb,可是当报文过大时,稳定性会大大减弱。
这是由于当报文过大时会被分割,使得每一个fragmentation(分割包)的长度小于MTU,而后分别发送,并在接收方从新
组合(reassemble),可是若是其中一个报文丢失,那么其余已收到的报文都没法返回给程序,也就没法获得完整的数据了。