《udp丢包分析》

1、ifconfig -a 可以列出所有网卡

2. 查看网卡 ethtool em1
实际网卡速率可以看Speed 100Mb/s;问题来了,是不是千兆网卡Speed一定是1000M呢?
不一定哟,网卡速率和网线,交换机端口速率都有关系,看到Auto-negotiation字段了吧,on表示开启
自动协商,插上网线后会两端会自动协商一个都支持的速率;

3、查看网卡驱动 ethtool -i em1 

4、sar查看当前网卡速率 sar -n DEV 1


rxpck/s:每秒钟接收的数据包

txpck/s:每秒钟发送的数据包

rxbyt/s:每秒钟接收的字节数

txbyt/s:每秒钟发送的字节数

rxcmp/s:每秒钟接收的压缩数据包

txcmp/s:每秒钟发送的压缩数据包

rxmcst/s:每秒钟接收的多播数据包


【网卡如何发送数据包:】

IP报文可以看作一个包。

Linux网卡驱动程序,将IP包添加14字节的MAC包头,构成MAC包。

MAC包中含有发送端和接收端的MAC地址信息。既然是驱动程序创建的MAC包头信息,当然可以随便输入地址信息的,主机伪装就是这么实现的。

驱动程序将MAC包拷贝到网卡芯片内部的缓存区,就算完事了。有网卡芯片接手处理。网卡芯片对MAC包,再次封装成物理帧,添加头部同步信息和CRC校验。然后丢到网线上,就完成一个IP报文的发送。所有挂接到本网线的网卡都可以看到该物理帧。

网卡接收数据包

1、正常情况:

网线上的物理帧首先被网卡芯片获取,网卡芯片会检查物理帧的CRC,保证完整性。

其次,网卡芯片将物理帧头去掉,得到MAC包。

网卡芯片检查MAC包内的目的MAC地址信息,和本网卡的MAC地址是否一致?不一致,抛弃。

网卡芯片将MAC帧拷贝到网卡内部的缓冲区,触发中断。

驱动程序通过中断,将MAC包拷贝到系统中,构建sk_buff。告诉上层。

上层去掉MAC包头,得到需要的IP包。

 过程中,网卡芯片对物理帧进行了MAC匹配过滤。这样做可以减小系统负荷。

试想一下,若网卡芯片对所有的MAC帧不加判断的直接提供给驱动,让CPU判决会是什么样子呢?

当总线上数据繁忙,CPU将浪费大部分时间去判断该MAC包是否是自己需要的,效率低下。

 

2、不正常模式(混听):

网线上的物理帧首先被网卡芯片获取,网卡芯片会检查物理帧的CRC,保证完整性。

其次,网卡芯片将物理帧头去掉,得到MAC包。

网卡芯片发现自己当前被配置为混听模式,就不对MAC包过滤。

网卡芯片将MAC帧拷贝到网卡内部的缓冲区,触发中断。

驱动程序通过中断,将MAC包拷贝到系统中,构建sk_buff。告诉上层。

上层去掉MAC包头,得到需要的IP包。

显然,这里的IP包并一定是发给自己的。

 

【驱动的问题】

网卡到底能不能接收其他MAC包,完全取决于网卡芯片中RCR(receive control register)配置。

驱动程序是决定网卡能否工作与混听模式的桥梁。

混听模式会加重CPU的负荷,而且也是不符合标准应用的!

 

所有的车辆都要从加油站穿过,(有些都不加油),加油站工作人员的任务量就可想而知。

当然也有例外,有些程序不通过驱动,也可以直接访问网卡芯片RCR达到设置混听模式。

所谓  条条大路通香港,就是这个道理:)没有绝对的

 

【本机和本机Socket通信会走网卡吗】
先说结论:不走网卡,不走物理设备,但是走虚拟设备,loopback device环回(本地回环).

