>

UDP只提供数据的不可靠传递,4、发送的包频率太

- 编辑:澳门博发娱乐官网 -

UDP只提供数据的不可靠传递,4、发送的包频率太

4. 其他减少丢包策略

      UDP发送端增加流量控制,控制每秒发送的数据包,尽量避免由于发送端发包速率过快,导致UDP接收端缓冲区很快被填满从而出现溢出丢包。测试采用google提供的工具包guava。RateLimiter类来做流控,采用了一种令牌桶的流控算法,RateLimiter会按照一定的频率往桶里扔令牌,线程拿到令牌才能执行,比如你希望自己的应用程序QPS不要超过1000,那么RateLimiter设置1000的速率后,就会每秒往桶里扔1000个令牌。

       采用流控后每秒发指定数量的数据包,而且每秒都会出现波谷波峰,如果不做流控,UDP发送端会全力发包一直在波峰附近抖动,大流量会一直持续,随着时间的增加,UDP发送端生产的速率肯定会超过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),但是如果其中一个报文丢失,那么其他已收到的报文都无法返回给程序,也就无法得到完整的数据了。

 


 

UDP丢包

我们是后一个包丢掉了

 

最近在做一个项目,在这之前,做了个验证程序.
发现客户端连续发来1000个1024字节的包,服务器端出现了丢包现象.
纠其原因,是服务端在还未完全处理掉数据,客户端已经数据发送完毕且关闭了.

有没有成熟的解决方案来解决这个问题.
我用过sleep(1),暂时解决这个问题,但是这不是根本解决办法,如果数据量大而多,网络情况不太好的话,还是有可能丢失.

 

你试着用阻塞模式吧...
select...我开始的时候好像也遇到过..不过改为阻塞模式后就没这个问题了...

 

采用回包机制,每个发包必须收到回包后再发下一个

 

UDP丢包是正常现象,因为它是不安全的。

 

丢包的原因我想并不是“服务端在还未完全处理掉数据,客户端已经数据发送完毕且关闭了”,而是服务器端的socket接收缓存满了(udp没有流量控制,因此发送速度比接收速度快,很容易出现这种情况),然后系统就会将后来收到的包丢弃。你可以尝试用setsockopt()将接收缓存(SO_RCVBUF)加大看看能不能解决问题。

 

服务端采用多线程pthread接包处理

 

UDP是无连接的,面向消息的数据传输协议,与TCP相比,有两个致命的缺点,一是数据包容易丢失,二是数据包无序。
要实现文件的可靠传输,就必须在上层对数据丢包和乱序作特殊处理,必须要有要有丢包重发机制和超时机制。
常见的可靠传输算法有模拟TCP协议,重发请求(ARQ)协议,它又可分为连续ARQ协议、选择重发ARQ协议、滑动窗口协议等等。
如果只是小规模程序,也可以自己实现丢包处理,原理基本上就是给文件分块,每个数据包的头部添加一个唯一标识序号的ID值,当接收的包头部ID不是期望中的ID号,则判定丢包,将丢包ID发回服务端,服务器端接到丢包响应则重发丢失的数据包。
模拟TCP协议也相对简单,3次握手的思想对丢包处理很有帮助。

 

udp是不安全的,如果不加任何控制,不仅会丢失包,还可能收到包的顺序和发送包的顺序不一样。这个必须在自己程序中加以控制才行。
收到包后,要返回一个应答,如果发送端在一定时间内没有收到应答,则要重发。

UDP本来存在丢包现象,现在的解决方案暂时考虑双方增加握手.
这样做起来,就是UDP协议里面加上了TCP的实现方法.
程序中采用的是pthread处理,丢包率时大时小,不稳定可靠

 

我感觉原因可能有两个,一个是客户端发送过快,网络状况不好或者超过服务器接收速度,就会丢包。
第二个原因是服务器收到包后,还要进行一些处理,而这段时间客户端发送的包没有去收,造成丢包。

解决方法,一个是客户端降低发送速度,可以等待回包,或者加一些延迟。
二是,服务器部分单独开一个线程,去接收UDP数据,存放在一个缓冲区中,又另外的线程去处理收到的数据,尽量减少因为处理数据延时造成的丢包。

 

