疑难杂症 / 网络工程 · 12 4 月, 2026 0

又被iPerf坑了?从收到0K数据包再谈iPerf3的UDP模式工作机制

发生肾么事了?

去年9月我就被iPerf3坑过一次。那次经历让我知道了iPerf3收发UDP包时会同时用TCP进行控制通信

时隔半年多,我们又要用iPerf3做带宽测试了。这次收信端(server)在公网,监听TCP/1234UDP/1234端口;发信端(client)在内网。收发两端中间有交换机和防火墙。构成大概是下面这样:

发信端(C) -- 防火墙 -- 交换机 -- Internet -> 收信端(S)

为了尽量减少对公网的端口暴露,同时吸取去年的教训,我们为交换机设置了ACL,仅允许内外网双向TCP/1234UDP/1234端口的通信,屏蔽了其他端口。

于是乎,实验开始:

# 收信端
iperf3 -s -p 1234

# 发信端
## TCP测试
iperf3 -c <公网IP> -p 1234 -b 1000 --time 60
## UDP测试
### 注意:iperf3发UDP默认包长度为8192。
### 有些网卡受不了这个,需要手动指定长度为1400。
### 否则可能出现大量fragement,或者收到的包都是0K(被网卡丢弃了)。
iperf3 -c <公网IP> -u -p 1234 -b 1000 --time 60 --length 1400

吊诡的现象出现了:实验中,用iPerf3从内网向外网发TCP包,一切正常。然而发UDP包的时候,发信端却直接卡住,收信端输出显示一直在收到0K的包


怎么解决的?

总之先抓包看看

在多次修改iPerf3参数无果后,笔者用WireShark对两台直连设备间的iPerf3通信进行了抓包,了解了iPerf3的UDP模式工作流程如下:

No.         C                                  S
01  TCP/xxxx|--------- hostname, etc. -------->|TCP/1234
02  TCP/xxxx|<------------- 0x09 --------------|TCP/1234
03  TCP/xxxx|-------------- 0x67 ------------->|TCP/1234
04  TCP/xxxx|-- {"udp":true,"len":8192,...} -->|TCP/1234
05  TCP/xxxx|<------------- 0x0a --------------|TCP/1234
06  UDP/yyyy|------- 15cd5b07 (Random?) ------>|UDP/1234
07  UDP/yyyy|<------ b168de3a (Random?) -------|UDP/1234
08  TCP/xxxx|<------------- 0x01 --------------|TCP/1234
09  TCP/xxxx|-------------- 0x02 ------------->|TCP/1234
10  UDP/yyyy|---------- udp payload ---------->|UDP/1234
11  UDP/yyyy|---------- udp payload ---------->|UDP/1234
12  UDP/yyyy|---------- udp payload ---------->|UDP/1234
13  UDP/yyyy|---------- udp payload ---------->|UDP/1234
14  TCP/xxxx|-------------- 0x0c ------------->|TCP/1234
15  TCP/xxxx|<------------- ... -------------->|TCP/1234

其中:

  • No.10~13 为iPerf3实际用于带宽测试的数据包。其它字符串或意义不明的16进制均为控制信息。
  • xxxxyyyy是两个不同的发信端口,两者默认均为随机生成。
  • No.04 中的JSON信息大致如下:
{
  "udp":true,
  "omit":0,
  "time":60,
  "parallel":1,
  "len":1400,
  "bandwidth":1000,
  "client_version":"3.1.3"
}

然后反观实验用的收信端和发信端,用WireShark抓包,发现了如下现象:

  • 发信端
    • 没有收到No.07中本该由收信端发来的UDP包。
    • 没有发送No.10及其以后的数据。
  • 收信端
    • 没有收到No.10及其以后的数据,它们本该由发信端发送。

恍然大悟!

从上述现象中不难看出,虽然收信端看似收到了0K的UDP数据包,但其实发信端自始至终都没有发送任何UDP包。而其直接原因,就是没有收到No.07的那个4byte的UDP包。它很有可能是启动发包的信号之一。

那为什么没收到呢?因为前文提到:

我们为交换机设置了ACL,仅允许内外网双向TCP/1234UDP/1234端口的通信,屏蔽了其他端口。

而位于内网的发信端,发送No.06的UDP包时使用的源端口是随机端口yyyy。作为回复,收信端向发信端发送No.07的UDP包时使用的目的端口就是yyyy。于是这个UDP包就被ACL给华丽地屏蔽了……

No.         C                                  S
06  UDP/yyyy|------- 15cd5b07 (Random?) ------>|UDP/1234
07  UDP/yyyy|   X<-- b168de3a (Random?) -------|UDP/1234

而TCP通信由于是有状态的,交换机/防火墙在处理TCP时,一般会自动做“连接跟踪”,因此不受该机制的影响。

怎么办?

如果不想修改交换机的ACL设置,便可以拿iPerf做文章。

iPerf3支持指定发信端口。既然ACL只允许UDP/1234,那我们就指定发信端口也为UDP/1234。只需要在启动发包时加上参数--cport即可:

# 发信端
## UDP测试
### 指定发信端口为1234
iperf3 -c <公网IP> -u -p 1234 -b 1000 --time 60 --length 1400 --cport 1234

这样一来实验就能正常进行了。收发双方间的通信变成下面这样:

No.         C                                  S
01  TCP/xxxx|--------- hostname, etc. -------->|TCP/1234
02  TCP/xxxx|<------------- 0x09 --------------|TCP/1234
03  TCP/xxxx|-------------- 0x67 ------------->|TCP/1234
04  TCP/xxxx|-- {"udp":true,"len":8192,...} -->|TCP/1234
05  TCP/xxxx|<------------- 0x0a --------------|TCP/1234
06  UDP/1234|------- 15cd5b07 (Random?) ------>|UDP/1234
07  UDP/1234|<------ b168de3a (Random?) -------|UDP/1234
08  TCP/xxxx|<------------- 0x01 --------------|TCP/1234
09  TCP/xxxx|-------------- 0x02 ------------->|TCP/1234
10  UDP/1234|---------- udp payload ---------->|UDP/1234
11  UDP/1234|---------- udp payload ---------->|UDP/1234
12  UDP/1234|---------- udp payload ---------->|UDP/1234
13  UDP/1234|---------- udp payload ---------->|UDP/1234
14  TCP/xxxx|-------------- 0x0c ------------->|TCP/1234
15  TCP/xxxx|<------------- ... -------------->|TCP/1234