我们在进行打流测试时,习惯性的使用centos自带的yum源安装iperf3,默认安装的版本号是3.1.7(可以通过iperf3 -v查看)。在测试tcp流量的时候并不会有太大的问题,但是当我们进行大流量的udp测试时,会发现测试结果比预期要低很多,用其他工具比如iperf2测试时,测试结果就可以达到预期。说明问题出在iperf3上,查找资料发现时iperf3的一个已知bug:https://github.com/esnet/iperf/issues/457,该问题在新版本的iperf3上已经解决,由于该issue是英文的,为了节省大家时间,我在这里给大家简单总结一下。
根本原因
UDP协议并不能保证数据包的发送顺序与接收顺序相同,而iperf3-3.1.7的错误代码导致服务端收到乱序的udp包时,会把乱序的报文当作错包。
问题代码
/* Out of order packets */
if (pcount >= sp->packet_count + 1) {
if (pcount > sp->packet_count + 1) {
sp->cnt_error += (pcount - 1) - sp->packet_count;
}
sp->packet_count = pcount;
} else {
sp->outoforder_packets++;
iperf_err(sp->test, "OUT OF ORDER - incoming packet = %zu and received packet = %d AND SP = %d", pcount, sp->packet_count, sp->socket);
}
pcount是从接收到的udp数据包中读取的序列号(这个序列号不是tcp的序列号,udp头部没有这个字段,所以应该是iperf3客户端标记的)。 sp-> packet_count是到目前为止记录的最大序列号,所以sp-> packet_count + 1就是预期收到的下一个报文的序列号。代码首先将检查接收到的数据包的序列号是否向前或向后移动。如果向前移动,并且移动的距离大于1,则将当前序列号和最大序列号的差值添加到sp-> cnt_error中,也就是将差值记录为已丢失的错包。然后将最大序列号更新。
如当前序列号向后移动,则将增加乱序计数器sp-> outoforder_packets的值,并输出一个带有实际和预期序列号以及文件描述符的错误提示。
假设客户端发送了一个序列号为(1、2、3、4)的数据包序列,但是服务端接收到了序列为(1、3、2、4)的数据包。服务端接收到数据包1时,该数据包将被正常处理;接收到数据包3后,iperf3认为数据包2已丢失,增加错误计数器并将最大序列号置为3;接收到数据包2时,iperf3认为这是一个乱序包。增加乱序计数器;最后,接收到数据包4,这是期望值,该数据包被正常处理。由此可见,即使实际上并没有实际的损失,这一序列的数据包也算作了一个乱序包和一个丢包。
解决方法
pcount是从数据包中读取的序列号,sp-> packet_count是看到的最大序列号(因此,服务端期望看到下一个到达的数据包序列号是sp-> packet_count + 1)。
如果序列号是向前移动的,并且大于sp-> packet_count + 1,那么就将差值的值计到cnt_error。并更新sp-> packet_count。
如果序列号向后移动了,则增加乱序包计数器outoforder_packets。此时,如果cnt_error大于0,则减少cnt_error的计数,并且在debug模式下增加乱序的提示信息。
测试
新版本iperf3安装方法:
yum install git
yum install gcc
git clone https://www.github.com/esnet/iperf.git
cd iperf
./configure
make
make install
make clean
模拟乱序的情况:
tc qdisc add dev lo root netem delay 1ms
tc qdisc change dev lo root netem reorder 100 gap 5 delay 10ms
经测试,yum源自带的iperf3-3.1.7会出现乱序丢包的情况,从github上源码安装的iperf3-3.7没有该问题。