有两种方法解决楼主的问题:
方法一:重新设计一下协议,增加接收确认超时重发。(推荐)
方法二:在接收方,将通信和处理分开,增加个应用缓冲区;如果有需要增加接收socket的系统缓冲区。(本方法不能从根本解决问题,只能改善)

 

网络丢包,是再正常不过的了。
既然用UDP,就要接受丢包的现实,否则请用TCP。
如果必须使用UDP,而且丢包又是不能接受的,只好自己实现确认和重传,说白了,就是自己实现TCP(当然是部分和有限的简单实现)。

 

UDP是而向无连接的,用户在实施UDP编程时,必须制定上层的协议,包括流控制,简单的超时和重传机制,如果不要求是实时数据,我想TCP可能会更适合你!

 


1:什么是丢包率? 

你的电脑向目标发送一个数据包,如果对方没有收到.就叫丢包. 
比如你发10个,它只收到9个. 那么丢包率就是 10% 
数据在网络中是被分成一各个个数据报传输的,每个数据报中有表示数据信息和提供数据路由的桢.而数据报在一般介质中传播是总有一小部分由于两个终端的距离过大会丢失,而大部分数据包会到达目的终端.所谓网络丢包率是数据包丢失部分与所传数据包总数的比值.正常传输时网络丢包率应该控制在一定范围内.

2:什么是吞吐量?
网络中的数据是由一个个数据包组成,防火墙对每个数据包的处理要耗费资源。吞吐量是指在没有帧丢失的情况下,设备能够接受的最大速率。其测试方法是:在测试中以一定速率发送一定数量的帧,并计算待测设备传输的帧,如果发送的帧与接收的帧数量相等,那么就将发送速率提高并重新测试;如果接收帧少于发送帧则降低发送速率重新测试,直至得出最终结果。吞吐量测试结果以比特/秒或字节/秒表示。

吞吐量和报文转发率是关系防火墙应用的主要指标,一般采用FDT(Full Duplex Throughput)来衡量,指64字节数据包的全双工吞吐量,该指标既包括吞吐量指标也涵盖了报文转发率指标。 

随着Internet的日益普及,内部网用户访问Internet的需求在不断增加,一些企业也需要对外提供诸如WWW页面浏览、FTP文件传输、DNS域名解析等服务,这些因素会导致网络流量的急剧增加,而防火墙作为内外网之间的唯一数据通道,如果吞吐量太小,就会成为网络瓶颈,给整个网络的传输效率带来负面影响。因此,考察防火墙的吞吐能力有助于我们更好的评价其性能表现。这也是测量防火墙性能的重要指标。

吞吐量的大小主要由防火墙内网卡,及程序算法的效率决定,尤其是程序算法,会使防火墙系统进行大量运算,通信量大打折扣。因此,大多数防火墙虽号称100M防火墙,由于其算法依靠软件实现,通信量远远没有达到100M,实际只有10M-20M。纯硬件防火墙,由于采用硬件进行运算,因此吞吐量可以达到线性90-95M,是真正的100M防火墙。

对于中小型企业来讲,选择吞吐量为百兆级的防火墙即可满足需要,而对于电信、金融、保险等大公司大企业部门就需要采用吞吐量千兆级的防火墙产品。

3:检测丢包率
下载一个世纪前线,在百度可以找到,很小的程序。

NetIQ Chariot  一款网络应用软件性能测试工具

网络吞吐量测试,CHARIOT测试网络吞吐量

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操作,会导致进程阻塞,cpu都在等待磁盘IO,不能及时处理内核socket缓冲区中的udp数据包。如果业务本身就是IO密集型的,要考虑在架构上进行优化,合理使用缓存降低磁盘IO。

这里有一个容易忽视的问题:很多服务器都有在本地磁盘记录日志的功能,由于运维误操作导致日志记录的级别过高,或者某些错误突然大量出现,使得往磁盘写日志的IO请求量很大,磁盘IO忙,导致udp丢包。

对于运维误操作,可以加强运营环境的管理,防止出错。如果业务确实需要记录大量的日志,可以使用内存log或者远程log。

4、物理内存不够用,出现swap交换

swap交换本质上也是一种磁盘IO忙,因为比较特殊,容易被忽视,所以单列出来。