本机的报文的路径是这样的:应用层-> socket接口 -> 传输层(tcp/udp报文) -> 网络层 -> back to 传输层 -> backto socket接口 -.> 传回应用程序在网络层,会在路由表查询路由,路由表(软件路由,真正的转发需要依靠硬件路由,这里路由表包括快速转发表和FIB表)初始化时会保存主机路由(host route,or 环回路由), 查询(先匹配mask,再匹配ip,localhost路由在路由表最顶端,最优先查到)后发现不用转发就不用走中断,不用发送给链接层了,不用发送给网络设备(网卡)。像网卡发送接收报文一样,走相同的接收流程,只不过net device是loopback device,最后发送回应用程序。这一套流程当然和转发和接收外网报文一样,都要经过内核协议栈的处理,不同的是本机地址不用挂net device.

网卡,交换机,网桥,路由器,网关工作在OSI模型哪一层

网桥:工作在数据链路层,在不同或相同类型的LAN之间存储并转发数据帧,必要时进行链路层上的协议转换。可连接两个或多个网络,在其中传送信息包。

交换机:工作在数据链路层,原理等同于多端口网桥。作用是连接数个相同网段的不同主机,减少网内冲突,隔离冲突域。利用存储转发和过滤技术来从物理上分割网段

路由器:工作在网络层,在不同的网络间存储并转发分组。可在异种网络之间(即不同类型的局域网互连,局域网与广域网,广域网与广域网)传输数据并进行路径选择,使用专门的软件协议从逻辑上对整个网络进行划分。

网关:对高层协议(包括传输层及更高层次)进行转换的网间连接器。允许使用不兼容的协议,比如SPX/IPX和TCP/IP的系统和网络互连。因为协议转换是网关最重要的功能,所以答案是工作在传输层及以上层次。

网卡:在物理层上网卡主要是完成物理接口的连接,电信号的传送以及将数据分解为适当大小的数据包之后向网络上发送的功能. 数据链路层功能包括链路建立和拆除,帧定界同步顺序差错控制这些。大多认为主要工作在物理层。

【使用iperf测试网卡吞吐性能】

首先配置待测试的两个网卡的网络地址到同一网段,保证ping对方的IP地址时可以通。两个网卡用网线连接到同一个交换机上,或者直连,交换机交换最大速率不能低于待测试网卡的标称速率。
 

在两台机器上分别运行命令,哪台做服务端,哪台做客户端都可以:
服务端命令:

iperf -s -P 0 -i 1 -p 5001 -w 2M -f k

客户端命令:

iperf -c 192.168.1.3 -i 1 -w 2M -t 600

这里服务端设置的IP地址是:192.168.1.3

 

一、linux 系统接收网络报文的过程。

首先网络报文通过物理网线发送到网卡
网络驱动程序会把网络中的报文读出来放到 ring buffer 中,这个过程使用 DMA(Direct Memory Access),不需要 CPU 参与
内核从 ring buffer 中读取报文进行处理,执行 IP 和 TCP/UDP 层的逻辑,最后把报文放到应用程序的 socket buffer 中
应用程序从 socket buffer 中读取报文进行处理

二、上述任何一个过程都可能会主动或者被动地把报文丢弃,因此丢包可能发生在网卡和驱动,也可能发生在系统和应用。

三、网卡丢包判断

本篇文章假定机器只有一个名字为 eth0 的 interface,
NOTE:文中出现的 RX (receive) 表示接收报文, TX (transmit) 表示发送报文。

1、ethtool -S eth0 
要查看网卡是否有丢包,可以使用 ethtool -S eth0 查看,在输出中查找 bad 或者 drop对应的字段是否有数据,在正常情况下,这些字段对应的数字应该都是 0。如果看到对应的数字在不断增长,就说明网卡有丢包。

另外一个查看网卡丢包数据的命令是 ifconfig ,它的输出中会有 RX (receive 接收报文)和 TX (transmit 发送报文)的统计数据:

