UDP协议实验避坑指南:从抓包到校验和手算

发布时间:2026/10/16 16:33:34
UDP协议实验避坑指南:从抓包到校验和手算 简介这份资源是面向计算机网络课程学习者的实验报告文档聚焦UDP协议探索与分析适合正在完成运输层实验、需要理解UDP数据报格式与Linux网络调试的学生参考。内容围绕UDP首部字段含义、网卡offload技术以及nc命令使用展开通过虚拟网络拓扑搭建、静态路由配置、Wireshark抓包等环节完整记录实验步骤与结果分析。资源包为1个docx文档大小约1.13MB内含实验目的、步骤记录、问题与分析等模块配有抓包截图与首部字段表格便于对照理解源端口、目的端口、长度、校验和等字段的实际取值。文档还包含校验和手动计算过程与接收方校验方法能帮助读者掌握UDP校验机制与动态端口分配逻辑。目前已有177人学习适合作为实验报告模板与协议分析思路的参考材料。1. 从一次“抓不到包”说起UDP 协议探索和分析到底在练什么很多人第一次做 UDP 协议实验都会卡在同一个地方nc命令敲下去对面也回了字符可 Wireshark 里就是看不到那个熟悉的 UDP 报文或者看到了却对不上长度。这不是玄学而是网卡 offload 在背后“帮忙”——它把本该由 CPU 算的校验和、分段工作揽了过去抓包工具抓到的往往是半成品。这份“计算机网络实验三UDP 协议探索和分析”资源核心就是让你用 Linux 网络命名空间搭一套虚拟拓扑关掉 offload再用nc和 Wireshark 把 UDP 首部每个字段、校验和计算过程完整走一遍。它适合正在上计算机网络实验课的学生也适合想补一补运输层底层细节、却一直停留在“知道端口和长度”层面的开发者。下面我按自己复现的顺序把拓扑搭建、静态路由、offload 关闭、抓包分析和校验和手算拆开讲。2. 搭虚拟拓扑与配静态路由把两台“主机”和路由器塞进一个 Linux 里2.1 为什么用 network namespace 而不是开虚拟机实验里反复出现的ns56A、ns57C、tap57C这些名字本质是 Linux network namespace 的缩写。一个 namespace 就是一套独立的网络协议栈有自己的网卡、路由表、ARP 表。用ip netns add创建出来的“主机”彼此之间默认隔离必须靠veth pair和bridge连起来这正好模拟了真实网络里主机—交换机—路由器的关系。相比开两台虚拟机namespace 启动快、资源占用低而且所有操作都能在一条bash命令里完成适合反复重来。实验指导书里提到的script 3.1通常就是一段批量创建 namespace、veth、bridge 并配好 IP 的 bash 脚本。我一般会先把它拆成几段看先建 ns再建 veth再把 veth 一端塞进 ns最后配 IP 和 up。# 创建两个命名空间模拟两台主机 ip netns add ns56A ip netns add ns57C # 创建一对 veth一端留在根命名空间一端放进 ns56A ip link add veth56A type veth peer name veth56A-in ip link set veth56A-in netns ns56A # 给 ns56A 内的接口配 IP 并启用 ip netns exec ns56A ip addr add 192.168.56.126/24 dev veth56A-in ip netns exec ns56A ip link set veth56A-in up ip netns exec ns56A ip link set lo up # 根命名空间这端也启用并挂到网桥上 ip link set veth56A up ip link add br0 type bridge ip link set veth56A master br0 ip link set br0 up这段代码的关键参数是ip netns exec它让后面的命令在指定 namespace 里执行。ip addr add里的/24决定子网掩码实验里192.168.56.126和192.168.57.254分属不同网段所以后面必须配静态路由才能通。lo接口一定要 up否则本机回环都不通nc可能直接报错。执行完可以用ip netns list看 ns 列表用ip netns exec ns56A ifconfig -a看接口用brctl show看网桥挂载情况——这三个命令就是实验步骤 1 里验证拓扑的“三件套”。2.2 静态路由配在哪、下一跳怎么写拓扑通了不代表能跨网段通信。ns56A的地址是192.168.56.126/24它认为192.168.57.0/24是外网必须有一条路由告诉它“去 57 网段找谁”。实验步骤 2 里的script 3.2一般就是给各 namespace 和路由器加静态路由。常见做法是在ns56A里加一条# 在 ns56A 内添加去往 57 网段的静态路由下一跳是路由器接口 ip netns exec ns56A ip route add 192.168.57.0/24 via 192.168.56.1 dev veth56A-in # 在 ns57C 内添加回程路由下一跳是路由器另一侧接口 ip netns exec ns57C ip route add 192.168.56.0/24 via 192.168.57.1 dev veth57C-in这里的via后面是下一跳 IPdev后面是出接口。很多人翻车是因为下一跳地址写成了对端主机的地址而不是路由器接口地址。判断方法很简单ip netns exec ns56A ip route get 192.168.57.254看它选哪条路由、从哪个接口出。如果返回unreachable说明路由没生效或者接口没 up。另外如果实验拓扑里路由器本身也是 namespace那路由器里要开ip_forwardip netns exec router sysctl -w net.ipv4.ip_forward1这个参数默认是 0不开的话路由器只收不发ping 能到路由器但到不了对端。配完路由后先ping通再抓包否则 Wireshark 里只有 ARP 没有 UDP。3. 关掉网卡 offload为什么抓到的 UDP 长度和校验和总对不上3.1 offload 到底替 CPU 干了什么网卡 offload 是一类硬件卸载技术的统称常见的有tx-checksumming、rx-checksumming、tso、gso、gro。以发送方向为例协议栈本来要自己算 UDP 校验和、按 MTU 分片但网卡说“你先把包给我校验和我来算分片我也来”。于是 Wireshark 在协议栈和网卡之间抓包时看到的校验和字段可能是 0或者长度字段和实际不符。实验步骤 3 要求关闭 offload就是把运输层封装时需要的计算还给 CPU让抓包结果和协议栈真实发出的内容一致。查看当前 offload 状态用ethtool -k关闭用ethtool -K。# 查看接口 offload 状态关注 tx-checksumming 和 tso ethtool -k veth56A # 关闭发送校验和卸载、TCP/UDP 分段卸载、通用接收卸载 ethtool -K veth56A tx off rx off tso off gso off gro off # 对 ns57C 侧接口做同样操作 ethtool -K veth57C tx off rx off tso off gso off gro off参数里tx off表示关闭发送校验和卸载rx off关闭接收校验和卸载tso、gso、gro分别对应 TCP 分段、通用分段和接收合并。注意ethtool -K对 veth 接口不一定全部支持如果提示Cannot change ...说明该接口本来就没启用这项不影响实验。关掉之后再用 Wireshark 抓UDP 首部里的校验和字段才会是真实计算值长度字段也才和实际数据一致。这一步不做后面手算校验和会一直对不上属于典型的“后悔药”环节。3.2 在 ns57C 上启动 Wireshark 抓 tap 接口实验步骤 5 要求在ns57C上后台启动 Wireshark并选tap57C接口。这里有个细节Wireshark 是图形程序在 namespace 里直接跑需要 X11 转发或者用sudo -E保留环境变量。常见做法是# 在 ns57C 内后台启动 Wireshark指定接口 tap57C ip netns exec ns57C sudo -E wireshark -i tap57C -k # 如果只想命令行抓包用 tcpdump 更轻量 ip netns exec ns57C tcpdump -i tap57C -w udp.pcap udp-i指定接口-k表示启动后立即开始抓包-w把结果写文件。如果 Wireshark 起不来先确认tap57C是否存在、是否 up再确认当前用户有没有权限。抓包过滤器写udp可以过滤掉 ARP 和 ICMP避免干扰。实验里nc -lvu 4499的-l是监听-v是详细输出-u是 UDP客户端nc -u 192.168.57.254 4499的-u同样指定 UDP。两边各发一行字符后Wireshark 里应该正好看到两个 UDP 报文一个从43990发往4499一个反向。4. 拆 UDP 首部与手算校验和把 0x78b7 一步步推出来4.1 首部八个字节里每个字段怎么读UDP 首部固定 8 字节四个字段各 2 字节源端口、目的端口、长度、校验和。实验里客户端发出的报文源端口43990是操作系统临时分配的动态端口目的端口4499是服务端监听端口长度字段是首部加数据的总字节数数据是hi,57C\n共 7 字节加上 8 字节首部就是 15所以长度字段是0x000F。服务端回发的数据是另一行字符长度 18 对应 10 字节数据加 8 字节首部。校验和字段在关闭 offload 后是真实值客户端报文里是0x78b7。这里容易混淆的是“长度”到底算不算首部——UDP 长度字段包含首部这点和 IP 首部里的“总长度”含义一致但和 TCP 的“首部长度”不同。4.2 校验和计算伪首部、反码求和、奇数填充UDP 校验和不是只算 UDP 报文本身还要加一个 12 字节的伪首部源 IP、目的 IP、保留字节、协议号、UDP 长度。实验问题 4 里给出的源 IP192.168.56.126、目的 IP192.168.57.254、协议号17、UDP 长度19注意这里的 19 是伪首部里写的 UDP 长度而实际 UDP 报文长度字段是 15两者不一致是因为伪首部里的长度按实际 UDP 长度填但实验记录里可能把填充后的长度也算进去了。手算步骤是把伪首部、UDP 首部、数据部分全部按 16 位对齐奇数长度末尾补一个全 0 字节然后对所有 16 位字做反码求和最后取反。接收方验证时把收到的校验和也加进去如果结果是0xFFFF或全 1就认为通过。# 手动计算 UDP 校验和的简化示例 def checksum(data): if len(data) % 2: data b\x00 # 奇数长度补零 s 0 for i in range(0, len(data), 2): word (data[i] 8) data[i1] s word s (s 0xFFFF) (s 16) # 回卷进位 return (~s) 0xFFFF # 伪首部源IP、目的IP、0、协议号17、UDP长度 pseudo bytes([192,168,56,126, 192,168,57,254, 0, 17, 0, 15]) udp_header bytes([0xAB,0xD6, 0x11,0x93, 0x00,0x0F, 0x00,0x00]) payload bhi,57C\n print(hex(checksum(pseudo udp_header payload)))这段代码里0xABD6是 43990 的十六进制0x1193是 4499 的十六进制。s (s 0xFFFF) (s 16)是反码求和的关键把超过 16 位的进位加回低 16 位。最后~s 0xFFFF取反得到校验和。实际算出来应该接近0x78b7如果差一位检查伪首部里的长度字段是否用了实际 UDP 长度、数据部分是否包含了换行符。接收方验证时把0x78b7也加进去结果应为0xFFFF。5. 避坑与排查UDP 实验里最容易翻车的五件事5.1 现象nc -lvu 4499启动后客户端连不上原因通常是服务端 namespace 里lo没 up或者nc版本不支持-u和-l组合。有些发行版的nc是 netcat-openbsd-l和-u可以一起用但如果是 nmap-ncat需要写成ncat -l -u 4499。解决方法是先ip netns exec ns57C ip link set lo up再确认nc -h里有没有-u。如果还不行换socatsocat UDP4-LISTEN:4499,fork -。5.2 现象Wireshark 里看不到 UDP只有 ARP原因是路由没配通或者抓包接口选错了。先ping对端 IP如果 ping 不通说明静态路由或ip_forward有问题。如果 ping 通但抓不到 UDP检查 Wireshark 是不是抓在tap57C而不是veth57C。tap接口通常是 namespace 内的虚拟接口veth是成对出现的另一端抓错接口就看不到应用层数据。用ip netns exec ns57C ip addr确认接口名。5.3 现象UDP 长度字段和实际数据对不上原因是 offload 没关干净或者抓包点选在了网卡驱动之后。ethtool -K关闭后最好重启一下nc进程再抓。另外如果数据里包含中文或特殊字符字节数会变长度字段要按实际字节数算不是字符数。实验里hi,57C\n是 7 字节如果写成hi,57C不带换行就是 6 字节长度字段相应变成0x000E。5.4 现象手算校验和结果和抓包值差很多先检查伪首部里的协议号是不是17UDP 长度是不是按实际 UDP 报文长度填。再检查数据部分是否包含了填充字节——奇数长度时补的0x00要参与计算但不会出现在实际报文里。最后检查字节序网络字节序是大端struct.pack(!H, port)才是对的直接写port.to_bytes(2, little)会算错。5.5 现象ip netns exec报 “No such file or directory”原因是 namespace 没创建成功或者名字拼错。ip netns list看列表ip netns identify看当前进程属于哪个 ns。如果之前创建过同名 ns 但没清理先ip netns del ns56A再重建。清理顺序是先删 veth 再删 ns否则 veth 会残留在根命名空间里下次创建同名接口会冲突。6. 进阶验证用 tcpdump 和 Python 把校验和闭环跑一遍实验报告里填完表 3.1 和表 3.2 不算完真正把 UDP 吃透的做法是自己构造一个 UDP 报文用 Python 算好校验和再用tcpdump抓下来对比。我一般会写一个最小发送端绑定43990发往192.168.57.254:4499数据用hi,57C\n然后看抓包里的校验和是不是0x78b7。如果一致说明伪首部和反码求和的逻辑完全正确如果不一致就逐字段打印中间结果定位是哪个 16 位字加错了。import socket # 构造 UDP 报文并发送观察抓包校验和 src_ip 192.168.56.126 dst_ip 192.168.57.254 src_port 43990 dst_port 4499 payload bhi,57C\n # 伪首部 UDP 首部 数据校验和先填 0 pseudo socket.inet_aton(src_ip) socket.inet_aton(dst_ip) b\x00\x11 (8 len(payload)).to_bytes(2, big) udp src_port.to_bytes(2, big) dst_port.to_bytes(2, big) (8 len(payload)).to_bytes(2, big) b\x00\x00 ck checksum(pseudo udp payload) udp udp[:6] ck.to_bytes(2, big) s socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind((src_ip, src_port)) s.sendto(udp[8:] payload, (dst_ip, dst_port))这段代码里socket.inet_aton把 IP 转成 4 字节b\x00\x11是保留字节加协议号 17。udp[:6]保留前 6 字节后 2 字节替换成算好的校验和。发送时sendto只传 UDP 数据部分首部由内核补但校验和我们已经算好内核如果发现校验和字段非 0 且 offload 已关会直接使用。抓包后对比0x78b7如果一致整个 UDP 首部、伪首部、反码求和、奇数填充的链路就闭环了。从那以后我每次做传输层实验都会先ethtool -K关 offload再tcpdump抓一份原始包最后用 Python 复算校验和——这三步走完UDP 首部里每个字段才真正算自己的而不是网卡替自己算的。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询