只要规划好物理内存的使用,并且合理设置系统参数,可以避免这个问题。

5)磁盘满导致无法IO

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

struct提供用format specifier方式对数据进行打包和解包(Packing and Unpacking)。例如:

3. 针对UDP丢包问题,进行系统层面和程序层面调优

模型2

其他测试条件同模型1,除UDP包头外,一百个字节数据。

测试结果

进程个数

1

2

4

8

平均处理速度(包/秒)

571433.4

752319.9

731545.6

751922.5

网卡流量(Mb/s)

855.482

855.542

855.546

855.549

CPU占用情���(%)

100

112.9

——

——

图片 1

图片 2

现象:

1、100个字节的包大小,比较符合平常的业务情形。

2、UDP的处理能力还是非常可观,单机峰值可以到达每秒75w。

3、在4,8个进程时,没有记录CPU的占用情况(网卡流量耗尽),不过可以肯定的是,CPU未耗尽。

4、随着进程个数的上升,处理能力没有明显提升,但是,丢包(UDP_ERROR)的个数大幅下降。

udp的recvfrom是阻塞的,一个recvfrom(x)必须对唯一一个sendinto(y),收完了x个字节的数据就算完成,若是y>x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠

3.1 诊断

n  网卡缓冲区溢出诊断

在Linux操作系统中,可以通过netstat -i –udp <NIC> 命令来诊断网卡缓冲区是否溢出,RX-DRP列显示网卡丢失的数据包个数。

For example: netstat -i –udp eth1

[[email protected] /usr/local/games/udpserver]# netstat -i –udp eth1Kernel Interface tableIface       MTU Met    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flgeth1       1500   0 1295218256      0      3      0  7598497      0      0      0 BMRU

上图的输出显示3个数据包被网卡丢掉

可以通过增大网卡缓冲区来有效减少网卡缓冲区溢出。

n  操作系统内核网络缓冲区溢出诊断

在Linux操作系统中可以通过cat /proc/net/snmp | grep -w Udp命令来查看,InErrors 列显示当操作系统UDP队列溢出时丢失的UDP数据包总个数。

[[email protected] /usr/local/games/udpserver]# cat /proc/net/snmp | grep -w UdpUdp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrorsUdp: 859428331 12609927 166563611 151449 166563611 0

有如下几种方法可以有效减缓操作系统缓冲区溢出:

1)     增大操作系统内核网络缓冲区的大小

2)     在数据包路径图中直接绕过操作系统内核缓冲区,通过使用用户空间栈或一些可以            绕过内核缓冲区的中间件 (e.g. Solarflare's OpenOnload).

3)     关闭未使用的网络相关的应用和服务使操作系统的负载降到最低

4)     系统中仅保留适当数量的工作的网卡,最大效率的合理化利用网卡和系统资源

n  应用程序socket缓冲区溢出诊断

在Linux操作系统中可以通过cat /proc/net/snmp | grep -w Udp命令来查看,RcvbufErrors 列显示当应用程序socket缓冲区溢出时丢失的UDP数据包总个数。

[[email protected] /usr/local/games/udpserver]# cat /proc/net/snmp | grep -w UdpUdp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrorsUdp: 859428331 12609927 166563611 151449 166563611 0

有如下几种方法可以有效减缓应用程序socket缓冲区溢出:

1)    接受缓冲区尽可能快地处理接受到的数据包(e.g.通过使用NIO的方式来异步非阻塞接受UDP数据包或者提高接受UDP数据包线程的优先级)

2)    增大应用程序接受socket缓冲区大小,注意这个受限于全局socket缓冲区大小,如果应用程序的socket缓冲区大于全局socket缓冲区将没有效果。

3)    把应用程序或接受线程指定到CPU专用的核上

4)    提高应用程序的io优先级(e.g.使用nice或ionice命令来调节)

5)    关闭所有未使用的网络相关的应用和服务使操作系统的负载降到最低

总结

UDP数据包长度

在本机(loopback)传输,可以根据需要设置MTU,但记住,UDP最大理论长度65507。

在内网传输,最好控制在1472字节(1500-8-20)。

在internet上传输,最好控制在548字节(576-8-20)以内。

UDP收包能力

UDP处理能力非常可观,在日常的业务情形中,UDP一般不会成为性能瓶颈。

