简介:TCP_UDP_PerformanceTest 是一款面向网络编程开发者与系统运维人员的传输层协议性能测试工具,用于对比 TCP 与 UDP 在真实网络环境下的吞吐量、延迟与丢包率表现,帮助判断高并发低延迟场景下应选用哪种协议。资源包共 5 个文件,约 79KB,包含可执行主程序、封装底层通信逻辑的 dll 库、记录服务器地址与端口等运行参数的 config 配置、存储测试结果或参数的 xml 文件以及授权用的 sn 文件,结构紧凑、开箱即用。用户可自定义发送数据量、发送速率与测试时长,模拟不同负载条件,观察 TCP 的可靠重传与 UDP 的轻量快速之间的实际差异。目前已有 1639 人学习下载,适合需要评估协议选型、优化网络服务性能的读者参考实践。
1. TCP_UDP_PerformanceTest 测试工具:为什么你测出来的带宽总是对不上
同一个千兆交换机,两台机器之间iperf3跑 TCP 能到 940 Mbps,换成 UDP 却怎么调都上不去,或者干脆丢包丢到 30% 以上——这种场景我见过太多次。问题往往不在网卡,也不在交换机,而在于测试方法本身:TCP 有拥塞控制和重传兜底,UDP 没有,两者的测试逻辑、参数含义、结果解读方式完全不同。TCP_UDP_PerformanceTest 测试工具要解决的,就是让你在同一套框架下分别对 TCP 和 UDP 做可复现的吞吐、时延、丢包测试,而不是拿一个数字到处套。
这篇文章面向需要做网络性能验证的开发和运维:你可能在调优tcp协议栈参数、验证udp协议栈的转发能力、排查tcp连接建立慢的问题,或者只是想知道自己写的 socket 程序到底能跑多快。我会从测试模型讲起,落到具体命令和参数,再把踩过的坑摊开说。读完你至少能做到:知道该测什么、怎么测、结果怎么读、哪里容易翻车。
2. 先把测试模型立住:TCP 和 UDP 到底该测什么
2.1 两种协议的性能指标根本不是一回事
很多人做性能测试的第一反应是“跑个带宽看看”,但 TCP 和 UDP 的“带宽”含义不同。TCP 的吞吐量受拥塞窗口、往返时延、丢包率共同影响,你测到的数字是协议栈在特定网络条件下协商出来的结果,不是链路的上限。UDP 则是“发多少是多少”,发送端可以一直灌,接收端丢不丢、丢多少完全看中间设备和缓冲区。
所以测试目标要分开定:
- TCP 关注:稳定吞吐量、连接建立时间、重传率、窗口变化。适合验证
tcp标定原理相关的参数调优效果,比如netsh interface tcp show global里看到的窗口缩放、ECN 状态。 - UDP 关注:极限发送速率、丢包率、时延抖动、乱序比例。适合验证
udp端口测试、udp探测场景下的链路质量。
我一般会先明确一个问题:你是要验证“这条链路最好能跑多少”,还是“业务在真实条件下能跑多少”。前者用 UDP 打流逼近上限,后者用 TCP 模拟真实连接行为。两者不能互相替代。
2.2 测试拓扑和最小可复现环境
不管用什么工具,拓扑要先固定下来。最常见的两种:
直连测试:两台机器网卡直连,或者通过一台交换机。这种环境变量最少,适合做基线。注意关闭防火墙、关闭省电模式、确认网卡协商速率。
跨设备测试:经过路由器、防火墙、NAT。这种环境更接近真实,但变量多,需要逐段排查。
最小环境建议:
| 角色 | 配置要求 | 说明 |
|---|---|---|
| 发送端 | 千兆及以上网卡,CPU 不低于 4 核 | 避免 CPU 成为瓶颈 |
| 接收端 | 同上,缓冲区调大 | 接收窗口决定 UDP 丢包率 |
| 中间设备 | 关闭 QoS 或确认策略 | 限速策略会直接污染结果 |
| 操作系统 | Linux 或 Windows 均可 | 命令略有差异 |
提示:测试前用
ethtool确认网卡实际协商速率,不要看标称值。我遇到过网线老化导致协商到 100 Mbps,测了半天以为是软件问题。
2.3 工具选型:iperf3 为主,自研脚本补位
iperf3是TCP_UDP_PerformanceTest场景下最通用的选择,支持 TCP 和 UDP 两种模式,参数清晰,结果可解析。常见做法是:
- TCP 测试:
iperf3 -c <server> -t 30 -P 4 - UDP 测试:
iperf3 -c <server> -u -b 1G -t 30
但iperf3有局限:它测的是应用层吞吐,不直接暴露协议栈内部指标。如果你需要看tcp dup ack机制触发情况、tcp三次握手耗时分布,就得配合tcpdump或ss命令。自研脚本的价值在于可以定制测试逻辑,比如模拟java tcp客户端重连场景下的连接风暴。
我一般会先用iperf3跑基线,再用脚本做针对性验证。两者结果对不上时,优先信抓包。
3. 用 iperf3 跑通 TCP 和 UDP 的最小命令集
3.1 服务端和客户端的启动顺序与参数
先在一台机器上启动服务端:
# 服务端:监听 5201 端口,绑定所有地址 iperf3 -s -p 5201 # 如果需要长期运行并输出日志 iperf3 -s -p 5201 --logfile /var/log/iperf3.log -D-s表示服务端模式,-p指定端口,-D是后台运行。服务端本身不需要太多参数,但要注意防火墙放行。
客户端 TCP 测试:
# TCP 测试:持续 30 秒,4 条并发流,输出间隔 1 秒 iperf3 -c 192.168.1.100 -p 5201 -t 30 -P 4 -i 1 # 反向测试:服务端发送,客户端接收 iperf3 -c 192.168.1.100 -p 5201 -t 30 -P 4 -R-c是客户端模式,-t是持续时间,-P是并发流数量,-i是报告间隔,-R反转方向。并发流数量很关键:单条 TCP 流在长肥管道上可能跑不满带宽,-P 4或-P 8能更好压满链路。
客户端 UDP 测试:
# UDP 测试:目标带宽 1Gbps,持续 30 秒,包长 1400 iperf3 -c 192.168.1.100 -p 5201 -u -b 1G -t 30 -l 1400 -i 1-u切换 UDP 模式,-b指定目标带宽,-l指定包长。UDP 模式下-b是必须关注的参数:设得太低测不出上限,设得太高丢包率飙升。我一般从链路速率的 80% 开始试,逐步往上加。
3.2 结果里哪些数字真正值得看
TCP 测试输出里,重点看三行:
- Sender/Receiver 的 Bitrate:两者差距大说明接收端有瓶颈。
- Retr:重传次数。非零就要查丢包原因。
- Cwnd:拥塞窗口。如果一直上不去,说明 RTT 或丢包限制了吞吐。
UDP 测试输出里,重点看:
- Jitter:时延抖动。实时业务对这个敏感。
- Lost/Total:丢包率。超过 1% 就要警惕。
- 发送端和接收端的带宽差:差多少就是丢了多少。
# 解析 iperf3 JSON 输出,提取关键指标 iperf3 -c 192.168.1.100 -p 5201 -t 10 -J > result.json # 用 jq 提取 TCP 重传和吞吐 jq '.end.sum_sent.retransmits, .end.sum_received.bits_per_second' result.json-J输出 JSON 格式,方便脚本化。jq是解析 JSON 的常用工具,上面命令分别取重传次数和接收端比特率。做自动化测试时,这个组合比解析文本输出可靠得多。
3.3 参数怎么调:从默认值到针对性配置
iperf3的默认参数适合快速验证,但做性能测试需要针对性调整:
| 参数 | 默认值 | 建议调整 | 适用场景 |
|---|---|---|---|
-P | 1 | 4~8 | TCP 多流压满链路 |
-l | TCP 自适应 / UDP 1460 | UDP 设 1400 或 1200 | 避免分片 |
-b | UDP 1Mbps | 从 80% 链路速率起 | UDP 极限测试 |
-w | 系统默认 | 256K 或 1M | 高带宽长时延链路 |
-t | 10 秒 | 30~60 秒 | 稳定状态测量 |
-w是 TCP 窗口大小,在长肥管道上必须调大,否则单流吞吐上不去。UDP 没有窗口概念,但接收端缓冲区大小会影响丢包率,这个要在系统层面调,不是iperf3参数。
注意:UDP 测试时
-b设成0表示不限速,但实际会受 CPU 和网卡限制。不要用这个值做精确测量。
4. 自研脚本补位:抓包验证和协议栈指标采集
4.1 用 Python 写一个最小 TCP/UDP 测试客户端
iperf3覆盖不到的场景,比如模拟特定连接模式、采集握手耗时,就需要自己写。下面是一个最小 TCP 客户端,记录连接建立时间和吞吐:
import socket import time def tcp_test(host, port, duration=10): # 记录三次握手耗时 start = time.time() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, port)) handshake_ms = (time.time() - start) * 1000 print(f"TCP handshake: {handshake_ms:.2f} ms") # 持续发送数据,统计吞吐 payload = b'x' * 1400 sent_bytes = 0 end_time = time.time() + duration while time.time() < end_time: try: n = sock.send(payload) sent_bytes += n except socket.error as e: print(f"Send error: {e}") break elapsed = duration print(f"Throughput: {sent_bytes * 8 / elapsed / 1e6:.2f} Mbps") sock.close() tcp_test('192.168.1.100', 5201)这段代码做了两件事:用time.time()夹住connect()调用测握手耗时,然后循环send()统计发送字节数。payload设为 1400 字节是为了避免 IP 分片。实际使用时,接收端也要有对应程序消费数据,否则发送缓冲区满了会阻塞。
UDP 版本更简单,但要注意sendto()不保证送达:
import socket import time def udp_test(host, port, duration=10, rate_mbps=100): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) payload = b'x' * 1400 interval = 1400 * 8 / (rate_mbps * 1e6) # 每个包的理论间隔 sent = 0 end_time = time.time() + duration while time.time() < end_time: sock.sendto(payload, (host, port)) sent += 1 time.sleep(interval) # 简单限速 print(f"Sent {sent} packets, {sent * 1400 * 8 / duration / 1e6:.2f} Mbps") sock.close() udp_test('192.168.1.100', 5201)interval是按目标速率算出的发包间隔,time.sleep()做粗粒度限速。这种方式精度不高,但足够验证基本连通性和大致速率。要精确限速得用令牌桶或SO_TXTIME。
4.2 用 tcpdump 和 ss 看协议栈内部状态
脚本测的是应用层,协议栈内部要靠系统工具:
# 抓取 TCP 握手和重传 tcpdump -i eth0 -nn 'tcp port 5201 and (tcp[tcpflags] & (tcp-syn|tcp-ack) != 0)' # 查看当前 TCP 连接状态和窗口 ss -tin state established '( dport = :5201 or sport = :5201 )' # 统计重传和 dup ack netstat -s | grep -E 'retrans|dup'tcpdump的过滤表达式里tcp[tcpflags]用来匹配标志位,tcp-syn和tcp-ack组合能抓到握手包。ss -tin输出里retrans字段直接显示重传次数,cwnd显示拥塞窗口。netstat -s是全局统计,适合看趋势。
这些数据配合iperf3结果,能定位大部分性能问题。比如iperf3显示重传高,ss看到cwnd上不去,基本就是链路丢包导致拥塞控制保守。
4.3 自动化测试的串联方式
单次测试说明不了问题,需要多轮次、多参数组合。我一般用 shell 脚本串联:
#!/bin/bash # 多轮 TCP 测试,输出 CSV for p in 1 2 4 8; do for i in $(seq 1 3); do result=$(iperf3 -c 192.168.1.100 -p 5201 -t 10 -P $p -J) bw=$(echo $result | jq '.end.sum_received.bits_per_second') retr=$(echo $result | jq '.end.sum_sent.retransmits') echo "$p,$i,$bw,$retr" >> tcp_result.csv done done外层循环并发流数量,内层循环重复次数,每次提取带宽和重传写入 CSV。这样跑完能看出并发流和吞吐的关系,以及结果的稳定性。UDP 版本类似,把-u -b加上,提取丢包率字段。
提示:自动化测试前先手动跑一轮确认环境正常,否则脚本跑完发现全是异常值,浪费时间。
5. 避坑指南:TCP_UDP_PerformanceTest 最常见的 5 个翻车点
5.1 现象:UDP 丢包率极高,但链路看起来没问题
原因:接收端 socket 缓冲区太小,或者接收程序处理不过来。UDP 没有流控,发得快收得慢就直接丢。
解决:调大接收缓冲区,Linux 下用sysctl -w net.core.rmem_max=26214400,程序里用setsockopt(SO_RCVBUF)。同时确认接收程序不是单线程阻塞处理。
5.2 现象:TCP 吞吐远低于预期,但重传为 0
原因:单条流的拥塞窗口受 RTT 限制,带宽时延积大的链路上单流跑不满。或者发送端 CPU 是瓶颈。
解决:增加并发流-P,调大窗口-w,检查发送端 CPU 使用率。用top看iperf3进程是否跑满一个核。
5.3 现象:测试结果每次差异很大,无法复现
原因:中间设备有动态限速、其他流量干扰、或者网卡省电模式导致降频。
解决:固定测试时间窗口,关闭网卡省电(ethtool -s eth0 wol d只是关唤醒,省电要查驱动参数),在交换机上确认没有 QoS 策略。多跑几轮取中位数。
5.4 现象:UDP 测试显示发送 1Gbps,接收只有 300Mbps
原因:中间设备限速、接收端丢包、或者发送端统计的是“尝试发送”而非“成功发送”。
解决:在接收端同时抓包统计实际到达包数,对比iperf3接收端报告。如果抓包数对得上接收端报告,说明丢在中间;对不上,说明接收端程序有问题。
5.5 现象:TCP 连接建立慢,但吞吐正常
原因:DNS 解析慢、SYN 重传、或者tcp三次握手过程中有安全设备拦截。
解决:用tcpdump抓握手包看时间戳,确认 SYN、SYN-ACK、ACK 的间隔。如果 SYN-ACK 来得慢,查中间设备;如果 ACK 发得慢,查客户端协议栈。
6. 进阶:把测试结果变成可对比的基线数据
单次测试的数字没有意义,有意义的是基线。我的习惯是:每套环境第一次测试时,固定参数跑 5 轮,取中位数和标准差,存成基线文件。后续任何变更——换网卡、调内核参数、改应用配置——都跟基线对比。
具体做法:
# 生成基线:TCP 4 流,30 秒,5 轮 for i in $(seq 1 5); do iperf3 -c 192.168.1.100 -p 5201 -t 30 -P 4 -J | \ jq -r '[.end.sum_received.bits_per_second, .end.sum_sent.retransmits] | @csv' \ >> baseline_tcp.csv done # 计算中位数和标准差 awk -F, '{sum+=$1; vals[NR]=$1} END { n=asort(vals); median=(n%2)?vals[(n+1)/2]:(vals[n/2]+vals[n/2+1])/2; for(i=1;i<=n;i++){sq+=(vals[i]-sum/n)^2} printf "Median: %.2f Mbps, StdDev: %.2f\n", median, sqrt(sq/n) }' baseline_tcp.csvjq -r输出 CSV 格式,awk计算中位数和标准差。中位数比平均值更能反映典型值,标准差说明稳定性。如果标准差超过中位数的 10%,说明测试环境不够干净,结果不可信。
UDP 基线同理,但重点看丢包率和抖动的分布。我一般会把不同-b值下的丢包率画成曲线,找到“丢包率开始明显上升”的拐点,这个拐点就是实际可用带宽。
注意:基线不是一次性的。网络设备固件升级、内核版本变更、甚至机房温度变化都可能影响结果。定期重跑基线,才能发现漂移。
这套方法我用了几年,最大的教训是:不要相信单次测试的数字,也不要相信没有抓包验证的结论。有一次排查一个“TCP 吞吐上不去”的问题,iperf3显示重传为 0,差点去查应用层,结果tcpdump抓到大量 dup ack,才发现是中间设备乱序导致的。工具给的是线索,不是答案。希望帮到你。
本文还有配套的精品资源,点击获取