简介:TCP&UDP测试工具是一款面向网络工程师与开发人员的轻量级网络协议调试软件,主要用于TCP/UDP通信的性能评估、故障排查与协议验证,可覆盖网络编程、设备测试和安全检查等场景。资源以rar压缩包形式提供,共13个文件,其中包含3个exe可执行程序、2个ini配置文件以及htm/css说明页面等,整体仅有1.5MB;解压后免安装即可运行,非常适合在临时测试环境中快速部署。目前已有388人学习浏览。工具支持自定义源/目的地址、指定端口、数据包大小和传输速率,可模拟真实网络环境下的TCP可靠传输与UDP低延迟通信;同时提供压力测试、吞吐量测试、延迟测试和丢包测试,帮助用户评估服务器承载能力、定位传输瓶颈。借助内置可视化配置和案例页面,测试过程中还能直观对照协议行为,发现不正确的协议实现或配置错误,适用于网络调试、课程实验及安全配置检查等场景。
1. TCP&UDP 测试工具:从「能通」到「能测准」要补的五个短板
拿到“TCP&UDP 测试工具”这个需求,大多数人的第一反应是找一个免费软件,填上 IP 和端口,点一下 Start,看到“Connected”就以为完成任务。真正做网络调试的工程师都清楚,这种“能通”离“测准”还有很长一段距离:TCP 三次握手成功不代表业务数据可靠,UDP 收到回包也不代表端口没被防火墙静默丢弃,更不用说带宽、抖动、乱序这些指标根本不是一次连接就能看出来的。这篇文章会把 TCP/UDP 测试工具拆成一套可落地的调试方案,覆盖选型、最小实现、参数调优和高频踩坑,适合嵌入式、运维、工控和客户端开发者照着复现。
2. 协议测试的分层逻辑:连通性、可靠性、带宽和端口探测不是同一件事
2.1 两份协议栈先补课:三次握手与无连接的 UDP 各自要验证什么
TCP 测试的核心不是“能连上”,而是连上之后的所有状态变化。三次握手只是把客户端和服务端的序列号、窗口、MSS 协商好了,真正决定业务质量的还有后续的数据传输、窗口收缩、重传和连接关闭。所以 TCP 测试工具至少要能区分几个层次:SYN 是否到达、ACK 是否回来、应用层是否返回数据、连接是否能维持足够长时间。很多工具只做到了第二层,在自动化测试里这种工具会漏掉一半问题。
UDP 是完全相反的逻辑。它没有握手,也没有重传,发送端把数据报扔给协议栈就结束了。UDP 测试工具真正要捕捉的是两类结果:一类是能不能把数据发出去、对方能不能收到;另一类是发送路径上有没有 ICMP 不可达报文被悄悄丢弃。由于 UDP 本身无状态,测试工具必须自己承担“回应”职责,否则你根本分不清是网络丢包、防火墙拦截还是对方协议栈根本没在处理。这也是 UDP 端口测试比 TCP 端口测试复杂的地方。
从 TCP/UDP 测试工具的设计角度讲,一份工具不太可能同时把这两类验证做到极致,但至少要让你明确当前测的是哪一层:连通性层、传输层还是应用层。连通性层只需要一个端口探测,传输层需要收发数据并判断乱序和重传,应用层则需要把业务协议(比如 Modbus TCP、自定义报文)拼装好再发出去。工具选型之前先回答这个问题,比下载任何软件都重要。
2.2 四类测试工具的选型矩阵与最小命令
常见做法是把工具分成四类:端口连通性、通用收发、性能打流、报文分析。端口连通性用 tcping 或 nc 的 -z 参数;通用收发用 nc 或自写脚本;性能打流用 iperf3;报文分析用 tcpdump/Wireshark。它们解决的问题完全不同,混用是新手最常犯的错。
| 测试目标 | 推荐工具 | 为什么选它 | 典型场景 |
|---|---|---|---|
| 端口是否开放 | tcping / nc -z | 轻量、不建立完整业务连接 | 排查 TCP 端口不通 |
| 通用 TCP/UDP 收发 | nc / socat | 支持双向、可带数据 | 验证自定义协议连通性 |
| 带宽、丢包、抖动 | iperf3 | 内置 UDP 打流和 JSON 输出 | 评估链路质量 |
| 协议栈行为分析 | tcpdump / Wireshark | 能看到握手、重传、窗口变化 | 定位 dup ack、乱序 |
先用最小命令把端口连通性查掉。nc 的 -vz 是只探测不发送数据,适合快速判断:
nc -vz 192.168.1.10 9000执行后如果显示Connection to 192.168.1.10 9000 port [tcp/*] succeeded!,说明 TCP 握手已经完成。此时的问题是:这个结果只能证明服务端有进程在监听,不能证明服务端会正常处理你的业务报文。要继续验证收发,需要启动一个监听端并主动发数据:
# 终端 A:监听 UDP 9000,并把收到的内容打印出来 nc -u -l 9000 # 终端 B:发送一段测试文本 echo "hello from tcpudp-test" | nc -u 192.168.1.10 9000这段命令的逻辑是:UDP 没有连接,所以必须有一个常驻的接收端。nc -u -l 进入监听模式后,发送端使用 nc 向目标端口注入数据。此时如果终端 A 打印出文本,说明 UDP 数据报已经穿过网络栈到达应用层。如果终端 A 没反应,就要去抓 ICMP 包确认是否被中间设备丢弃了。这比单纯用在线端口扫描工具更能反映真实网络路径。
2.3 失败时先改哪几个参数:timeout、retry、buffer
无论用哪类工具,测试结果的可靠性都取决于三个参数:超时时间、重试次数、缓冲区大小。TCP 扫描工具的默认超时往往只有几秒,放在跨地域链路上,一个正常的握手响应需要 3 秒以上,工具就会误报失败。我一般会把 TCP 连接超时调到 5 秒以上,UDP 探测则必须依赖重试,因为 UDP 没有确认机制,一次丢包就可能让你误判端口不可达。
缓冲区大小对 UDP 测试尤其关键。UDP 协议栈默认接收缓冲区在 Linux 上是几十 KB 到上百 KB 不等,如果发送端一次性灌入大报文,接收端缓冲溢出后数据报会被直接丢弃,测试工具看到的丢包率就会飙升。你需要在工具里把 buffer 设置为与业务报文一致,而不是越大越好。例如 Modbus TCP 报文通常只有 260 字节左右,测试时用一个 512 字节的 buffer 足够了;如果业务走的是视频流,才需要按 MTU 或 jumbo frame 来调整。
3. 自己动手写一个 TCP/UDP 测试工具:Python 双栈脚本与参数落地
3.1 服务端与客户端的最小实现
很多现成工具不满足定制需求,写一个足够小的 TCP/UDP 测试工具反而更顺手。我用 Python 标准库 socket 做过一版,没有第三方依赖,部署到目标机器时不用装 pip 包。这个脚本分两部分:一个是通用服务端,既能监听 TCP 也能监听 UDP,收到数据后原样返回;另一个是通用客户端,主动连接并发送自定义内容。下面贴出的是精简后的服务端:
# server.py - TCP/UDP 通用回环服务端 import argparse import socket def run_tcp_server(port, buffer_size): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(("0.0.0.0", port)) sock.listen(5) print(f"TCP server listening on :{port}", flush=True) while True: conn, addr = sock.accept() with conn: print(f"connect from {addr}", flush=True) data = conn.recv(buffer_size) if data: conn.sendall(b"echo:" + data) def run_udp_server(port, buffer_size): with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: sock.bind(("0.0.0.0", port)) print(f"UDP server listening on :{port}", flush=True) while True: data, addr = sock.recvfrom(buffer_size) sock.sendto(b"echo:" + data, addr) if __name__ == "__main__": parser = argparse.ArgumentParser(description="TCP/UDP test server") parser.add_argument("--proto", choices=["tcp", "udp"], required=True) parser.add_argument("--port", type=int, required=True) parser.add_argument("--buffer-size", type=int, default=1024) args = parser.parse_args() if args.proto == "tcp": run_tcp_server(args.port, args.buffer_size) else: run_udp_server(args.port, args.buffer_size)逻辑说明:TCP 分支走经典的 listen-accept-recv 流程,客户端连上后服务端收到第一段数据就回显。UDP 分支走 recvfrom-sendto,端口固定监听在 0.0.0.0 上,这样局域网内任意网卡的请求都能到达。注意 TCP 分支设置了 SO_REUSEADDR,目的是让进程重启后端口不会进入 TIME_WAIT 状态导致 bind 失败,这在调试中会经常救你一命。buffer_size 这里默认为 1024,如果你要模拟 MTU 分片,改成 1472 更贴近实际 UDP 负载上限。
客户端的实现思路是参数控制协议、目标地址、端口和发送内容,并统计耗时:
# client.py - TCP/UDP 通用测试客户端 import argparse import socket import time def tcp_send(host, port, message, timeout): with socket.create_connection((host, port), timeout=timeout) as s: s.sendall(message.encode()) resp = s.recv(1024) print(f"TCP response: {resp.decode()!r}") def udp_send(host, port, message, timeout): with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: s.settimeout(timeout) s.sendto(message.encode(), (host, port)) try: resp, addr = s.recvfrom(1024) print(f"UDP response from {addr}: {resp.decode()!r}") except socket.timeout: print("UDP timeout, no response") if __name__ == "__main__": parser = argparse.ArgumentParser(description="TCP/UDP test client") parser.add_argument("--proto", choices=["tcp", "udp"], required=True) parser.add_argument("--host", required=True) parser.add_argument("--port", type=int, required=True) parser.add_argument("--message", default="ping") parser.add_argument("--timeout", type=float, default=5.0) args = parser.parse_args() start = time.time() if args.proto == "tcp": tcp_send(args.host, args.port, args.message, args.timeout) else: udp_send(args.host, args.port, args.message, args.timeout) print(f"elapsed: {time.time() - start:.3f}s")这份客户端代码里最值得留意的是 UDP 分支的 timeout 处理。UDP 发送本身几乎不会报错,真正的失败信息只能靠 recvfrom 超时暴露出来。你在写自动化测试脚本时,UDP 的结果应该以“是否收到响应”为准,而不是以 sendto 是否抛出异常为准。TCP 分支的 timeout 则直接体现在 create_connection 上,连不上会抛出 socket.timeout 或 ConnectionRefusedError,不需要额外判断。
3.2 把脚本变成可复用的命令行工具
上面两个脚本只能手动跑,在自动化测试里不实用。常见的改进方向是合并成一个命令,类似net-tools.py tcp-client --host 10.0.0.2 --port 9000 --message hello,并且把输出改成带时间戳的 JSON 行,方便下游解析。合并时只需要把 argparse 的子命令加上,内部复用刚才的两个函数。
另外建议增加“连续模式”。很多偶发问题要跑几千次才出现,人工执行一次根本看不出统计意义。我一般会增加--count N和--interval 0.1两个参数,让客户端循环执行并汇总成功率、平均延迟和最大延迟。代码结构上不要在多线程里共享 socket,直接顺序执行即可,这样结果更可控。再加上--log-file把每次结果落盘,排查现场问题时能少扯很多皮。
参数调优方面,TCP 连续连接测试要特别关注 TIME_WAIT。如果每次测试都是客户端主动关闭连接,短时间大量新建连接会让客户端端口进入 TIME_WAIT,后续 connect 可能抛 “Address already in use”。解法是使用SO_REUSEADDR,但注意这只能缓解 bind 冲突,真正要避免 TIME_WAIT 堆积,更好的做法是让服务端主动关闭,或者把连接改为长连接复用。这属于 TCP 测试工具最容易踩的坑,后面避坑章节会展开。
3.3 用脚本还原三次握手与 dup ack 现象
脚本能告诉我们结果,但要知道为什么还需要抓包。我在调试 TCP 连接慢时,通常会在目标机器上同时跑 tcpdump,然后执行客户端脚本,再把抓包结果导入 Wireshark。三次握手的三个包分别显示为 SYN、SYN-ACK、ACK,这个顺序一目了然。如果只看到 SYN 而看不到 SYN-ACK,大概率是目标端口没监听或者防火墙丢包;如果 SYN-ACK 到了但最终的 ACK 没回来,问题出在客户端协议栈或中间设备上。
dup ack 机制在测试工具里怎么验证?简单办法是抓包时看 TCP 头部 sequence number。客户端发送大数据时触发快速重传,抓包里会出现连续三个相同 ACK 号。我这里有一个快速验证脚本片段,它通过循环发送大报文来制造乱序窗口:
# 注意:这段脚本只用来观察 dup ack,不要在生产环境跑 import socket s = socket.create_connection(("192.168.1.10", 9000)) payload = b"A" * 4096 for i in range(200): s.sendall(payload) s.recv(1024)观察方式是在另一个终端执行sudo tcpdump -i eth0 tcp and host 192.168.1.10 -w dup.pcap,跑完后打开 dup.pcap,用 Wireshark 的 Statistics -> TCP Stream Graph 查看序列号曲线。如果曲线出现回折,说明确实发生了重传;如果出现平直的阶梯,说明接收窗口在收缩。这个实验的价值在于:很多 TCP 测试工具能告诉你“通了”,但只有抓包能告诉你“为什么慢”,两者必须配合使用。
4. 把玩具升级成工具箱:iperf3 打流与端口探测的实战用法
4.1 iperf3 的 TCP/UDP 双向打流与参数调优
自研脚本适合小报文验证,真要看带宽和丢包率,我一般直接上 iperf3。它的部署成本极低,两端各一个可执行文件就能跑。服务端启动命令:
iperf3 -s -p 5201客户端先做 TCP 打流,默认向服务端发送数据直到指定时间结束:
iperf3 -c 192.168.1.10 -p 5201 -t 30参数说明:-c 指定目标地址,-p 指定端口,-t 是测试时长(秒),-t 30 表示打流 30 秒。输出里看三项:Bitrate、Retr、Cwnd。Retr 是重传次数,如果 30 秒内 Retr 持续增长,说明网络有丢包或者接收端缓冲区不够。Cwnd 是拥塞窗口,数值不增长往往意味着链路存在瓶颈,比如带宽受限或者对端 window 太小。
真正能压出 UDP 协议栈问题的是 UDP 打流。UDP 打流必须手动指定带宽,因为 iperf3 默认不知道你想要的发送速率:
iperf3 -c 192.168.1.10 -p 5201 -u -b 200M -t 20 --get-server-output参数说明:-u 切到 UDP 模式,-b 200M 表示目标带宽 200Mbps,-t 20 表示持续 20 秒,--get-server-output 让客户端把服务端的统计一起打出来,省得来回对账。UDP 模式的输出中会出现 Total datagrams、Jitter、Lost/Total Datagrams 三行,丢包率是失包数除以总数,比如14234/100000 (14%),通常超过 1% 就要开始排查。Jitter 是抖动,单位是毫秒,对视频业务来说抖动比平均延迟更重要。丢包率高时先看是不是发送速率超出了链路容量,把 -b 降到链路带宽的 80% 再测一轮,这叫留余量,不是掩盖问题。
4.2 从探测到验收:tcping、nc、端口扫描
iperf3 解决的是“带宽够不够”,端口状态还得靠更轻的工具。tcping 比普通 ping 更进一步,它做的是 TCP 三次握手,所以在禁 ICMP 的网络上也能用。常见用法:
tcping -t 5 -i 192.168.1.20 8080-t 5 是超时 5 秒,-i 是无限循环,方便观察端口抖动。如果只测一次,去掉 -i。tcping 的用处是快速区分“端口没开”和“网络不通”:端口没开会立刻返回 Connection refused;网络不通会等到超时时间结束才报错。现场排查时这两个现象对应完全不同的人去处理,所以看到结果先别急,把拒绝和超时分开记录。
端口扫描在生产环境要谨慎。nmap 扫一个 IP 的常用命令:
nmap -sS -Pn -p 80,443,9000 192.168.1.20-sS 是 SYN 半开扫描,只发 SYN 不建连,速度比全连接快。-Pn 表示跳过主机发现,直接对端口做探测,适合处理禁 ICMP 的主机。跑完输出里 open、filtered、closed 三种状态要分清楚:open 说明有服务监听,filtered 说明被防火墙拦截,closed 说明主机可达但没有服务。很多运维只看 open 和 closed,漏了对 filtered 的关注,这是自动化测试脚本误报的主要来源。
在 Modbus TCP 这类工控场景里,验收分三步:先用 tcping 确认 502 端口可达,再用自研脚本发一个标准的 Modbus TCP 读保持寄存器请求,最后抓包对比请求响应的 transaction id 和 protocol id。三步都通过才算这个端子处于可服务状态。单一工具测通并不能覆盖业务协议的正确性,自动化测试工具链必须这样串起来。
4.3 现场问题的三条对照线索
在客户现场做过几轮排障后,我总结出三条固定对照线索。第一条线索是“TCP 握手成功但业务超时”,这种问题往往不在网络,而在业务层;需要把测试工具切换到应用层协议,比如发一个真实的业务心跳包。第二条线索是“UDP 能发不能收”,先确认对端有没有 listen,再用 tcpdump 抓 ICMP 的 port unreachable,如果抓到说明端口确实没监听;抓不到就要怀疑防火墙静默丢包。第三条线索是“TCP 连接时好时坏”,重点看服务端连接队列是否满了,ss -lnt中 Recv-Q 长期大于 0 说明 accept 太慢,工具层面怎么调都无效。
5. TCP/UDP 测试工具的避坑清单:五个高频翻车点与排查步骤
5.1 UDP 测试“成功”了,但目标端口根本没监听
现象是客户端用 UDP 发送数据,socket 不报错,程序也显示“sent 100 bytes”。去看服务端,发现日志里什么都没收到。原因是 UDP 面向无连接,数据报发出后本端协议栈不会收到任何反馈,如果中间路由器或目标机器防火墙静默丢弃,发送端永远感知不到。
解决步骤:第一,在目标机器上用ss -lunp | grep 9000确认进程真的绑定了端口;第二,在发送端执行tcpdump -i any udp port 9000抓包,看报文是否离开本机;第三,再到接收端抓包,如果连续抓一段时间接收端一无所获,问题在网络路径或防火墙。解决了这三个位置中的哪一个,UDP 测试结果才有意义。
5.2 TCP connect 超时,UDP 却一直有回应
现象是同一台设备上 TCP 端口测试总是超时,但 UDP 测试却能收到回包。很多人第一反应是服务端只开了 UDP。真实原因可能有两种:TCP 和 UDP 是不同协议栈层级的服务,服务端可能只监听 UDP;也可能是防火墙只允许 UDP 而拦截了 TCP 的 SYN。
排查时先执行nc -vz 目标IP 端口,确认是否能拿到 succeeded。如果超时,再换一个不常用的端口比如 50000 测试,仍然超时基本可以断定是防火墙拦截。若不确定防火墙规则,用nmap -sS -Pn -p 9000看端口状态,输出是 filtered 就说明中间设备拦了 SYN。此时拿 TCP 测试工具反复重试没有意义,正确方向是审计防火墙规则或服务端监听地址。
5.3 回环地址测不出防火墙和路由问题
现象是测试工具指向 127.0.0.1 或 localhost 时全部通过,指向本机局域网 IP 时部分失败,指向远程设备时基本失败。原因是回环流量从 lo 接口直接回到协议栈,根本不走网卡驱动、防火墙入站规则和路由表,所以测不出任何真实网络问题。
解决方法是做分层测试:先测127.0.0.1验证服务端程序本身可用;再测本机局域网 IP,验证监听地址是否绑到 0.0.0.0;最后测跨主机 IP,验证物理链路和防火墙。很多自动化测试工具配置里默认填 localhost,上线前必须改成目标机器的真实 IP,否则报告全是假阳性。
5.4 丢包率忽高忽低,顺序乱跳,误以为网卡坏了
现象是 iperf3 UDP 打流丢包率第一次 2%,第二次 30%,第三次 0%,同时收到的报文顺序完全错乱。原因大概率不是网络坏了,而是接收端操作系统的 UDP buffer 溢出。Linux 上 UDP 是尽力而为,内核接收队列满后直接丢包,而且不会通知应用层。
先检查内核参数再下结论:
netstat -su | grep -i error cat /proc/sys/net/core/rmem_max cat /proc/sys/net/core/wmem_max如果 error 计数持续增长,增大缓冲区通常能解决。临时调整可以这样:
sysctl -w net.core.rmem_max=134217728 sysctl -w net.core.wmem_max=134217728参数说明:134217728 是 128MB,适合高吞吐 UDP 测试。但要注意这个值会影响整个主机的内存占用,别在低配机器上盲目调大。调整后重新打流,如果丢包率明显下降,说明问题在接收缓冲而不是链路。换到 Windows 环境时,可以用netsh interface tcp show global检查 TCP 全局参数里的接收窗口自动调谐级别,UDP 测试则没有对应命令,只能靠第三方工具加大接收缓冲,这也是 Windows 上 UDP 压测更容易丢包的原因之一。
5.5 端口明明开着,程序总报 Address already in use
现象是服务端进程还在运行,重启测试工具时 socket bind 抛 “Address already in use”。原因是上一个连接断开后有大量 TIME_WAIT 连接占用了端口对,或者服务端进程没有从内核数据结构里彻底退出。
解决方式是设置 SO_REUSEADDR,这一点在第 3 章的代码里已经体现。但还有一个容易被忽略的变体:如果测试工具同时开多个 socket 并发测试,多个 socket 尝试 bind 同一个端口时即使设置了 SO_REUSEADDR 也可能会冲突,此时需要用 SO_REUSEPORT。Python 里可以这样写:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if hasattr(socket, "SO_REUSEPORT"): sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)加了之后多个进程可以共同监听一个端口,内核会自动做负载均衡。不过这只是测试场景的解法,生产服务如果不需要多进程监听,不要随便开 SO_REUSEPORT,会让连接分发变得难以追踪。遇到报错时先看ss -tlnp或lsof -i :端口找出占用方,再做对症处理,比盲目加参数更省时间。
6. 进阶技巧:把测试工具接进你的日常脚本与验收流程
6.1 批量端口状态脚本与连续监控
单机测试工具做得再好,也只能解决“点对点”的问题。实际交付时最需要的是一次验证几十台设备的上百个端口。我习惯写一个批量探测脚本,用 Python 的 socket 对清单里每个 IP:端口发起 TCP 连接,并把结果输出成 CSV。核心片段如下:
import csv, socket, concurrent.futures targets = [("192.168.1.10", 80), ("192.168.1.11", 502), ("192.168.1.12", 9000)] def check(item): host, port = item try: with socket.create_connection((host, port), timeout=3): return (host, port, "open") except socket.timeout: return (host, port, "timeout") except ConnectionRefusedError: return (host, port, "refused") with concurrent.futures.ThreadPoolExecutor(max_workers=50) as pool: results = list(pool.map(check, targets)) with open("port_report.csv", "w", newline="") as f: writer = csv.writer(f) writer.writerow(["host", "port", "status"]) writer.writerows(results)逻辑说明:ThreadPoolExecutor 用 50 个并发线程同时探测,单台设备不会拖垮整批扫描。每个探测的超时时间固定 3 秒,如果目标数量很大,建议把它做成配置文件而不是硬编码在代码里。
6.2 连续监控看抖动
还有一种常见场景是自动化测试过程中网络偶尔闪断,普通测试工具跑一次无法捕捉。我给自研工具加过连续监控模式,每隔 10 秒测一次,共测 60 次,把每次的耗时和状态写入日志。如果某几次耗时是平时的十倍以上,即使没有连接失败,这条链路也已经处于不稳定状态,需要在交付报告里单独标注。这里要注意监控本身不能影响被测系统,间隔别小于 5 秒,否则测试流量本身就给链路上加了负载。
6.3 我保留的两个习惯与一句忠告
做 TCP/UDP 测试工具这两年,我养成了两个习惯。第一个习惯是所有测试命令都留档,包括参数、目标 IP、抓包文件、时间点,不然售后问题来了只能靠回忆,非常被动。第二个习惯是测试工具永远保留“原始输出”,不要让 UI 或报告层过滤掉关键字段,比如 TCP 重传次数、UDP 丢包率、ICMP 错误计数,这些才是判断网络质量的硬指标。如果你也正在做类似的调试方案,希望这几章的思路和代码能帮到你,少踩几个我踩过的坑。
本文还有配套的精品资源,点击获取