随着进程个数的增加,处理能力未明显上升,但是丢包个数明显下降。

采用多进程监听不同端口的模型,而不是多进程或多线程监听同一个端口。

本文永久更新链接地址:

) UDP数据包长度 UDP数据包的理论长度 udp数据包的理论长度是多少,合适的udp数据包应...

服务端:

2. UDP丢包问题分析

由于UDP协议只提供数据的不可靠传输,数据包发出去后就不管了,数据包在网络的传输过程中都可能丢失。甚至即使数据包成功到达了接收端节点,也不意味着应用程序能够收到,因为要从网卡到达应用程序还需要经历很多阶段,每个阶段都可能丢包。

上图描述了一种应用程序接受网络数据包的典型路径图。

首先,NIC(网卡)接收和处理网络数据包。网卡有自己的硬件数据缓冲区,当网络数据流量太大,大到超过网卡的接收数据硬件缓冲区,这时新进入的数据包将覆盖之前缓冲区的数据包,导致丢失。网卡是否丢包取决于网卡本身的计算性能和硬件缓冲区的大小。

其次,网卡处理后把数据包交给操作系统缓冲区。数据包在操作系统阶段丢包主要取决于以下因素:

  • 操作系统缓冲区的大小
  • 系统的性能
  • 系统的负载
  • 网络相关的系统负载

最后,当数据包到达应用程序的socket缓冲区,如果应用程序不能及时从socket缓冲区把数据包取走,累积的数据包将会超出应用程序socket缓冲区阀值,导致缓冲区溢出,数据包丢失。数据包在应用程序阶段丢包主要取决于以下因素:

  • 应用程序缓冲区大小
  • 应用程序处理数据包的能力,即如何尽可能快的从缓冲区取走数据包

UDP收包能力测试

图片 3

在Linux 上,编写一个每秒接收 100万UDP数据包的程序究竟有多难?,udp有多难

在Linux 上,编写一个每秒接收 100万UDP数据包的程序究竟有多难?
写的不错,转载一下

测试环境

处理器:Intel(R) Xeon(R) CPU X3440 @ 2.53GHz,4核,8超线程,千兆以太网卡,8G内存

图片 4

3.2 调优

n  网卡缓冲区调优

Linux下运行ethtool -g <NIC> 命令查询网卡的缓冲设置,如下:

[[email protected] /usr/local/games/udpserver]# ethtool -g eth1Ring parameters for eth1:Pre-set maximums:RX:             4096RX Mini:        0RX Jumbo:       0TX:             4096Current hardware settings:RX:             256RX Mini:        0RX Jumbo:       0TX:             256

       通过命令ethtool -G d<NIC> rx NEW-BUFFER-SIZE可以设置RX ring的缓冲区大小,改变会立即生效不需要重启操作系统或刷新网络栈,这种变化直接作用于网卡本身而不影响操作系统,不影响操作系统内核网络栈但是会影响网卡固件参数。更大的ring size能承受较大的数据流量而不会丢包,但是因为工作集的增加可能会降低网卡效率,影响性能,所以建议谨慎设置网卡固件参数。

n  操作系统内核缓冲区调优

运行命令sysctl -A | grep net | grep 'mem|backlog' | grep 'udp_mem|rmem_max|max_backlog'查看当前操作系统缓冲区的设置。如下:

[[email protected] /usr/local/games]# sysctl -A | grep net | grep 'mem|backlog' | grep 'udp_mem|rmem_max|max_backlog'net.core.netdev_max_backlog = 1000net.core.rmem_max = 212992net.ipv4.udp_mem = 188169       250892  376338

增加最大socket接收缓冲区大小为32MB:

sysctl -w net.core.rmem_max=33554432

增加最大可分配的缓冲区空间总量,数值以页面为单位,每个页面单位等于4096 bytes:

sysctl -w net.ipv4.udp_mem="262144 327680 393216"

增加接收数据包队列大小:

sysctl -w net.core.netdev_max_backlog=2000

修改完成后,需要运行命令 sysctl –p使之生效