2、~# ifconfig eth0
...
        RX packets 3553389376  bytes 2599862532475 (2.3 TiB)
        RX errors 0  dropped 1353  overruns 0  frame 0
        TX packets 3479495131  bytes 3205366800850 (2.9 TiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
...

3、网卡或者驱动丢包
之前讲过,如果 ethtool -S eth0 中有 rx_***_errors 那么很可能是网卡有问题,导致系统丢包,需要联系服务器或者网卡供应商进行处理。

4、如果硬件或者驱动没有问题,一般网卡丢包是因为设置的缓存区(ring buffer)太小,可以使用ethtool 命令查看和设置网卡的 ring buffer。
ethtool -g 可以查看某个网卡的 ring buffer,可以使用 ethtool -G eth0 rx 8192 设置它的值。比如下面的例子
# ethtool -g eth0
Ring parameters for eth0:
Pre-set maximums:
RX:        4096
RX Mini:    0
RX Jumbo:    0
TX:        4096
Current hardware settings:
RX:        256
RX Mini:    0
RX Jumbo:    0
TX:        256
Pre-set 表示网卡最大的 ring buffer 值

四、协议丢包
此外,linux 系统也提供了各个网络协议的丢包信息,可以使用 netstat -s 命令查看,加上--udp 可以只看 UDP 相关的报文数据:

[root@holodesk02 GOD]# netstat -s -u

对于上面的输出,关注下面的信息来查看 UDP 丢包的情况:

packet receive errors 不为空,并且在一直增长说明系统有 UDP 丢包
packets to unknown port received 表示系统接收到的 UDP 报文所在的目标端口没有应用在监听,一般是服务没有启动导致的,并不会造成严重的问题
receive buffer errors 表示因为 UDP 的接收缓存太小导致丢包的数量
NOTE: 并不是丢包数量不为零就有问题,对于 UDP 来说,如果有少量的丢包很可能是预期的行为,比如丢包率(丢包数量/接收报文数量)在万分之一甚至更低。

================================================

UDP主要丢包原因及具体问题分析
一、主要丢包原因
1、接收端处理时间过长导致丢包:调用recv方法接收端收到数据后,处理数据花了一些时间,处理完后再次调用recv方法,在这二次调用间隔里,发过来的包可能丢失。对于这种情况可以修改接收端,将包接收后存入一个缓冲区,然后迅速返回继续recv。
2、发送的包巨大丢包:虽然send方法会帮你做大包切割成小包发送的事情,但包太大也不行。例如超过50K的一个udp包,不切割直接通过send方法发送也会导致这个包丢失。这种情况需要切割成小包再逐个send。
3、发送的包较大,超过接受者缓存导致丢包:包超过mtu size数倍,几个大的udp包可能会超过接收者的缓冲,导致丢包。这种情况可以设置socket接收缓冲。以前遇到过这种问题,我把接收缓冲设置成64K就解决了。

int nRecvBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));

4、发送的包频率太快:虽然每个包的大小都小于mtu size 但是频率太快,例如40多个mut size的包连续发送中间不sleep,也有可能导致丢包。这种情况也有时可以通过设置socket接收缓冲解决,但有时解决不了。所以在发送频率过快的时候还是考虑sleep一下吧。

5、局域网内不丢包,公网上丢包。这个问题我也是通过切割小包并sleep发送解决的。如果流量太大,这个办法也不灵了。总之udp丢包总是会有的,如果出现了用我的方法解决不了,还有这个几个方法: 要么减小流量,要么换tcp协议传输,要么做丢包重传的工作。

二、具体问题分析
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.”(一秒钟几个数据包不算什么,但是一秒钟成百上千的数据包就不好办了)。 要解决接收方丢包的问题很简单,首先要保证程序执行后马上开始监听(如果数据包不确定什么时候发过来的话),其次,要在收到一个数据包后最短的时间内重新回到监听状态,其间要尽量避免复杂的操作(比较好的解决办法是使用多线程回调机制)。

2.报文过大丢包
至于报文过大的问题,可以通过控制报文大小来解决,使得每个报文的长度小于MTU。以太网的MTU通常是1500 bytes,其他一些诸如拨号连接的网络MTU值为1280 bytes,如果使用speaking这样很难得到MTU的网络,那么最好将报文长度控制在1280 bytes以下。

