
简介TCPUDPDebug 是一款面向网络编程开发者与运维调试人员的传输层协议测试工具用于在开发 TCP 服务器、客户端或 UDP 服务时验证连接性能、排查通信异常。资源包共 18 个文件约 1.15MB以 exe 可执行程序与 dll 动态库为核心辅以 ini、xml 配置与语言文件、htm 说明页、css 样式、bat 批处理脚本及 jpg 截图等结构紧凑、开箱即用。工具覆盖连接建立与断开测试、数据收发验证、丢包检测、顺序校验、错误分析、吞吐量与延迟监控、端口扫描以及多线程并发压力测试等场景可帮助开发者定位网络环境、服务器配置或代码实现层面的瓶颈。目前已有 1278 人学习下载适合需要深入理解 TCP 与 UDP 特性、优化通信效率并提升网络程序稳定性的中高级开发者参考使用。1. 一个调试工具该有的样子从 TCP/UDPDebug 说起手头有个 TCP/UDPDebug 工具界面朴素功能就两块TCP 客户端/服务端、UDP 收发。很多人第一次见会觉得这东西太简单不如 Wireshark 抓包直观不如 iperf3 打流专业。但真到了现场——设备网口刚焊好、协议栈还没跑通、上位机连不上、PLC 不响应——你会发现最顺手的往往就是这种能手动发一帧、手动收一帧的小工具。它解决的不是吞吐量问题而是「链路到底通没通、我发的字节对方收到没有、对方回的字节我解析对不对」这三个最原始的问题。适合嵌入式网络调试、工控协议对接、上位机联调的人也适合刚学 socket 编程想找个参照物的新手。下面按「先搞懂它在测什么 → 自己怎么搭一个 → 参数怎么调 → 坑在哪」的顺序讲透。2. TCP/UDPDebug 到底在测什么协议行为与工具边界2.1 TCP 模式连接状态比数据本身更重要TCP 调试工具的核心价值不在「发字符串」而在让你亲眼看到连接状态的变化。三次握手能不能完成、四次挥手谁先发 FIN、RST 是谁抛出来的这些在代码里是 errno在工具里就是一行状态提示。常见做法是工具同时提供客户端和服务端两种角色客户端模式填目标 IP 和端口去 connect服务端模式 bind 本地端口等 accept。调试时先让工具当服务端设备当客户端连过来能连上说明设备侧 socket 创建、目标地址、路由都没问题连不上再换工具当客户端去连设备逐步缩小范围。这里有个容易被忽略的点TCP 是字节流没有消息边界。工具界面上你发「AA BB CC」对方 recv 到的可能是「AA」和「BB CC」两次也可能一次全收到。所以调试私有协议时工具必须支持十六进制发送和接收显示并且要能看清每次 recv 的实际分片。我一般会把接收区设成带时间戳和长度的十六进制视图这样对方粘包、拆包一眼就能看出来。2.2 UDP 模式无连接不等于无状态UDP 调试看起来更简单填目标地址端口直接 sendto 就行。但正因为无连接很多问题反而更隐蔽。工具当服务端时bind 之后不需要 listen 和 accept直接 recvfrom 就能收。调试时要注意如果工具先 sendto 再 recvfrom某些系统上会自动绑定一个随机本地端口对方回包到这个端口才能收到如果对方回包目标端口和你发送源端口不一致就会收不到。常见做法是工具显式 bind 一个固定本地端口再收发避免随机端口带来的「发得出去收不回来」。UDP 还有一个典型场景是广播和组播。广播要设 SO_BROADCAST组播要 join 组播组。工具如果不支持这些选项调试设备发现协议时就会卡住。另外 UDP 的 recvfrom 要拿到对方地址工具界面上最好把每次收到的源 IP 和源端口显示出来否则多设备同时上报时你分不清是谁发的。2.3 工具边界它不替代抓包但比抓包快TCP/UDPDebug 这类工具和 Wireshark 是互补关系。Wireshark 看的是链路上的真实帧能发现校验和错误、重传、窗口为零这些底层问题调试工具看的是应用层收发能快速验证「我发这个字节序列对方回什么」。现场没有镜像口、不能装驱动的时候调试工具就是唯一手段。它的边界也很清楚不能看协议栈内部状态不能统计吞吐和时延分布不能做压力测试。所以定位是「连通性验证和协议交互验证」不是性能测试工具。3. 自己搭一个最小可用的 TCP/UDP 调试工具3.1 环境准备与依赖选择用 Python 写最省事标准库 socket 就够不需要额外安装。界面可以用 tkinter也可以用命令行加参数。我倾向于先写命令行版本逻辑清晰方便嵌入脚本需要给不写代码的同事用时再套一层 tkinter。下面以 Python 3 为例Windows 和 Linux 都能跑。注意 Windows 防火墙第一次会弹窗要允许专用网络访问否则工具当服务端时外部连不进来。3.2 TCP 服务端最小实现import socket import threading def tcp_server(host0.0.0.0, port9000): srv socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 避免重启时地址被占用 srv.bind((host, port)) srv.listen(5) print(f[TCP] listen on {host}:{port}) while True: conn, addr srv.accept() print(f[TCP] connect from {addr}) threading.Thread(targethandle_client, args(conn, addr), daemonTrue).start() def handle_client(conn, addr): try: while True: data conn.recv(4096) if not data: print(f[TCP] {addr} closed) break print(f[TCP] recv {len(data)} bytes: {data.hex( )}) conn.sendall(data) # 回显方便验证双向通路 except ConnectionResetError: print(f[TCP] {addr} reset by peer) finally: conn.close() if __name__ __main__: tcp_server()逻辑说明SO_REUSEADDR 是关键调试时频繁重启服务端不加这个会报「Address already in use」。accept 之后每个连接开一个线程处理避免一个客户端卡住影响其他连接。recv 返回空 bytes 表示对端正常关闭抛 ConnectionResetError 表示对端发 RST这两种情况要分开打印现场排查时能直接看出是正常断开还是异常复位。sendall 回显是为了让客户端发什么收什么验证双向通路。参数说明host 填 0.0.0.0 表示监听所有网卡调试本机可以用 127.0.0.1。port 选 9000 以上避免和系统服务冲突。recv 的 4096 是缓冲区大小调试小包够用如果要收大包可以调到 65535。3.3 TCP 客户端最小实现import socket def tcp_client(host, port, payload_hex): cli socket.socket(socket.AF_INET, socket.SOCK_STREAM) cli.settimeout(3) # 连接超时避免卡死 try: cli.connect((host, port)) print(f[TCP] connected to {host}:{port}) payload bytes.fromhex(payload_hex.replace( , )) cli.sendall(payload) print(f[TCP] sent {len(payload)} bytes) resp cli.recv(4096) print(f[TCP] recv {len(resp)} bytes: {resp.hex( )}) except socket.timeout: print([TCP] timeout) except ConnectionRefusedError: print([TCP] connection refused, check port and firewall) finally: cli.close() if __name__ __main__: tcp_client(192.168.1.100, 9000, 01 03 00 00 00 0A)逻辑说明settimeout 必须设否则 connect 到不存在的主机会卡很久。payload 用十六进制字符串传入方便调试 Modbus 这类二进制协议。ConnectionRefusedError 说明目标端口没开或被防火墙拒绝和 timeout 是两种不同故障要分别提示。参数说明host 填目标设备 IPport 填目标端口。payload_hex 里空格可有可无代码里会去掉。recv 只收一次调试时如果对方分多次回需要循环收。3.4 UDP 收发最小实现import socket def udp_server(bind_port9001): srv socket.socket(socket.AF_INET, socket.SOCK_DGRAM) srv.bind((0.0.0.0, bind_port)) print(f[UDP] bind on {bind_port}) while True: data, addr srv.recvfrom(4096) print(f[UDP] recv from {addr}: {data.hex( )}) srv.sendto(data, addr) # 回显 def udp_client(host, port, payload_hex): cli socket.socket(socket.AF_INET, socket.SOCK_DGRAM) cli.bind((0.0.0.0, 0)) # 显式绑定随机本地端口确保能收回复 payload bytes.fromhex(payload_hex.replace( , )) cli.sendto(payload, (host, port)) print(f[UDP] sent {len(payload)} bytes to {host}:{port}) cli.settimeout(3) try: resp, addr cli.recvfrom(4096) print(f[UDP] recv from {addr}: {resp.hex( )}) except socket.timeout: print([UDP] no response) finally: cli.close()逻辑说明UDP 服务端不需要 listen 和 acceptbind 之后直接 recvfrom。客户端 bind 到 0 端口让系统分配一个本地端口这样 recvfrom 才能收到对方回包。如果对方回包目标端口和你发送源端口不一致这里就收不到需要检查对方配置。参数说明bind_port 是本地监听端口客户端 host 和 port 是目标地址。UDP 没有连接状态sendto 成功不代表对方收到所以必须靠 recvfrom 超时来判断。3.5 十六进制与 ASCII 双视图调试二进制协议时接收区只显示 ASCII 会看到一堆乱码。我一般会在工具里同时显示 hex 和 ASCIIhex 用空格分隔ASCII 不可打印字符用点代替。发送区支持两种输入模式ASCII 模式直接发字符串hex 模式把「01 03 00 00」转成 bytes。切换用单选按钮避免手动转码出错。这个功能不复杂但能省很多事。4. 参数怎么设端口、超时、缓冲区与协议选项4.1 端口选择的三个原则第一避开 0 到 1023 的知名端口除非你明确要模拟 HTTP 或 Modbus TCP 的标准端口。第二本机调试时客户端和服务端不要用同一个端口否则 bind 会冲突。第三Windows 上 5000 端口可能被系统占用选 9000 以上比较安全。如果必须用标准端口Linux 下需要 root 权限Windows 下需要管理员权限。4.2 超时设置连接超时和接收超时分开连接超时管的是 connect 阶段一般设 3 到 5 秒。接收超时管的是 recv 阶段调试时设 1 到 3 秒太长会等得难受太短会误判。有些工具只有一个超时设置这是不够的。我一般把两个分开界面上两个输入框。UDP 没有连接阶段只需要接收超时。4.3 缓冲区大小与粘包处理TCP recv 的缓冲区设 4096 还是 65535取决于对方单次发送的最大长度。调试 Modbus TCP 这种小包4096 够用。如果对方可能发几 KB 的配置数据设 65535。但要注意recv 返回的长度不代表对方发了一次可能只是内核缓冲区里当前有多少。所以工具界面上要显示每次 recv 的长度和时间戳方便判断是否粘包。UDP 的 recvfrom 缓冲区如果小于数据报长度多余部分会被丢弃所以 UDP 调试时缓冲区要设得比预期最大包大。4.4 TCP_NODELAY 与 SO_REUSEADDRTCP_NODELAY 禁用 Nagle 算法让小包立即发送。调试交互式协议时建议打开否则可能等 200ms 才发出去。SO_REUSEADDR 允许服务端重启时立即绑定同一端口调试时必开。这两个选项在 Python 里分别用 setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 和 setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 设置。4.5 UDP 广播与组播选项广播要设 SO_BROADCAST目标地址填 255.255.255.255 或子网广播地址。组播要设 IP_ADD_MEMBERSHIP加入组播组地址。这两个选项在调试设备发现协议时经常用到工具如果不支持就只能换别的软件。Python 里组播加入用 struct.pack 打包组播地址和本地接口地址稍微麻烦一点但一次写好就不用再动。5. 避坑与排查现场最容易翻车的五个点5.1 现象服务端 bind 报「Address already in use」原因上一次运行的服务端没有完全退出端口还处于 TIME_WAIT 状态。解决设置 SO_REUSEADDR或者等 30 秒再启动。Windows 上可以用 netstat -ano | findstr 9000 找到占用进程Linux 用 ss -tlnp | grep 9000。5.2 现象TCP 客户端 connect 超时但 ping 得通原因目标端口没开或者中间有防火墙拦截了 TCP 握手。ping 走的是 ICMP和 TCP 是两套东西。解决先用 telnet 目标IP 端口 测一下如果 telnet 也不通检查目标服务是否启动、防火墙是否放行。Windows 防火墙默认阻止入站连接要在高级设置里加规则。5.3 现象UDP 发得出去收不回来原因客户端没有 bind 固定本地端口或者对方回包目标端口不对。解决客户端显式 bind 一个本地端口再 sendto抓包确认对方回包的源 IP 和源端口。如果对方回包到随机端口说明对方配置有问题需要对方改。5.4 现象TCP 接收区显示的数据和发送的不一致原因TCP 是字节流可能粘包或拆包。你发两次对方一次 recv 全收到或者你发一次对方分两次 recv。解决工具接收区显示每次 recv 的实际内容不要假设一次 recv 对应一次 send。协议设计时要在应用层加长度字段或分隔符。5.5 现象工具当服务端时外部连不进来本机可以原因绑定了 127.0.0.1 而不是 0.0.0.0。解决bind 的 host 改成 0.0.0.0监听所有网卡。另外检查 Windows 防火墙是否允许该程序入站第一次运行时会弹窗如果点了「取消」就要手动去防火墙里加。6. 进阶技巧用脚本批量验证与自动化回归手动点按钮适合单次调试但设备固件更新后需要回归验证时手动就太慢了。我一般会把上面的 TCP/UDP 函数封装成命令行工具用参数控制模式、目标、发送内容然后写一个 shell 或 Python 脚本批量跑用例。比如设备有 10 条 Modbus 寄存器要读就写一个循环每条发不同的十六进制帧检查回复的字节数和 CRC。这样每次固件更新后跑一遍脚本几分钟就能确认没有回归。import subprocess cases [ (192.168.1.100, 502, 00 01 00 00 00 06 01 03 00 00 00 0A), (192.168.1.100, 502, 00 02 00 00 00 06 01 03 00 0A 00 0A), ] for host, port, payload in cases: result subprocess.run( [python, tcp_client.py, host, str(port), payload], capture_outputTrue, textTrue, timeout5 ) print(fcase {payload[:20]}... - {result.stdout.strip()})逻辑说明每个用例独立起一个进程避免连接状态互相影响。capture_output 拿到工具的输出检查是否包含预期回复。timeout 防止某个用例卡死拖垮整个脚本。参数说明cases 列表里每条是目标 IP、端口、十六进制载荷。实际使用时可以把预期回复也写进去用 assert 判断。这个思路同样适用于 UDP把 tcp_client.py 换成 udp_client.py 即可。还有一个技巧是让工具支持从文件读取发送内容每行一帧按间隔逐条发送。调试设备主动上报协议时可以用这个方式模拟设备发数据验证上位机解析逻辑。文件格式用纯文本每行十六进制注释用 # 开头简单够用。最后说个血泪经验调试工具本身也要有日志。每次连接、发送、接收、断开都写一行到文件带时间戳。现场出问题时日志比记忆可靠。我习惯把日志放在工具同目录的 debug.log按天滚动不占地方。这个习惯帮我省过很多次「刚才到底发了什么」的后悔药。希望帮到你。本文还有配套的精品资源点击获取