n  应用程序调优

      要减少数据包丢失,应用程序必须尽可能快从缓冲区取走数据,可以通过适当增大socket缓冲区和采用异步非阻塞的IO来快速从缓冲区取数据,测试采用JAVA NIO构建一个Asynchronous UDP server。

            //建立            DatagramChannel dc = DatagramChannel.open();            dc.configureBlocking(false);            //本地绑定端口            SocketAddress address = new InetSocketAddress(port);            DatagramSocket ds = dc.socket();            ds.setReceiveBufferSize(1024 * 1024 * 32);//设置接收缓冲区大小为32M            ds.bind(address);            //注册            Selector select = Selector.open();            dc.register(select, SelectionKey.OP_READ);            ByteBuffer buffer = ByteBuffer.allocateDirect(1024);            System.out.println("Listening on port " + port);            while (true) {                int num = select.select();                if (num == 0) {                    continue;                }                //得到选择键列表                Set Keys = select.selectedKeys();                Iterator it = Keys.iterator();                while (it.hasNext()) {                    SelectionKey k = (SelectionKey) it.next();                    if ((k.readyOps() & SelectionKey.OP_READ)                        == SelectionKey.OP_READ) {                        DatagramChannel cc = (DatagramChannel) k.channel();                        //非阻塞                        cc.configureBlocking(false);

UDP数据包长度

#_*_coding:utf-8_*_
from socket import *
ip_port=('127.0.0.1',8080)
tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)
conn,addr=tcp_socket_server.accept()
data1=conn.recv(2) #一次没有收完整
data2=conn.recv(10)#下次收的时候,会先取旧的数据,然后取新的
print('----->',data1.decode('utf-8'))
print('----->',data2.decode('utf-8'))
conn.close()

1. UDP概念

   用户数据报协议(英语:User Datagram Protocol,缩写为 UDP),又称使用者资料包协定,是一个简单的面向数据报的传输层协议,正式规范为RFC 768

   在TCP/IP模型中,UDP为网络层以上和应用层以下提供了一个简单的接口。UDP只提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份(所以UDP有时候也被认为是不可靠的数据报协议)。UDP在IP数据报的头部仅仅加入了复用和数据校验(字段)。

模型3

单机,单进程,多线程异步UDP服务,多线程共用一个fd,无业务逻辑,除UDP包头外,一个字节数据。

测试结果:

线程个数

1

2

平均处理速度(包/秒)

791676

509868

网卡流量(Mb/s)

514.361

714.229

CPU占用情况(%)

100

150

现象:

1、随着线程个数的增加,处理能力不升反降。

结论:

1、多线程共用一个fd,会造成相当大的锁争用。

2、多线程共用一个fd,当有包来时,会激活所有的线程,导致频繁的上下文切换。

 

最终结论:

1、UDP处理能力非常可观,在日常的业务情形中,UDP一般不会成为性能瓶颈。

2、随着进程个数的增加,处理能力未明显上升,但是丢包个数明显下降。

3、本次测试过程中,瓶颈在网卡,而不在CPU。

4、采用多进程监听不同端口的模型,而不是多进程或多线程监听同一个端口。

3,基本用法

5. 真实测试数据

n  机器类型

发送端和接收端均采用C1类型机器,配置如下:

C1 Intel(R) Xeon(R) CPU X3440 @ 2.53GHz:8 8G 500G:7200RPM:1:SATA NORAID

接收端网卡信息如下:

[[email protected] /usr/local/games]# ethtool eth1                     Settings for eth1:        Supported ports: [ TP ]        Supported link modes:   10baseT/Half 10baseT/Full                                 100baseT/Half 100baseT/Full                                 1000baseT/Full         Supports auto-negotiation: Yes        Advertised link modes:  10baseT/Half 10baseT/Full                                 100baseT/Half 100baseT/Full                                 1000baseT/Full         Advertised pause frame use: Symmetric        Advertised auto-negotiation: Yes        Speed: 1000Mb/s        Duplex: Full        Port: Twisted Pair        PHYAD: 1        Transceiver: internal        Auto-negotiation: on        MDI-X: on        Supports Wake-on: pumbg        Wake-on: g        Current message level: 0x00000007 (7)        Link detected: yes[[email protected] /usr/local/games]# ethtool -g eth1Ring parameters for eth1:Pre-set maximums:RX:             4096RX Mini:        0RX Jumbo:       0TX:             4096Current hardware settings:RX:             256RX Mini:        0RX Jumbo:       0TX:             256

