首页 > 代码库 > sendto频率过快导致发送丢包

sendto频率过快导致发送丢包

编写一个转发模块,虽然没有要求一转多时要达到多少路(不采用组播的情况下,单纯的一路转成多路),但是本着物尽其用的原则,尽可能测试一下极限。

网络环境:1000M,直连,多网卡

系统:Linux version 3.19.0

接收模式:udp模式的raw socket(优化的话,可以直接通过网卡处理)

发送模式:udp模式的raw socket(优化的话,可以直接通过网卡处理),单线程/多线程

                     2M               1转N

设备A   ---------------->   转发设备  ---------------->  设备B

 

但N大到一定程度时,发现发送丢包。

注意,是转发设备发送丢包,不是设备B接收丢包。

设备B接收丢包是可以理解的,毕竟2M码率本身的突发性相当高,1转N时,这个突发率更加扩大。

但是发送丢包是一个什么情况,sendto的返回值都进行了判断,如果异常是会出现打印信息的,但是没有异常出现。

上网查资料。其中最靠谱的是

http://www.cnblogs.com/mengyan/archive/2012/10/04/2711340.html

1.发送频率过高导致丢包

很多人会不理解发送速度过快为什么会产生丢包,原因就是UDP的SendTo不会造成线程阻塞,也就是说,UDP的SentTo不会像TCP中的SendTo那样,直到数据完全发送才会return回调用函数,它不保证当执行下一条语句时数据是否被发送。(SendTo方法是异步的)这样,如果要发送的数据过多或者过大,那么在缓冲区满的那个瞬间要发送的报文就很有可能被丢失。至于对“过快”的解释,作者这样说:“A few packets a second are not an issue; hundreds or thousands may be an issue.”(一秒钟几个数据包不算什么,但是一秒钟成百上千的数据包就不好办了)。

 

发送方丢包:内部缓冲区(internal buffers)已满,并且发送速度过快(即发送两个报文之间的间隔过短); 

但是更让人郁闷的事情出现了。无论是网上资料,还是询问同事,与tcp不同,发送这一块没有缓存区啊。

问题的,已经设置SO_SNDBUF为64M,修改系统值为128M,设置后获取到的SO_SNDBUF为128M。

现在就是在此种情况下发送丢包,128M是什么概念啊,所以基本可以排除这一块的问题。

通过命令watch netstat -s,可以明确的看出 Ip 项下的 outgoing packets dropped 持续增长,也就意味着确实是发送丢包。

然后就通过outgoing packets dropped ,sendto频率过快等等关键词开始查资料,结果让人蓝瘦香菇啊

阴差阳错的情况下,查到了 IOCTLS

http://blog.csdn.net/wl_haanel/article/details/5305159

SIOCGIFTXQLEN , SIOCSIFTXQLEN
使用 ifr_qlen 读取 或 设置 设备的 传输队列长度. 设置 传输队列长度 是 特权操作.

 

于是通过

struct ifreq ifr;

memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name));

if (-1 == ioctl(sock_, SIOCGIFTXQLEN, &ifr))
    PLOG(ERROR) << "failed to get dev eth0 queue length";
LOG(KEY) << "Dev eth0 queue length " << ifr.ifr_qlen;

获取到eth0上的队列长度为1000,设置成10000试试

struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name));
ifr.ifr_qlen = 10000;
if (-1 == ioctl(sock_, SIOCSIFTXQLEN, &ifr))
  PLOG(ERROR) << "failed to set dev eth0 queue length";
if (-1 == ioctl(sock_, SIOCGIFTXQLEN, &ifr))
  PLOG(ERROR) << "failed to get dev eth0 queue length";
LOG(KEY) << "Dev eth0 queue length " << ifr.ifr_qlen;

果然,发现好了,没有发送丢包了

去掉SO_SNDBUF的设置,获取下SO_SNDBUF,才多少K,再测试,仍然没有发送丢包。

 

结论:

sendto过快导致发送丢包,是因为发送队列满了,如果说缓存区,估计大部分人都将误解。

 

至于接收方因为突发率导致接收丢包的问题,那么就要在发送方进行发送平滑进行解决。

sendto频率过快导致发送丢包