3.发送方丢包
发送方丢包:内部缓冲区(internal buffers)已满,并且发送速度过快(即发送两个报文之间的间隔过短);  接收方丢包:Socket未开始监听;  虽然UDP的报文长度最大可以达到64 kb,但是当报文过大时,稳定性会大大减弱。这是因为当报文过大时会被分割,使得每个分割块(翻译可能有误差,原文是fragmentation)的长度小于MTU,然后分别发送,并在接收方重新组合(reassemble),但是如果其中一个报文丢失,那么其他已收到的报文都无法返回给程序,也就无法得到完整的数据了。


三、解决丢包这个问题,三个方面来解决这个问题.

1. 从发送端解决(推荐)

适用条件: ①发送端是可以控制的.②微秒数量级的延迟可以接受.

解决方法:发送时使用usleep(1)延迟1微秒发送,即发送频率不要过快,延迟1微妙发送,可以很好的解决这个问题.

2.从接收端解决方法一

适用条件:①无法控制发送端发送数据的频率

解决方法: 用recvfrom函数收到数据之后尽快返回,进行下一次recvfrom,可以通过多线程+队列来解决.收到数据之后将数据放入队列中,另起一个线程去处理收到的数据.

3.从接收端解决方法二

适用条件:①使用方法2依然出现大规模丢包的情况,需要进一步优化

解决方法:使用setsockopt修改接收端的缓冲区大小,

    int rcv_size = 1024*1024; //1M
    int optlen=sizeof(rcv_size);
    int err=setsockopt(sock,SOL_SOCKET,SO_RCVBUF,(char *)&rcv_size,optlen);//设置好缓冲区大小

四、UDP丢包检测
udp丢包是指网卡接收到数据包后,linux内核的tcp/ip协议栈在udp数据包处理过程中的丢包,主要原因有两个:
1、udp数据包格式错误或校验和检查失败。
2、应用程序来不及处理udp数据包。

对于原因1,udp数据包本身的错误很少见,应用程序也不可控,本文不讨论。
首先介绍通用的udp丢包检测方法,使用netstat命令,加-su参数。
# netstat -su

Udp:
    2495354 packets received
    2100876 packets to unknown port received.
    3596307 packet receive errors
    14412863 packets sent
    RcvbufErrors: 3596307
    SndbufErrors: 0

从上面的输出中,可以看到有一行输出包含了"packet receive errors",如果每隔一段时间执行netstat -su,发现行首的数字不断变大,表明发生了udp丢包


五、下面介绍一下应用程序来不及处理而导致udp丢包的常见原因:

1、linux内核socket缓冲区设的太小
# cat /proc/sys/net/core/rmem_default
# cat /proc/sys/net/core/rmem_max
可以查看socket缓冲区的缺省值和最大值。

rmem_default和rmem_max设置为多大合适呢?如果服务器的性能压力不大,对处理时延也没有很严格的要求,设置为1M左右即可。如果服务器的性能压力较大,或者对处理时延有很严格的要求,则必须谨慎设置rmem_default 和rmem_max,如果设得过小,会导致丢包,如果设得过大,会出现滚雪球。

2、服务器负载过高,占用了大量cpu资源,无法及时处理linux内核socket缓冲区中的udp数据包,导致丢包。
一般来说,服务器负载过高有两个原因:收到的udp包过多;服务器进程存在性能瓶颈。如果收到的udp包过多,就要考虑扩容了。服务器进程存在性能瓶颈属于性能优化的范畴,这里不作过多讨论。
3、磁盘IO忙
磁盘IO忙,导致udp丢包。

4、物理内存不够用,出现swap交换
swap交换本质上也是一种磁盘IO忙,因为比较特殊,容易被忽视,所以单列出来。
只要规划好物理内存的使用,并且合理设置系统参数,可以避免这个问题。

5)磁盘满导致无法IO
没有规划好磁盘的使用,监控不到位,导致磁盘被写满后服务器进程无法IO,处于阻塞状态。最根本的办法是规划好磁盘的使用,防止业务数据或日志文件把磁盘塞满,同时加强监控,例如开发一个通用的工具,当磁盘使用率达到80%时就持续告警,留出充足的反应时间。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值