n  实际调优

接收端服务器调优后的参数如下:

[[email protected] /usr/local/games]# sysctl -A | grep net | grep 'mem|backlog' | grep 'udp_mem|rmem_max|max_backlog'net.core.rmem_max = 67108864net.core.netdev_max_backlog = 20000net.ipv4.udp_mem = 754848       1006464 1509696

 发送端是否做发送流量控制在测试场景中体现

n  测试场景

场景1:发送100w+数据包,每个数据包大小512byte,数据包都包含当前的时间戳,不限流,全速发送。发送5次,测试结果如下:

测试客户端:

发100w个512字节的udp包,发100w数据包耗时4.625s,21wQPS

测试服务器端:

客户端发5次包,每次发包100w(每个包512字节),第一次服务端接受90w丢约10w,第二次服务端接受100w不丢,第三次接受100w不丢,第四次接受97w丢3w,第五次接受100w不丢

服务端记录日志:

 服务端操作系统接收UDP记录情况:(和日志记录结果完全一致)

       场景2:发送端增加流量控制,每秒4w数据包,每个数据包512byte,包含当前时间戳,发送时间持续2小时,测试结果如下:

1.Udpclient端,加入流量控制:

QPS:4W

datapacket:512byte,包含发送的时间戳

持续发送时长:2h

累计发包数: 287920000(2.8792亿)

CPU平均消耗: 16% (8cpu)

内存平均消耗: 0.3%(8G)

2.Udpserver端:

Server端接受前网卡记录的UDP 详情:

[[email protected] ~]# cat /proc/net/snmp | grep -w UdpUdp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrorsUdp: 1156064488 753197150 918758960 1718431901 918758960 0

Server端接受完所有的udp数据包后网卡记录的UDP详情:

[[email protected] ~]# cat /proc/net/snmp | grep -w UdpUdp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrorsUdp: 1443984568 753197150 918758960 1718432045 918758960 0

前后变化分析:

InDatagrams: (1443984568-1156064488)= 287920080

InErrors:0 (记录操作系统层面udp丢包,丢包可能是因为系统udp队列满了)

RcvbufErrors:0(记录应用程序层面udp丢包),丢包可能是因为应用程序socket buffer满了)

Server端日志情况:

总记录日志文件:276个,总大小:138G

日志总数: 287920000 (和udpclient发送数据包总量一致,没有丢包)

根据日志时间戳,简单计算处理能力:

time cost:(1445410477654-1445403277874)/1000=7199.78s

process speed: 287920000/7199.78 = 3.999w/s

 

CPU消耗: 平均46% (8cpu),要不停异步写日志,IO操作频繁,消耗比较多cpu资源

内存消耗: 平均4.7%(8G)

  场景3:发送端增加流量控制,每秒6w数据包,每个数据包512byte,包含当前时间戳,发送时间持续2小时,出现丢包,测试结果如下:

1.Udpclient端,加入流量控制:

QPS:6W

datapacket:512byte,包含发送的时间戳

持续发送时长:2h

累计发包数: 432000000 (4.32亿)

CPU平均消耗: 70% (8cpu)

内存平均消耗: 0.3%(8G)

2.Udpserver端:

Server端接受前网卡记录的UDP 详情:

[[email protected] ~]# cat /proc/net/snmp | grep -w UdpUdp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrorsUdp: 2235178200 753197150 918960131 1720242603 918960131 0

Server端接受完所有的udp数据包后网卡记录的UDP详情:

[[email protected] ~]# cat /proc/net/snmp | grep -w UdpUdp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrorsUdp: 2667158153 753197150 918980378 1720242963 918980378 0

前后变化分析:

InDatagrams: (2667158153 -2235178200)= 431979953

InErrors: (918980378 -918960131)= 20247 (记录操作系统层面udp丢包,丢包可能是因为系统udp队列满了)

RcvbufErrors: (918980378 -918960131)= 20247 (记录应用程序层面udp丢包),丢包可能是因为应用程序socket buffer满了)

Server端日志情况:

总记录日志文件:413个,总大小:207G

日志总数: 431979753 (和网卡收到udp包总数一致,写日志文件没有丢包)

