Linux:UDP 和 TCP 报头管理详解
在 Linux 网络编程中,理解 TCP 和 UDP 的报头(Header)结构非常重要,因为它们直接决定了数据如何被发送、接收、校验、排序、拥塞控制等。Linux 内核网络栈(net/ipv4/tcp_input.c、udp.c 等)会解析这些字段,而用户态程序(socket 编程)也需要正确处理它们。
下面分别详细说明两种协议的报头结构、各字段含义、字节序、常用场景以及Linux 中的管理方式。
1. UDP 报头(User Datagram Protocol)
UDP 是无连接、不可靠的传输层协议,报头非常精简,只有 8 字节(固定长度,无选项字段)。
UDP 报头结构(总 8 字节,网络字节序 Big-Endian):
| 偏移(字节) | 字段名 | 长度(bit) | 说明 |
|---|---|---|---|
| 0–1 | Source Port | 16 | 发送方端口号(0 表示不关心回复) |
| 2–3 | Destination Port | 16 | 接收方端口号 |
| 4–5 | Length | 16 | UDP 数据报总长度(报头 8 字节 + 数据长度),最小 8,最大 65535 |
| 6–7 | Checksum | 16 | 校验和(覆盖伪首部 + UDP 报头 + 数据),可为 0(不校验) |
关键特点:
- 固定 8 字节,无选项字段
- Length 字段包含报头 + 数据的总长度(单位:字节)
- Checksum 是可选的(很多应用设为 0 以提高性能,但 IPv6 下必须计算)
- 伪首部(Pseudo-header):校验和计算时会包含 IP 层源/目的 IP、协议号(17)、UDP 长度(不包含在 UDP 报头中)
Linux 中的典型管理:
recvfrom()/sendto()时,内核自动填充/解析端口和长度- 校验和由内核网络栈计算(可通过
setsockopt(SO_NO_CHECK)关闭) - 常见应用:DNS、DHCP、RTP、QUIC(部分)、游戏、视频流
2. TCP 报头(Transmission Control Protocol)
TCP 是面向连接、可靠的协议,报头至少 20 字节,最多 60 字节(带选项时)。
TCP 报头结构(网络字节序 Big-Endian):
| 偏移(字节) | 字段名 | 长度(bit) | 说明 |
|---|---|---|---|
| 0–1 | Source Port | 16 | 发送方端口号 |
| 2–3 | Destination Port | 16 | 接收方端口号 |
| 4–7 | Sequence Number | 32 | 当前段第一个字节的序号(SYN 段为初始序号 ISN) |
| 8–11 | Acknowledgment Number | 32 | 期望收到的下一个字节序号(ACK 标志置位时有效) |
| 12–13 | Data Offset + Reserved + Flags | 16 | 前 4 bit:Data Offset(报头长度,单位 32-bit 字,最小 5=20 字节) 后 12 bit:保留 + 标志位 |
| 14–15 | Window Size | 16 | 接收窗口大小(字节),用于流量控制 |
| 16–17 | Checksum | 16 | 校验和(伪首部 + TCP 报头 + 数据) |
| 18–19 | Urgent Pointer | 16 | 紧急指针(URG 标志置位时有效,表示紧急数据的结束偏移) |
| 20+ | Options(可选) | 0–40 字节 | 常见:MSS、Window Scale、SACK、Timestamp、Selective ACK 等 |
TCP 标志位(Flags)(12 bit 中的重要 6 位):
- URG:紧急指针有效
- ACK:确认号有效
- PSH:接收方尽快交付数据(push)
- RST:重置连接(异常终止)
- SYN:同步序号(建立连接)
- FIN:结束连接
常见 TCP Options(Options 字段):
| Kind | 长度 | 名称 | 作用 |
|---|---|---|---|
| 0 | — | End of Options | 选项结束 |
| 1 | 1 | NOP | 填充字节 |
| 2 | 4 | MSS | 最大段大小(通常 1460) |
| 3 | 3 | Window Scale | 窗口扩大因子(移位) |
| 4 | 2 | SACK Permitted | 允许选择性确认 |
| 5 | var | SACK | 选择性确认块 |
| 8 | 10 | Timestamp | 时间戳(RTT 计算、PAWS 保护) |
Linux 中的典型管理:
- 内核自动维护 Sequence/Acknowledgment、Window、Flags、Checksum
- 用户程序通过
connect()、accept()、send()、recv()间接使用 - 可通过
setsockopt()控制: TCP_NODELAY(禁用 Nagle 算法)TCP_MAXSEG(设置 MSS)TCP_INFO(获取连接统计,如 RTT、拥塞窗口)TCP_USER_TIMEOUT等- 抓包工具(tcpdump、Wireshark)最常看到这些字段
3. UDP vs TCP 报头对比(快速记忆)
| 特性 | UDP | TCP |
|---|---|---|
| 报头长度 | 固定 8 字节 | 20–60 字节 |
| 连接性 | 无连接 | 面向连接 |
| 序号字段 | 无 | 有(32 bit Sequence + Acknowledgment) |
| 确认机制 | 无 | 有(ACK 号 + 窗口) |
| 流量控制 | 无 | 有(滑动窗口) |
| 拥塞控制 | 无 | 有(Reno、Cubic、BBR 等) |
| 校验和 | 可选(可为 0) | 强制(但可以伪造) |
| 选项字段 | 无 | 有(MSS、SACK、Timestamp 等) |
| 可靠性 | 应用层负责 | 内核保证 |
| Linux 典型使用 | DNS、DHCP、RTP、QUIC 数据报 | HTTP/HTTPS、SSH、FTP、数据库等 |
4. Linux 中查看/调试报头的方式
- 抓包:
tcpdump -i eth0 -nnvv -s0 port 53 # 看 DNS(UDP)
tcpdump -i any -nnvv 'tcp port 80' # 看 HTTP(TCP)
- Wireshark:最直观,可直接看到每个字段的解析
- ss / netstat(查看连接状态):
ss -tunlp # 查看 TCP/UDP 监听和连接
- strace:跟踪用户程序的 socket 调用
strace -e network curl example.com
- 内核源码:感兴趣可看
net/ipv4/tcp_input.c、udp_recvmsg()等
总结一句话:
- UDP 报头极简(8 字节),只管端口 + 长度 + 校验,适合低延迟、高吞吐、无状态场景。
- TCP 报头复杂(20+ 字节),承载了序号、确认、窗口、标志、选项,是 Linux 网络编程中最复杂的部分之一。
如果你想深入某个部分(比如 TCP 三次握手/四次挥手中的报文变化、Linux 如何实现滑动窗口、如何用 raw socket 自己构造报文等),告诉我,可以继续展开代码示例或抓包分析。