Appearance
TCP 字段速查表
TCP 首部(Header)由 20 字节的固定部分和可选的扩展选项(Options)组成。首部的总长度由 Data Offset(数据偏移)字段指示,而应用数据紧随在首部之后。在 TCP 通信中,一个方向上的 Seq(序列号)标识该方向正在发送的字节流位置,而 Ack(确认号)则标识期望从反方向连续接收到的下一个字节位置。
固定首部布局
text
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 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|DOff |Res|A|C|E|U|A|P|R|S|F| Window |
| | |E|W|C|R|C|S|S|Y|I| |
| | | |R|E|G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options and Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Application Data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+AE 标志位属于 AccECN 扩展协议。在抓包分析传统 TCP 报文时,该位置可能显示为保留位(Reserved)或旧称,具体取决于抓包工具(如 Wireshark)的版本,以及连接双方是否成功协商了 AccECN。
固定字段
| 字段 | 位数 | 解析方式 | 主要用途 | 应用层的控制方式 |
|---|---|---|---|---|
| Source Port | 16 | 按网络字节序读取 | 标识发送端端口 | 通过 bind 选择本地端口;客户端也可交给操作系统自动分配临时端口 |
| Destination Port | 16 | 按网络字节序读取 | 标识接收端端口 | 通过 connect 系统调用传入的目标地址指定 |
| Sequence Number | 32 | Wireshark 默认显示相对值 | 标识当前报文段首个数据字节在整个字节流中的位置。当带有 SYN 标志时,该字段携带本方向的初始序列号(ISN),真实数据的首字节序号从 ISN+1 开始 | 完全由底层 TCP 协议栈管理 |
| Acknowledgment Number | 32 | ACK 标志置位时生效 | 表示期望从对端接收的下一个字节序号,用于向发送方确认之前的数据已成功到达 | 完全由底层 TCP 协议栈根据实际接收进度自动产生 |
| Data Offset | 4 | 数值乘以 4 字节 | 指示 TCP 首部的总长度(包含 Options) | 应用程序通过开启特定的 TCP 选项(如 Timestamp)间接影响首部长度 |
| Reserved | 通常3 | 结合当前规范和扩展解释 | 为未来的协议扩展保留 | 普通应用层无直接控制接口 |
| Flags | 9 | 单独检查每一位 | 包含 9 个标志位,用于表示握手、确认、断开连接、重置、推送(Push)和拥塞控制反馈等状态 | 部分行为可由 connect、shutdown、连接关闭方式及 Socket 选项间接触发 |
| Window | 16 | 结合 Window Scale(缩放因子)计算 | 用于通告接收方的当前可用窗口大小(即流控机制中的接收能力) | 受应用层读取速率、系统接收缓冲区大小以及内核 TCP 算法共同影响 |
| Checksum | 16 | 结合 IP 伪首部计算 | 用于检测 TCP 首部和数据在传输过程中的完整性,防范误码 | 完全由底层协议栈计算。注意:如果网卡开启了校验和卸载(Checksum Offload),在本机抓取发出的包时可能会看到报错,这属于正常现象 |
| Urgent Pointer | 16 | URG 标志置位时生效 | 作为相对于当前 Sequence Number 的偏移量,指出紧急数据结束的位置 | 仅少数平台提供带外数据(Out-of-band Data)接口,现代应用中已极少使用 |
Data Offset (数据偏移) 快速计算
Data Offset 字段的单位是 32 位字(即 4 字节)。
| Data Offset | TCP 首部长度 | Options 与 Padding 长度 |
|---|---|---|
| 5 | 20字节 | 0字节 |
| 6 | 24字节 | 4字节 |
| 8 | 32字节 | 12字节 |
| 10 | 40字节 | 20字节 |
| 15 | 60字节 | 40字节 |
举个例子:如果 IP 层信息显示该 TCP 报文段的总长(Total Length)为 132 字节,而 TCP 头的 Data Offset 为 8,那么实际的 TCP 负载(Payload)数据长度就是:
在 IPv4 网络抓包时,TCP 数据的长度也可以直接通过 IPv4 报文总长计算得出:
如果是 IPv6 环境,其基础首部使用的是 Payload Length(有效载荷长度)字段,计算时还需要将路径上附加的各类 IPv6 扩展首部(Extension Headers)开销一并扣除。
标志位
| 标志 | 名称 | 常见阶段 | 含义与抓包分析提示 |
|---|---|---|---|
| SYN | Synchronize | 建立连接 | 用于同步双方的初始序列号。注意:SYN 标志本身会在序列空间中消耗一个位置(即长度算作 1) |
| ACK | Acknowledgment | 握手第二步及之后的所有报文 | 表明 Acknowledgment Number 字段是有效的 |
| FIN | Finish | 有序关闭连接 | 表示发送方的数据已经发送完毕。注意:FIN 标志同样会消耗一个序列号位置 |
| RST | Reset | 异常中止或拒绝连接 | 用于立即重置或终止连接状态。在排查问题时,通常需要结合序列号以及具体的上下文场景来判断 RST 的触发来源 |
| PSH | Push | 数据传输 | 提示接收端的 TCP 协议栈应尽快将这批数据向上层应用交付,而不是继续在缓冲区积攒。注意:TCP 是流式协议,PSH 并不定义应用层的“消息边界” |
| URG | Urgent | 早期或专用协议 | 表明 Urgent Pointer 字段是有效的 |
| ECE | ECN Echo | ECN 协商和拥塞反馈 | 在 Classic ECN 中,用于参与能力协商并向发送方反馈网络拥塞;在 AccECN 中,它与 CWR 和 AE 组合编码,提供更细粒度的拥塞反馈 |
| CWR | Congestion Window Reduced | ECN 响应 | 在 Classic ECN 中,发送方用它来告知接收方自己已经减小了拥塞窗口;在 AccECN 中,与 ECE 和 AE 共同参与反馈编码 |
| AE | Accurate ECN | AccECN 协商与计数反馈 | 属于 AccECN 扩展,与 ECE、CWR 组成 3 位联合编码,具体含义需参考 RFC 9768 规范 |
Seq 与 Ack 速算规则
假设有这样一个 TCP 报文段(Segment):
- 相对序列号
Seq = 1000 - TCP 负载数据长度为 300 字节
- 未设置 SYN 或 FIN 标志
当接收端成功收到该报文后,它返回的 ACK 确认号将向后推进至:
以下是几个计算 Seq 推进量的常用规则:
| 报文内容 | 本方向下一 Seq 的推进量 |
|---|---|
| 纯 ACK,无负载数据 | 0 |
| 携带 120 字节负载数据 | 120 |
| SYN 报文,无负载数据 | 1 |
| FIN 报文,无负载数据 | 1 |
| 携带 80 字节负载数据并附加 FIN 标志 | 81 |
注意:TCP 序列号是一个 32 位的无符号整数,采用回绕(Wrap-around)的模运算机制。当序列号逼近最大值
常用选项 (TCP Options)
| 选项 | 常见 Kind | 出现位置 | 主要用途 | 抓包分析要点 |
|---|---|---|---|---|
| End of Option List | 0 | 选项末尾 | 标记有效选项列表的结束 | 该标志之后,直到 Data Offset 所指示的首部末尾,均视为无意义的填充数据(Padding) |
| No-Operation | 1 | 任意选项区 | 单字节占位符,主要用于 4 字节边界对齐 | 该选项只占 1 字节,没有 Length 字段 |
| MSS | 2 | SYN、SYN+ACK | 通告本端所能接收的最大 TCP 数据载荷(Payload)大小 | 通信双方会在握手阶段各自独立通告。其实际取值受路径 MTU、IP 地址族(IPv4/IPv6)以及底层首部开销的综合影响 |
| Window Scale | 3 | SYN、SYN+ACK | 由于基础 Window 字段只有 16 位,此选项用于按比例放大窗口的最大表达范围 | 仅在三次握手期间进行双向独立协商。确定的缩放因子将作用于该方向上的后续所有报文 |
| SACK Permitted | 4 | SYN、SYN+ACK | 用于协商连接双方是否支持选择性确认(SACK)功能 | 仅仅是在握手阶段声明能力,并不实际在此报告丢包缺口 |
| SACK | 5 | 发生乱序或丢包时的 ACK 报文 | 精确报告已经收到的不连续数据块范围,以避免不必要的重传 | 每个 SACK 块(Block)由左边界(起点)和右边界(终点)组成,右边界指向已收连续数据段后的第一个未收字节位置 |
| Timestamp | 8 | 协商成功后的大量报文 | 用于精确计算往返时延(RTT)并防范序列号回绕(PAWS 机制) | 包含 TSval(发送端时间戳)和 TSecr(回显的对端时间戳)两个值,通过分析它们可以追踪报文的交互时序 |
在估算 MSS 与底层网络 MTU 的关系时,常用的公式为:
例如,在标准以太网中,MTU 通常为 1500 字节。扣除 20 字节的 IPv4 基础首部和 20 字节的 TCP 基础首部后:
对于 IPv6,由于其基础首部增至 40 字节,相同条件下的 MSS 会缩减为 1440 字节。此外,如果是经过 VPN/隧道封装、启用了 IP 扩展首部,或者受限于整条链路中的最小路径 MTU(PMTU),最终实际协商的 MSS 取值往往会变得更小。
Wireshark 常见显示提示
| Wireshark 显示项 | 来源机制 | 抓包分析排障提示 |
|---|---|---|
[TCP Segment Len: N] | Wireshark 根据捕获到的 IP 报文总长减去各级首部长度计算得出 | 注意:如果抓包的机器(发送端)开启了 TSO/GSO 等分段卸载功能,网卡发包前还没有进行实际分片,你可能会看到远超 MTU 的巨大 Length(如几万字节),这是正常现象 |
[Next sequence number: X] | 由 Wireshark 自动计算推导 | 综合考虑了负载数据长度,以及 SYN/FIN 标志对序列号空间的占用(各占用 1 字节长度) |
[Calculated window size: N] | 由捕获到的 Window 字段原始值乘以协商好的缩放因子得出 | 注意:如果在抓包时错过了前期的三次握手阶段,Wireshark 将无法得知 Window Scale 因子,此时计算出的窗口大小可能严重偏小,并不代表真实的流控状态 |
[SEQ/ACK analysis] | 由 Wireshark 的 TCP 会话追踪引擎通过上下文状态推断生成 | 极度依赖抓包数据的完整性。如果出现抓包层面漏抓丢包、数据包乱序到达分析器,或者你是从连接中途开始抓包的,这里的专家提示(如 TCP Retransmission, Dup ACK 等)往往会频繁误报失效 |
Checksum: ... [unverified] | 受 Wireshark 的首选项配置或网卡 Checksum Offload(硬件卸载)的影响 | 建议在排查校验问题时,尽量在接收端进行抓包,因为网卡收到包时线缆传输的真实 Checksum 尚未被修改或卸载,更具诊断价值 |
应用程序层面的控制能力
作为底层的传输协议,TCP 对上层应用屏蔽了大量的细节。对于普通的流式 Socket 应用程序,开发者通常只能进行以下几方面的直接控制:
- 四元组建立:通过
bind()选择绑定的本地 IP 与端口;通过connect()指定远端的目的地址与端口。 - 数据发送行为:决定何时调用
send()/write()写入数据,以及每次写入的具体字节内容。 - 数据接收行为:通过
recv()/read()的读取频率和每次读取的大小,间接影响本地接收缓冲区的消耗速率(进而动态影响 TCP 向对端通告的接收窗口)。 - Socket 选项调优:例如设置
TCP_NODELAY(禁用 Nagle 算法以降低小包延迟)、开启 TCP Keepalive 保活探测机制,或调整系统层面的收发缓冲区配额大小(SO_SNDBUF,SO_RCVBUF)。 - 连接拆除方式:调用
shutdown()发起优雅的单向半关闭,调用close()进行双向的正常挥手关闭,或者利用SO_LINGER等选项触发发送 RST 报文以进行异常中止。
与此同时,诸如数据分段(Segmentation)、序列号与确认号的管理、重传超时的计算、滑动窗口的通告与调节,以及校验和(Checksum)的生成与验证等核心机制,均由操作系统内核中的 TCP 协议栈全权接管处理。我们在应用代码中无法直接干预,只能通过网络抓包工具来旁路观察这些底层行为。
抓包分析:报文快速检查 Checklist
在实际排查网络问题阅读 TCP 数据包时,建议按以下顺序进行逐层解析:
- 确定流向与身份:检查源/目的 IP 及端口(即四元组),明确当前报文是由客户端发往服务端,还是相反方向。
- 分离首部与数据:查看 Data Offset(首部长度),剥离头部后,确认实际传输的 TCP 负载数据长度(Payload Length)。
- 识别报文性质:查看 Flags 标志位,判断这是握手建连、正常数据传输、主动挥手断开,还是异常重置(RST)。
- 定位发送进度:检查当前报文的 Seq(序列号),结合 Payload 长度,明确本段数据在整个发送字节流中的具体位置区间。
- 确认接收进度:如果 ACK 标志置位,查看 Ack(确认号),明确对端已经成功收到了哪个位置之前的连续字节。
- 评估流控压力:观察 Window(接收窗口大小)并务必结合 Window Scale(缩放因子),判断接收端此时是否有足够的缓冲区余量。
- 检查附加功能:解析 Options(可选字段),重点关注 MSS、SACK 和 Timestamp 等会显著影响传输性能与机制的协商参数。
- 排查校验异常:最后查看 Checksum。如显示报错,需结合抓包位置(发送端还是接收端)判断是否是由于网卡 Checksum Offload(硬件卸载)引发的“假报警”。