丢包情况:

Client端发送:432000000,

服务端网卡接受udp包总数:431979953,

日志记录:431979953,

udp网卡接受丢包:20247,

丢包率:1/20000

由于测试服务器硬盘资源有限,只测试了2个小时,随着发送和接受时间增长,丢包率可能会增大。

 对比图:不加流控和加流控(限流4w)发送100w个512byte数据包,每毫秒发送数据包雷达波型对比图,雷达波型图中,外围波型值为发送数据包的毫秒值,雷达轴距为每毫秒发送的数据包数取值范围。按顺序,图1为限流4w生成的图,图2为不限流生成的图。从图中可以看出限流时每秒都会出现波谷波峰,不会一直持续高流量发送,能适当缓解UDP接收端的压力;不限流时数据在波峰附近波动,持续高流量发送,对UDP接收端有很多压力,接收端如没及时从缓冲区取走数据或消费能力低于发送端的生成能力,则很容易丢包。


 总结:UDP发包在不做流控的前提下,发送端很快到达一个相对稳定的波峰值并一直持续发送,接收端网卡或操作系统缓冲区始终有限,随着发包时间不断增加,到某个时间点必定填满接收端网卡和系统的缓冲区,而且发送端的生产速率将远远超过接收端消费速率,必然导致丢包。发送端做了流量控制后,发送速率得到有效控制,不会一直持续高流量发送,每秒都会出现波谷波峰,有效缓解了接收端的压力,在合理发包速率的前提下,通过相关系统调优,基本可以保证不丢包,但要确保数据的高完整性,由于UDP协议的天生不可靠性,还是要在UDP协议基础上做相关扩展,增加数据完整性校验,方能确保业务数据的完整。

【注】文章第2和第3部分翻译国外一篇文章,原文如下:

UDP数据包的理论长度

udp数据包的理论长度是多少,合适的udp数据包应该是多少呢?从TCP-IP详解卷一第11章的udp数据包的包头可以看出,udp的最大包长度是2^16-1的个字节。由于udp包头占8个字节,而在ip层进行封装后的ip包头占去20字节,所以这个是udp数据包的最大理论长度是2^16-1-8-20=65507。

图片 5

然而这个只是udp数据包的最大理论长度。首先,我们知道,TCP/IP通常被认为是一个四层协议系统,包括链路层、网络层、运输层、应用层。UDP属于运输层,在传输过程中,udp包的整体是作为下层协议的数据字段进行传输的,它的长度大小受到下层ip层和数据链路层协议的制约。

发送端可以是1k,1k的发送数据而接受端的应用程序可以2k,2k的提取数据,当然也有可能是3k或者多k提取数据,也就是说,应用程序是不可见的,因此TCP协议是面来那个流的协议,这也是容易出现粘包的原因而UDP是面向无连接的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任一字节的数据,这一点和TCP是很同的。怎样定义消息呢?认为对方一次性write/send的数据为一个消息,需要命的是当对方send一条信息的时候,无论鼎城怎么样分段分片,TCP协议层会把构成整条消息的数据段排序完成后才呈现在内核缓冲区。    

IP分包udp数据包长度的影响

如上所述,由于网络接口卡的制约,mtu的长度被限制在1500字节,这个长度指的是链路层的数据区。对于大于这个数值的分组可能被分片,否则无法发送,而分组交换的网络是不可靠的,存在着丢包。IP 协议的发送方不做重传。接收方只有在收到全部的分片后才能 reassemble并送至上层协议处理代码,否则在应用程序看来这些分组已经被丢弃。

假定同一时刻网络丢包的概率是均等的,那么较大的IP datagram必然有更大的概率被丢弃,因为只要丢失了一个fragment,就导致整个IP datagram接收不到。不超过MTU的分组是不存在分片问题的。

MTU的值并不包括链路层的首部和尾部的18个字节。所以,这个1500字节就是网络层IP数据报的长度限制。因为IP数据报的首部为20字节,所以IP数据报的数据区长度最大为1480字节。而这个1480字节就是用来放TCP传来的TCP报文段或UDP传来的UDP数据报的。又因为UDP数据报的首部8字节,所以UDP数据报的数据区最大长度为1472字节。这个1472字节就是我们可以使用的字节数。

