
简介计算机网络实验三UDP协议探索和分析是一份完整的实验报告资源适合计算机网络课程学生、Linux网络运维人员及协议分析初学者。报告以UDP协议为核心通过搭建虚拟网络拓扑、配置静态路由、关闭网卡offload、使用nc命令建立客户与服务器通信并利用Wireshark抓包分析UDP用户数据报的源端口、目的端口、长度及校验和等字段完整展示了从环境准备到抓包验证的全过程。内容包含实验目的、详细步骤截图、结果记录和问题分析特别是针对校验和的手算方法做了清晰推导能帮助读者深入理解UDP无连接传输的特点和网卡offload机制。资源包仅含1个docx文档大小1.13MB排版清晰、图文并茂可直接作为实验报告模板或复习资料。已有176人学习使用对于正在完成类似网络实验或准备相关考试的同学具有实际参考价值。1. UDP 实验不是简单抓包offload、命名空间和 nc 的组合拳很多人觉得 UDP 协议比 TCP 简单头部才 8 个字节随便抓个包就能看懂。但真正动手做计算机网络实验时你会发现最耗时间的不是读首部字段而是把实验环境搭对虚拟网络拓扑怎么建、网卡 offload 要不要关、nc 命令的参数怎么拼。这份「UDP 协议探索和分析」实验资源就是用 Linux 命名空间模拟两台真实主机在关闭网卡硬件计算的情况下用 nc 完成一次双向 UDP 通信再用 Wireshark 逐字节拆解数据报最后手动验算校验和。适合正在做计算机网络课程实验、想搞懂 UDP 首部字段和校验和计算原理的在校生也适合想把虚拟网络实验流程跑通的一线运维和网络初学者。2. 先搭虚拟网络拓扑netns、veth 与 brctl 组合成跨网段环境2.1 为什么非要用 Linux 命名空间把一台物理机拆成两台“主机”实验的第一个难点不是 UDP而是怎么在没有真实设备的情况下让两个不同网段的主机互相通信。常见做法是用 Linux 网络命名空间netns配合虚拟以太网对veth和 Linux 网桥bridge来实现。命名空间的作用是隔离网络栈每个 netns 有自己的网卡、路由表和防火墙规则相当于一台独立的主机。veth 是一根虚拟网线一头插在 ns56A 里另一头插在网桥上和真实网线没有任何区别。这个方案比直接开两台虚拟机轻量得多资源占用几乎可以忽略不计而且所有操作都能用命令复现实验报告里截图也方便。实验指导书里提到的 script 3.1 就是干这个的常见的做法是写一个 bash 脚本把创建 netns、创建 veth、把 veth 挂到 bridge 上、分配 IP 这些动作一次性做完。2.2 创建拓扑的完整脚本与验证命令下面这个脚本是常见做法和指导书的 script 3.1 做的事一致两个 netnsns56A 和 ns57C、一个网桥 br56、两个 veth 对。脚本要在 root 权限下执行。#!/bin/bash # 创建 bridge brctl addbr br56 ip link set br56 up # 创建 ns56A 和 ns57C 两个命名空间 ip netns add ns56A ip netns add ns57C # 创建 veth 对veth56A 在 ns56A 里veth57C 在 ns57C 里 ip link add veth56A type veth peer name tap56A ip link add veth57C type veth peer name tap57C # 把 veth 的一头塞进命名空间另一头挂到 br56 ip link set veth56A netns ns56A ip link set veth57C netns ns57C brctl addif br56 tap56A brctl addif br56 tap57C # 启动网桥侧接口 ip link set tap56A up ip link set tap57C up # 在命名空间内配置 IP 并启动接口 ip netns exec ns56A ip link set lo up ip netns exec ns56A ip link set veth56A up ip netns exec ns56A ip addr add 192.168.56.126/24 dev veth56A ip netns exec ns57C ip link set lo up ip netns exec ns57C ip link set veth57C up ip netns exec ns57C ip addr add 192.168.57.254/24 dev veth57C脚本的逻辑是先建网桥作为二层交换核心再用 veth 把两台“主机”接到网桥上。注意 tap56A 和 tap57C 是留在默认命名空间里的网桥侧接口而 veth56A 和 veth57C 才是真正进了 ns56A 和 ns57C 的“主机网卡”。IP 地址的分配也很有讲究ns56A 是 192.168.56.126/24ns57C 是 192.168.57.254/24两个网段不同所以后面必须配静态路由才能互通。创建完之后一定要验证拓扑是否真的建起来了用下面三条命令逐一检查ip netns list ip netns exec ns56A ifconfig -a ip netns exec ns57C ifconfig -a brctl showip netns list 输出应该有 ns56A 和 ns57C 两个名字。ifconfig -a 能看到每个命名空间里只有 loopback 和 veth 接口而且 IP 地址正确。brctl show 能看到 br56 下挂了 tap56A 和 tap57C 两个接口这证明二层链路已经通了。如果这里哪一步不对后面所有抓包分析都是白做。2.3 配置静态路由让两个网段跨桥互通两个命名空间在不同网段默认情况下它们互不知道对方的存在发出去的包找不到路由会被直接丢弃。指导书里的 script 3.2 就是配置静态路由的最常见的配置是给每个命名空间加一条默认路由指向网桥上对应接口的 IP。# ns56A 里增加默认路由网关是桥接网络侧接口地址 ip netns exec ns56A ip route add default via 192.168.56.1 dev veth56A # ns57C 里增加默认路由 ip netns exec ns57C ip route add default via 192.168.57.1 dev veth57C这里需要说明一下因为实验环境里没有配置真正的网关这里的默认路由更像是一个占位符让数据包能沿着 veth 送到桥接网络。实际上如果两个命名空间挂在同一个网桥上并且配置了不同网段的 IP真正要通信时往往需要给网桥本身配置对应网段的 IP 作为网关或者使用更复杂的路由规则。但在这个实验里虚拟网桥工作在二层数据包从 ns56A 发出后目的 MAC 对应的主机在同一个广播域内所以路由表的作用是确认“该从哪个接口出去”。配完路由后用ip netns exec ns56A ping 192.168.57.254验证一下能通就说明链路没问题了后面 UDP 通信的排查范围就缩小到端口和协议层面。3. 关掉网卡 offload 再做 UDP 打流ethtool 参数与 nc 的仿真客户端3.1 offload 是什么为什么观看校验和之前必须先关掉它现代网卡有个让实验者头疼的功能叫 offload简单说就是网卡硬件替 CPU 干了本该由操作系统协议栈做的活最常见的是 TCP/UDP 校验和的计算和校验。正常情况下这是性能优化但在抓包实验里它会变成一个严重的干扰源数据包在内存里明明还没算校验和网卡在发送时硬件算好填进去Wireshark 抓到的是网卡算完的结果更麻烦的是接收方向网卡硬件校验通过后驱动可能把校验和字段标记为“已校验”抓包软件看到的值并不是网络上真正传输的值。这个实验要求掌握 UDP 数据格式和首部字段尤其是校验和字段。所以实验流程里专门安排了关闭网卡 offload 这一步把运输层封装时需要的计算还给 CPU让操作系统协议栈老老实实算出校验和再交给网卡发送。指导书里的 script 3.3 用的就是 ethtool 工具下面给出常见做法。3.2 关闭 offload 的命令与验证ethtool -K 组合参数# 关闭校验和计算与分段卸载等 offload 功能 ethtool -K tap56A tx off rx off sg off tso off gso off gro off ethtool -K tap57C tx off rx off sg off tso off gso off gro offtx off / rx off关闭发送和接收方向的校验和计算与校验这两个最关键sg off关闭 scatter-gather即关闭分散聚合 IO 能力避免数据包被拆成多个片段tso off / gso off关闭 TCP 分段卸载和通用分段卸载虽然 UDP 用不上但一起关掉更干净gro off关闭通用接收合并防止接收方向把多个小包合并成大包影响抓包分析验证命令是ethtool -k tap56A输出里会看到 tx-checksumming、rx-checksumming 等字段变成了 off。这里有个容易踩的坑主机的物理网卡可能开启了很多 offload 功能但这套实验用的是 veth 虚拟网卡每个 veth 都有自己的 ethtool 设置所以要分别对 tap56A 和 tap57C 执行。有些同学只关了物理网卡结果抓包里的校验和状态照样不对就是这个原因。3.3 nc 做 UDP 服务端和客户端的正确姿势ncnetcat是 Linux 下调试网络最常用的瑞士军刀这个实验用它模拟 UDP 服务端和客户端再合适不过。先在 ns57C 上启动服务端监听ip netns exec ns57C nc -lvu 4499-l监听模式作为服务端等待连接-u使用 UDP 协议不加这个参数默认走 TCP-v详细输出能看到连接建立和收发数据的日志然后在 ns56A 上启动客户端指定服务端的 IP 和端口ip netns exec ns56A nc -u 192.168.57.254 4499这里不写 -l作为客户端主动向 192.168.57.254 的 4499 端口发送数据。一个小细节nc 的 UDP 模式没有真正的“连接”它只是把数据包发过去。所以客户端这边敲了字符回车后如果服务端没反应不要立刻以为失败了——UDP 是单向的服务端没有数据回传时客户端窗口不会显示任何内容。需要双向通信就两边各敲一行字符这个实验就是让 ns56A 发 “hi,57C”ns57C 再发回一行。另外注意实验指导书里是先在 ns57C 的后台启动 Wireshark再启动 nc 服务端和客户端这个先后顺序其实无所谓但抓包一定要在数据收发之前开始否则前面的交互过程就漏掉了。我一般习惯先让 Wireshark 开始抓包再敲字符确保每个包都被记录。4. Wireshark 拆解 UDP 首部从抓包文件读出 43990 与 4499 的完整链路4.1 抓包时机与过滤条件在 ns57C 上启动 Wireshark 并选择 tap57C 接口开始抓包这里 tap57C 是 ns57C 连接物理网桥的接口能同时看到发往和来自 ns57C 的数据包。抓包完成后停止保存为 pcap 文件。由于实验环境里只有 UDP 通信数据包不会太多。但如果在同一台物理机上还跑着其他网络服务抓到的包会很杂这时可以用显示过滤器过滤。Wireshark 的显示过滤表达式写udp只显示 UDP 数据报。更精确一点可以写udp.port 4499只看本次实验涉及的业务端口。如果还想看到 ARP 之类的底层协议交互可以写udp || arp但分析 UDP 首部时不需要。4.2 UDP 首部四个字段的逐项解读UDP 首部固定 8 个字节四个字段各占 2 字节源端口、目的端口、长度、校验和。实验里截获的数据报很典型客户端发给服务端的报文关键值如下字段名值说明源端口43990客户端操作系统临时分配的动态端口目的端口4499服务端监听端口固定不变长度158 字节首部 7 字节数据单位是字节校验和0x78b7覆盖 UDP 首部、数据与伪首部的计算结果这里长度字段值得细说。UDP 的长度字段是整个 UDP 报文的总长度包括 8 字节固定首部和数据部分。实验里客户端发送的数据是 “hi,57C” 加回车换行即 ASCII 码 0x68 0x69 0x2c 0x35 0x37 0x43 0x0a共 7 字节加上首部 8 字节长度就是 15。这是 UDP 和 IP 的一个关键差异IP 层的总长度字段是 IP 包总长UDP 长度字段只管自己这一层不包含 IP 头。服务端回给客户端的报文长度是 18说明服务端返回的数据有 10 字节。如果你在表格里看到长度是 8那就意味着这是个不携带数据的空 UDP 报文很多网络探活工具就是利用这种空报文判断对端端口是否开放。4.3 动态端口的证据43990 是怎么分配出来的实验题目里专门问了一个问题操作系统给客户端分配的端口号是多少属于哪种类型。从抓包里看到源端口是 43990这就是答案。这个端口属于动态端口范围Linux 下通常是 3276860999由操作系统在客户端调用 socket 发送数据时临时从本机未占用的端口中挑选一个。这里有个隐蔽的细节客户端在调用nc -u之前并不知道自己会用哪个端口是 UDP socket 第一次发送数据时内核才从端口范围内取一个空闲端口绑定上去。服务端什么时候能获知这个端口服务端本身不主动查询而是在收到第一个 UDP 报文时从报文首部直接读出源端口 43990之后响应的目的端口就是它。所以你可以看到实验里服务端返回的数据报源端口是 4499、目的端口变成了 43990两个方向的首部正好互补。用 Wireshark 查看时选中任意一个 UDP 报文下方数据包详情树会依次展开 Ethernet、IP、UDP 三层。UDP 层能看到 Source Port、Destination Port、Length、Checksum 四个字段对照表格填入即可。如果想进一步确认数据内容点开 Data 字段能看到十六进制和 ASCII 两种视图实验里应该能看到 68 69 2c 35 37 43 0a 对应的 “hi,57C”。5. 伪首部与校验和手算0x78b7 的避坑指南5.1 伪首部计算法12 字节八元组怎么拼UDP 校验和的计算范围不只是 UDP 首部和数据还有一个位于它们前面的伪首部。伪首部不是真的加在报文里只是计算校验和时临时拼出来的一段 12 字节数据包含源 IP、目的 IP、协议号和 UDP 长度。为什么要包含 IP 地址因为校验和的目的是保证数据在端到端传输中不被篡改如果只校验 UDP 报文本身IP 层的地址错误就发现不了。实验的捕获结果显示 UDP 客户发给服务器的报文最终校验通过接收方用同样的方法验算时会得到 0。伪首部的八元组结构按顺序是源 IP 地址 4 字节、目的 IP 地址 4 字节、全零 1 字节、协议号 1 字节、UDP 长度 2 字节。本次实验的数据是源 IP 为 192.168.56.126目的 IP 为 192.168.57.254协议号 17UDPUDP 长度 15。一个非常容易翻车的点是伪首部里的 UDP 长度必须和 UDP 首部中的长度字段严格一致这里是 15不是 IP 包总长度更不是某些资料上写的 19。如果把这个长度填错整个校验和的验算结果必然对不上。5.2 0x78b7 的完整手算过程把十六进制按 16 位一组排列源 IP 192.168.56.126 分成两段 0xc0a8 和 0x387e目的 IP 192.168.57.254 分成 0xc0a8 和 0x39fe协议号字段是 0x0011UDP 长度是 0x000f源端口 43990 换算成十六进制是 0xabd6目的端口 4499 是 0x1193长度字段 0x000f校验和字段在发送方计算时先置为 0x0000最后是数据部分 0x6869、0x2c35、0x3743以及补零的 0x0a00。把所有 16 位字按二进制反码求和过程如下0xc0a8 0x387e 0xf926 0xf926 0xc0a8 0x1b9ce进位回卷得 0xb9cf 0xb9cf 0x39fe 0xf3cd 0xf3cd 0x0011 0xf3de 0xf3de 0x000f 0xf3ed 0xf3ed 0xabd6 0x19fc3进位回卷得 0x9fc4 0x9fc4 0x1193 0xb157 0xb157 0x000f 0xb166 0xb166 0x6869 0x119cf进位回卷得 0x19d0 0x19d0 0x2c35 0x4605 0x4605 0x3743 0x7d48 0x7d48 0x0a00 0x8748最后对 0x8748 取反码得到 0x78b7这和抓包文件里的校验和完全一致。这个亲手算完再和 Wireshark 对比的过程比看十遍理论都管用。接收方验算时把收到的校验和 0x78b7 一并参与反码求和全部 16 位字加完后如果结果是 0xffff 的反码形式 0x0000就说明校验通过。还有一种说法是结果为 0xffff 也通过这是因为全 0 和全 1 在反码运算中是同一个状态业界约定发送方若算出 0xffff 会存成 0x0000 再发送接收方看到 0x0000 计算结果时理解为通过。5.3 校验和验算的四个典型踩坑记录现象一按资料上的 UDP 长度 19 手算伪首部校验和结果怎么都对不上。原因把 UDP 长度误填成了 IP 总长度或写错的十进制数字。解决从抓包里单独读 UDP 层 Length 字段本实验是 15转十六进制 0x000f 参与计算。现象二Wireshark 里显示该 UDP 报文 checksum 状态是未验证或错误但 nc 两端收发都正常。原因网卡 offload 没有关干净数据包发送时硬件改了校验和Wireshark 在驱动层看到的是原始占位值。解决重新执行 ethtool 关闭 tx/rx 校验和重新抓包确认状态变成 verified。现象三奇数长度数据的最后一个字节无处安放计算时不知道该拿它怎么办。原因UDP 校验和计算要求 16 位对齐数据是 7 字节时最后只剩 1 字节。解决在尾部追加一个全 0 的填充字节参与计算但填充字节不计入 UDP 长度字段也不在网络上传输。现象四手算到最后一步得到 0xffff怀疑自己算错。原因反码求和过程中进位处理不一致。解决所有 16 位字相加每次进位都必须回卷加到最低位最后对总和取反码得到校验和接收方验算时要得到全 0 或全 1 的等价状态。这四个坑里最隐蔽的是第一个。我当年做这个实验时把伪首部长度填成了 IP 层看到的总长度手算三遍都对不上最后对着抓包文件一行一行核对才意识到问题。从那以后我每次验算校验和都会先确认三个值源 IP、目的 IP、UDP 长度一个不对就全盘皆输。6. 把这次实验延伸成 UDP 测量习惯时间戳、丢包率与 iperf3 对比6.1 用 iperf3 验证 UDP 没有拥塞控制做完 nc 通信和校验和验算可以顺手做一个小延伸用iperf3在同一套拓扑上打 UDP 流观察 UDP 和 TCP 在拥塞控制上的差异。iperf3 的 UDP 模式允许你指定带宽比如限 10Mbps 发 30 秒命令如下ip netns exec ns57C iperf3 -s -p 5201 ip netns exec ns56A iperf3 -c 192.168.57.254 -u -p 5201 -b 10M -t 30iperf3 客户端会持续发送 UDP 数据报服务端统计收到的包数和丢失的包数。UDP 不会因为网络拥堵而主动降速发送端只管按设定码率往外丢丢包率会直接暴露网络瓶颈。这个习惯在排查真实网络问题时很实用通过 UDP 打流测量丢包率能快速判断链路质量比反复 ping 更接近真实业务表现。6.2 Wireshark 时间戳看单向时延与抖动刚才抓的包除了看首部字段还能做简单的时延测量。Wireshark 每一行都带精确到微秒的时间戳选中客户端发送的报文记录时间再选中服务端响应的报文记录时间两者的差值就是一次往返时延的近似值。严格来说 UDP 没有 ACK这里计算的是应用层一来一回的时间但这在真实环境里正是用户感知到的响应延迟。想看抖动的话用统计菜单里的 IO Graph以 4499 端口为过滤条件绘制时间序列观察相邻报文的时间间隔。如果间隔波动大说明网络排队或丢包重传导致抖动加剧。这个分析思路对后面学习 TCP 的拥塞控制、对比 UDP 的“无状态”特性特别有帮助。6.3 把 nc 换成自研脚本一个更贴近业务的验证方式最后分享一个我自己的习惯做完 nc 实验后我会用 Python 写一个最小化的 UDP 客户端和服务端把刚才手算校验和验证过的过程自动化。这样既验证了协议正确性又为后续做混合场景测试留了脚本基础。Python 标准库 socket 就能完成不需要装额外依赖。# udp_probe.py —— 发送一条 UDP 消息并等待回包 import socket s socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.settimeout(2) msg bhi,57C s.sendto(msg, (192.168.57.254, 4499)) resp, addr s.recvfrom(1024) print(frecv {len(resp)} bytes from {addr}: {resp}) s.close()这个脚本会复用实验里 nc 相同的逻辑从本机随机端口发送数据到 192.168.57.254 的 4499 端口然后等待回包。通过 socket 的getockname()还能拿到系统分配的临时端口号和 Wireshark 里的源端口对比。从那以后我每次做 UDP 相关的调试都强制走一遍“关 offload、抓包、核对端口、验算校验和”的流程这套动作能挡住大多数网络实验里“数据通了但解释不通”的玄学问题。希望帮到你。本文还有配套的精品资源点击获取