TCP 报文段首部解析
TCP 报文段结构
TCP 把应用数据切成一块块**报文段(Segment)**发送。每个报文段 = TCP 首部 + 数据载荷。
+------------------+------------------+
| TCP 首部 | 数据 Payload |
| 20~60 字节 | 可变长度 |
+------------------+------------------+
首部固定 20 字节,选项最多 40 字节,所以首部最大 60 字节。这种"固定+可变"的设计兼顾了处理效率与扩展性。
TCP 首部 20 字节逐字段解析
把 20 字节展开成 5 行 × 32 位(每行 4 字节),这是 RFC 793 的标准画法:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1. 源端口 & 目的端口(各 16 位)
标识发送方和接收方的应用进程。例如 HTTP 服务器监听 80 端口,浏览器从随机端口(如 54321)发起连接。
2. 序列号 Sequence Number(32 位)
标识本报文段发送数据的第一个字节在整个字节流中的位置。TCP 把数据视为无结构的字节流,每个字节都有隐式编号。
初始序列号(ISN)不是从 0 开始!RFC 793 建议每 4 微秒增量 1,现代系统采用更复杂的随机化策略,防止序列号预测攻击。
3. 确认号 Acknowledgment Number(32 位)
累积确认:值为"期望收到的下一个字节的序号",隐含表示该序号之前的所有数据均已正确接收。
例如收到字节 0~999,回复 Ack = 1000,表示"1000 之前的都收到了,请从 1000 开始发"。
4. 数据偏移 Data Offset(4 位)
以 4 字节为单位指示首部长度。最小值 5(5×4=20 字节,无选项),最大值 15(15×4=60 字节,选项满)。
5. 控制标志 Flags(6 位)
| 标志 | 名称 | 作用 | 关键场景 |
|---|---|---|---|
| URG | 紧急 | Urgent Pointer 有效 | 带外数据(极少使用) |
| ACK | 确认 | Acknowledgment Number 有效 | 除首个 SYN 外,所有报文都应置位 |
| PSH | 推送 | 立即交付应用层 | 交互式应用(如 Telnet) |
| RST | 重置 | 异常关闭连接 | 端口未监听、连接异常 |
| SYN | 同步 | 建立连接时同步 ISN | 三次握手第一步 |
| FIN | 结束 | 释放连接 | 四次挥手第一步 |
SYN 和 FIN 各消耗一个序列号!这是理解三次握手和四次挥手长度差异的关键。
6. 窗口大小 Window Size(16 位)
接收方通告的**接收窗口(rwnd)**大小,以字节为单位。告诉对方"我还能接收多少数据"。
原始设计最大仅 64KB(2^16-1),RFC 7323 引入窗口缩放选项后,实际窗口可达 1GB。
7. 校验和 Checksum(16 位)
覆盖 TCP 首部 + 数据 + 12 字节伪首部(包含源 IP、目的 IP、协议字段和 TCP 长度)。
伪首部的作用:检测 IP 地址错误(如路由错误导致包送到错误主机)。注意 TCP 校验和强度弱于 CRC,属于完整性保护而非安全机制。
8. 紧急指针 Urgent Pointer(16 位)
与 URG 标志配合,指示紧急数据在字节流中的结束位置。现代应用极少使用(Telnet 时代遗留)。
TCP 伪首部
+-------------------------------+
| Source IP Address | 32 位
+-------------------------------+
| Destination IP Address | 32 位
+-------------------------------+
| Zero | Protocol | TCP Len | 8+8+16 位
+-------------------------------+
校验和计算时把伪首部放在 TCP 首部前面一起计算,但伪首部不实际传输,仅在计算时使用。
TCP 选项(Options)
选项采用 TLV(Type-Length-Value) 编码,常见选项:
| 选项类型 | 名称 | 作用 |
|---|---|---|
| 2 | MSS | 最大段大小,三次握手时协商 |
| 3 | Window Scale | 窗口缩放因子,突破 64KB 限制 |
| 5 | SACK | 选择性确认,精确通告乱序段 |
| 8 | Timestamp | 时间戳,精确测 RTT + 防序列号回绕 |
| 34 | TFO | TCP Fast Open,减少 1 个 RTT |
选项仅在三次握手时通过 SYN 报文协商,连接建立后不可动态变更。
Wireshark 实战:解读一个真实 TCP 包
Transmission Control Protocol, Src Port: 54321, Dst Port: 443
Source Port: 54321
Destination Port: 443
Sequence Number: 1234567890
Acknowledgment Number: 0
Header Length: 28 bytes (7) ← Data Offset=7, 首部 28 字节(含 8 字节选项)
Flags: 0x002 (SYN) ← 只有 SYN 置位
Window: 65535
Checksum: 0x1234 [unverified]
Urgent Pointer: 0
Options: (8 bytes)
Maximum segment size: 1460 bytes
Window scale: 8 (multiply by 256)
SACK permitted
Timestamps
解读:
- 源端口 54321(浏览器随机端口),目的端口 443(HTTPS)
- Seq=1234567890(ISN),Ack=0(首次握手无确认)
- Header Length=28(20 固定 + 8 选项)
- Flags=0x002(只有 SYN)
- 协商了 MSS=1460、Window Scale=8、SACK、Timestamp
本篇小结
- TCP 首部固定 20 字节 + 选项最多 40 字节
- 源/目的端口(16 位)标识应用进程
- 序列号(32 位)标识字节流位置,ISN 随机生成
- 确认号(32 位)采用累积确认
- 控制标志:SYN/FIN 各消耗一个序列号;ACK 除首 SYN 外必置位
- 窗口大小(16 位)原始最大 64KB,窗口缩放后可更大
- 校验和覆盖首部 + 数据 + 伪首部
- 选项 TLV 编码,握手时协商,常见:MSS/Window Scale/SACK/Timestamp/TFO
动手实践
Wireshark 抓包访问一个 HTTPS 网站,找到第一个 SYN 报文,逐字段验证上述理论
计算:如果 Data Offset=8,首部多少字节?选项多少字节?
观察 SYN 报文中的选项:MSS 是多少?Window Scale 因子是多少?是否支持 SACK?
思考:为什么 SYN 和 FIN 要各消耗一个序列号?如果 SYN 不消耗序列号,三次握手会有什么问题?