图片 6

当我们发送的UDP数据大于1472的时候会怎样呢?这也就是说IP数据报大于1500字节,大于MTU。这个时候发送方IP层就需要分片(fragmentation)。把数据报分成若干片,使每一片都小于MTU。而接收方IP层则需要进行数据报的重组。而更严重的是,由于UDP的特性,当某一片数据传送中丢失时,接收方便无法重组数据报。将导致丢弃整个UDP数据报。因此,在普通的局域网环境下,将UDP的数据控制在1472字节以下为好。

进行Internet编程时则不同,因为Internet上的路由器可能会将MTU设为不同的值。如果我们假定MTU为1500来发送数据的,而途经的某个网络的MTU值小于1500字节,那么系统将会使用一系列的机制来调整MTU值,使数据报能够顺利到达目的地。鉴于Internet上的标准MTU值为576字节,所以在进行Internet的UDP编程时,最好将UDP的数据长度控件在548字节(576-8-20)以内。

#客户端
import socket
import time
import subprocess
din=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=('127.0.0.1',8080)
din.connect(ip_port)
din.send('helloworld'.encode('utf-8'))
time.sleep(3)
din.send('sb'.encode('utf-8'))

模型1

单机,单线程异步UDP服务,无业务逻辑,只有收包操作,除UDP包头外,一个字节数据。

测试结果

进程个数

1

2

4

8

平均处理速度(包/秒)

791676.1

1016197

1395040

1491744

网卡流量(Mb/s)

514.361

713.786

714.375

714.036

CPU占用情况(%)

100

200

325

370

图片 7

图片 8

现象:

1、单机UDP收包处理能力可以每秒达到150w左右。

2、处理能力随着进程个数的增加而增强。

3、在处理达到峰值时,CPU资源并未耗尽。

结论:

1、UDP的处理能力还是非常可观的。

2、对于现象2和现象3,可以看出,性能的瓶颈在网卡,而不在CPU,CPU的增加,处理能力的上升,来源于丢包(UDP_ERROR)个数的减少。

而udp发送数据,对端是不会返回确认信息的,因此不可靠

浅谈UDP(数据包长度,收包能力,丢包及进程结构选择)

import socket
import subprocess
din=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=('127.0.0.1',8080)
din.bind(ip_port)
din.listen(5)
conn,deer=din.accept()
data1=conn.recv(1024)
data2=conn.recv(1024)
print(data1)
print(data2)

MTU相关概念

以太网(Ethernet)数据帧的长度必须在46-1500字节之间,这是由以太网的物理特性决定的。这个1500字节被称为链路层的MTU(最大传输单元)。因特网协议允许IP分片,这样就可以将数据包分成足够小的片段以通过那些最大传输单元小于该数据包原始大小的链路了。这一分片过程发生在网络层,它使用的是将分组发送到链路上的网络接口的最大传输单元的值。这个最大传输单元的值就是MTU(Maximum Transmission Unit)。它是指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位)。最大传输单元这个参数通常与通信接口有关(网络接口卡、串口等)。

在因特网协议中,一条因特网传输路径的“路径最大传输单元”被定义为从源地址到目的地址所经过“路径”上的所有IP跳的最大传输单元的最小值。

需要注意的是,loopback的MTU不受上述限制,查看loopback MTU值:

图片 9

[[email protected] ~]# cat /sys/class/net/lo/mtu 

65536

2,首先需要掌握一个socket收发消息的原理

终极版的服务端

1,发送端需要等到本机的缓冲区满了以后才发出去,造成粘包(发送数据时间间隔很短,数据很小,python使用了优化算法,合在一起,产生粘包)

#_*_coding:utf-8_*_
import socket
BUFSIZE=1024
ip_port=('127.0.0.1',8080)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(ip_port)
s.send('hello'.encode('utf-8'))
s.send('feng'.encode('utf-8'))

3-3 总结

服务端

#_*_coding:utf-8_*_
import socket
BUFSIZE=1024
ip_port=('127.0.0.1',8080)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(ip_port)
s.send('hello feng'.encode('utf-8'))

五,粘包问题如何解决?

本文由胜博发-运维发布,转载请注明来源:UDP只提供数据的不可靠传递,4、发送的包频率太