3. UDP 报文结构分析:头部格式与数据载荷

UDP 的报文结构,说白了就是「轻量、快速、没负担」。我刚开始学网络时,总觉得 TCP 才是正经协议,UDP 像个野孩子。后来在项目中抓过几次实时音视频流,才真正体会到——有些场景,UDP 才是王道。

这一节,我们就把 UDP 报文拆开来看。从头部那 8 个字节,到后面的数据载荷,一个细节都不放过。

3.1 UDP 头部格式:就 8 个字节,但个个关键

UDP 头部固定 8 字节。你想想看,TCP 头部最少 20 字节,UDP 只有它的一半不到。这就是 UDP 快的原因之一——开销小。

头部由四个字段组成,每个字段 2 字节:

字段 长度 说明
源端口 16 bit (2 字节) 发送方的端口号,可选(可为 0)
目的端口 16 bit (2 字节) 接收方的端口号,必填
长度 16 bit (2 字节) UDP 头部 + 数据的总长度(字节)
校验和 16 bit (2 字节) 覆盖头部、数据及伪头部,可选(IPv4 下可为 0)

嗯,这里要注意——源端口在某些场景下是可以不填的。比如 DNS 客户端发送查询时,源端口通常设为一个临时端口,但如果你只是单向发送数据,不关心回复,源端口可以置 0。我在一次物联网项目中就见过这种用法,设备只上报数据,不接收任何回包。

3.2 逐个字段拆解

3.2.1 源端口

发送方的端口号。客户端一般用临时端口(1024 以上),服务器用知名端口(如 DNS 用 53)。

我个人习惯在抓包时先看源端口,能快速判断流量方向。比如源端口是 123,那大概率是 NTP 时间同步。

3.2.2 目的端口

接收方的端口号。这个字段是必须的,没有它,数据到了对端都不知道该交给哪个应用。

我曾经排查过一个视频卡顿的问题,抓包发现目的端口写错了,数据发到了 8080 而不是 554(RTSP 默认端口)。嗯,这种低级错误,配置检查一遍就能避免。

3.2.3 长度

这个字段表示整个 UDP 报文(头部 + 数据)的字节数。最小是 8(只有头部,没有数据),最大是 65535。

你想想看,如果长度字段和实际数据对不上,Wireshark 会直接标记为「Malformed Packet」。我在一次压力测试中见过这种情况——某个嵌入式设备发的 UDP 包长度字段写死了 1500,但实际数据只有 100 字节,结果对端直接丢弃。

3.2.4 校验和

校验和覆盖三个部分:伪头部、UDP 头部、UDP 数据。伪头部是从 IP 头部中提取的源 IP、目的 IP、协议类型和 UDP 长度。

为什么需要伪头部?说白了,就是为了检测数据是否被错误地路由到了别的机器。如果 IP 地址变了,校验和就会失败。

在 IPv4 下,校验和是可选的(可以全 0)。但 IPv6 下,校验和是强制的。我建议你在生产环境中永远不要关闭校验和,否则数据静默损坏了你都不知道。

核心要点:UDP 头部只有 8 字节,但校验和的计算涉及 IP 伪头部。这意味着 UDP 的完整性校验是跨层的,不是只看自己这一层。

3.3 UDP 数据载荷分析

头部之后就是数据载荷。UDP 本身不关心载荷里是什么——可能是 DNS 查询、RTP 音视频流、SNMP 管理消息,或者就是你自定义的二进制协议。

在 Wireshark 中,数据载荷会以十六进制和 ASCII 两种形式展示。我个人习惯先看 ASCII 部分,如果能看到可读的字符串(比如 HTTP 的 GET、DNS 的域名),就能快速判断上层协议。

举个例子,一个典型的 DNS 查询报文:

UDP 头部(8 字节)
  源端口: 54321
  目的端口: 53
  长度: 35
  校验和: 0x1234

UDP 载荷(27 字节)
  00 01 01 00 00 01 00 00 00 00 00 00
  03 77 77 77 06 67 6f 6f 67 6c 65 03
  63 6f 6d 00 00 01 00 01

看到没?载荷里的 03 77 77 77 就是 www,06 67 6f 6f 67 6c 65 就是 google。Wireshark 会自动解析这些,但如果你手动看原始字节,也能看出规律。

我的经验:在 Wireshark 中右键点击数据载荷区域,选择「Show Packet Bytes…」,可以单独查看载荷的十六进制和 ASCII 对照。调试自定义协议时,这个功能非常实用。

3.4 UDP 报文结构全景图

下面这张图,把 UDP 报文从 IP 层到应用层的关系画清楚了。我建议你多看几遍,尤其是伪头部的位置——很多人会忽略它。

UDP 报文结构全景图 应用层数据(载荷) UDP 头部(8 字节) 源端口(2B) | 目的端口(2B) | 长度(2B) | 校验和(2B) 伪头部(仅用于校验和计算,不传输) 源 IP(4B) | 目的 IP(4B) | 协议(1B) | UDP 长度(2B) IP 头部(20 字节,标准 IPv4) 说明: • 伪头部不是真实传输的,只在计算校验和时临时构造 • 校验和覆盖范围:伪头部 + UDP 头部 + UDP 数据 • IPv4 下校验和可选(全 0 表示禁用),IPv6 下强制 • 长度字段 = UDP 头部(8 字节)+ 数据载荷

3.5 实战:在 Wireshark 中定位 UDP 头部

抓到一个 UDP 包后,怎么快速找到头部字段?我一般这么做:

  1. 在包列表区选中一个 UDP 包
  2. 在中间协议树区展开 User Datagram Protocol 那一行
  3. 直接看四个字段的值:Source Port、Destination Port、Length、Checksum
  4. 最下面的 Data 就是载荷,可以右键导出原始字节

举个例子,你打开一个 DNS 响应包,会看到:

User Datagram Protocol, Src Port: 53, Dst Port: 54321
    Source Port: 53
    Destination Port: 54321
    Length: 123
    Checksum: 0xabcd [unverified]
    [Checksum Status: Unverified]
    Data (115 bytes)

看到 [Checksum Status: Unverified] 了吗?Wireshark 默认不校验 UDP 校验和,因为计算量比较大。你可以右键点击该字段,选择「Validate UDP checksum if possible」来手动触发校验。

注意:如果 Wireshark 显示校验和错误(红色标记),不要急着认为是网络问题。很多网卡开启了硬件校验和卸载(Checksum Offloading),会在发送时重新计算校验和,导致抓到的包校验和看起来是错的。这是正常现象,不是丢包。

3.6 常见问题与避坑

  • 长度字段和实际数据不一致:我遇到过嵌入式设备在计算长度时忘了加头部自己的 8 字节,导致对端解析失败。抓包时一眼就能看出来——长度字段比实际载荷小 8。
  • 校验和全 0:IPv4 下允许,但建议不要这么做。我曾经在一个金融项目中关闭了校验和,结果某个路由器误改了数据,导致交易金额出错。从那以后,我再也不敢在生产环境关校验和了。
  • 源端口为 0:常见于某些简单设备或攻击流量。如果看到大量源端口为 0 的 UDP 包,要警惕是不是 UDP Flood 攻击。

好了,UDP 报文结构就这些。8 字节的头部,四个字段,加上载荷,就这么简单。但简单不代表不重要——你想想看,整个互联网的 DNS、NTP、音视频通话,都跑在 UDP 上。把头部结构吃透,后面分析具体协议时才能游刃有余。