TCP 数据偏移是头里有多少个 32 位字,用来指出用户数据从哪里开始。带选项时,头仍然是 32 位的整数倍。选项区的公式是数据偏移减 5,再乘 32。规范是 2022 年 8 月的 RFC 9293 第 3.1 节。它取代 RFC 793 里的 TCP 说明。本文没有把两份第 3 节逐字对照。
先看有多少个 32 位字
一个可独立引用的定义句:数据偏移(DOffset)是 4 位,表示 TCP 头里的 32 位字数。它指出数据从哪里开始。TCP 头即使含有选项,也是 32 位的整数倍(RFC 9293 第 3.1 节)。
图 1 在选项之前印出的字段是:源端口、目的端口、序号、确认号、这一行里的数据偏移和标志与窗口、校验和、紧急指针。按图数是 5 个 32 位字,也就是 20 个八位组。原文没有印出 20。数据偏移为 5 时,选项那一行的条件不成立。大于 5 时,选项才出现。
第 6 节把位偏移定义成图 1 里那 16 位对齐视图中的位置,并写明偏移 0 到 3 是数据偏移,不是头标志。按图从左往右,这 4 位在该视图的左端。原文没有把「高 4 位」印成另一套说法。这是第 3.1 节的图和第 6 节的对照。
| 第 6 节的位偏移 | 名称 | 第 3.1 节怎样描述 |
|---|---|---|
| 0 到 3 | 数据偏移 | 4 位。TCP 头的 32 位字数 |
| 4 到 7 | 留给将来 | 第 3.1 节收成一个 4 位保留字段 |
| 8、9 | CWR、ECE | 各 1 位。指向 RFC 3168 |
| 10 到 15 | URG、ACK、PSH、RST、SYN、FIN | 各 1 位 |
保留字段在第 3.1 节是 4 位。原文要求生成的段里为 0;收到时,若发送方或接收方主机没有实现对应的未来功能,则忽略。这两句的 Must 和 must 都不是全大写。RFC 9293 写明,BCP 14 的关键词只在全大写时按 BCP 14 解释。本文不把这两句升成大写必须。第 6 节的分配注记写,位 7 先前由 Historic RFC 3540 用作 NS,依据是 RFC 8311。这两份本文没有重读,因此不描述 NS 曾经怎样计算。
2025 年 11 月 22 日有一条勘误,要求把图里的保留字段拆成四个独立位,以匹配第 6 节。2026 年 1 月 20 日它被拒绝。核验说明写:这不影响协议的运作,也不影响正确实现;别的 RFC 以后仍可能更新这个字段。本文仍按第 3.1 节,把这 4 位称作一个保留字段,同时保留第 6 节已经分开列出的位偏移。
窗口字段是同一行里另外 16 位。它的数值在使用窗口缩放时要移位。移位不改变数据偏移,见 窗口缩放。
选项区是减 5 再乘 32
一个可独立引用的定义句:选项只在数据偏移大于 5 时出现。其大小写成 size(Options) == (DOffset-5)*32。这个大小把跟在实际选项后面的填充也算进去(RFC 9293 第 3.1 节)。
公式那一行没有印出单位。数据偏移的定义是 32 位字,因此每多 1 个字,是 32 位,也就是 4 个八位组。数据偏移为 6 时,按这个读法选项区是 4 个八位组。若把 32 当成八位组,会得到 32 个八位组。本文采用前一种。图的记法指向参考文献 [66],那是 2022 年 3 月 7 日仍在进行的草稿。本文没有重读它,因此不把草稿里 size() 的约定写成已经核对过的单位定义。
选项的长度是 8 位的整数倍,全部进入校验和。一个选项可以从任意八位组边界开始。种类 0 和种类 1 各占 1 个八位组,没有长度字节。其余选项是种类、长度、数据。长度把种类和长度这两个八位组也算进去。选项清单可以短于数据偏移所暗示的空间。选项列表结束之后的头内容必须是值为 0 的头填充。这是 MUST-69。
第 3.1 节要求必须支持的选项只有三种:
| 种类 | 长度 | 含义 |
|---|---|---|
| 0 | 无单独的长度字节 | 选项列表结束 |
| 1 | 无单独的长度字节 | 无操作 |
| 2 | 4 | 最大段长度 |
最大段长度通告的是载荷上限,不含 IP 头和 TCP 头。数值怎样和 MTU 分开,见 MSS 钳制。种类 2 的长度在这张表里是 4。只改写其中的数值,这个长度不一定变,数据偏移因此不一定变。某一设备会不会同时增删别的选项,本文没有测量,也不提供改写步骤。
时间戳和窗口缩放都放在这段选项区里。数据偏移负责指出选项区有多长,不负责解释位移或两个时间戳字段。字段含义见 TCP 时间戳。实现必须能够在任何段里接收 TCP 选项。除了种类 0 和种类 1,所有选项都必须有长度字段,包括将来的选项。实现必须准备处理非法的选项长度,例子是 0。原文建议的做法是重置连接并记下原因。这是 MUST-7 的建议步骤,不是本文追加的操作。
第 3.1 节还写,扩展选项空间的工作仍在进行,并指向参考文献 [65]。2026 年 10 月 24 日打开的数据跟踪页上,draft-ietf-tcpm-tcp-edo-15 标成 Expired。最近修订是 2025 年 5 月 31 日,页面上的 Last updated 是 2025 年 12 月 2 日。摘要写它更新 RFC 9293,用来放更大的选项。正文没有读,因此不描述扩展之后数据偏移怎样解释。同日的 RFC 9293 元数据没有列出更新者。
它指出数据,不代替校验和
校验和盖住头和正文。选项既在头里,就在这个范围里。计算时校验和字段本身先当成 0。段的头加正文若是奇数个八位组,计算时在最后一个八位组右侧补 0,凑成 16 位。这个补 0 不作为段的一部分发出。它不是数据偏移所要求的头填充。头填充是发出去的 0,而且必须出现在选项列表结束之后。两份 0 不要并成一份。
伪首部另有一个 TCP 长度:TCP 头长加上数据长,单位是八位组。它不是线上字段,也不把伪首部那 12 个八位组算进去。头长由数据偏移指出。第 3.1 节没有把「数据偏移乘 4」印成这个 TCP 长度的等式。IPv6 的伪首部指向 RFC 8200 第 8.1 节。本文没有重读那一节,因此不另写三个 0 字节怎样排列。校验和为何不能省略,见 TCP 校验和。
外层隧道若原样封装整份 IP 包,内层 TCP 头里的数据偏移不因外层变长而重算。外层自己的 TCP 或 UDP 头另有长度字段。UDP 那个长度含不含 UDP 头,不由数据偏移决定。本文没有把 RFC 768 的长度字段再定义一遍。
序号是本段第一个数据八位组的编号。SYN 置位时,序号是初始序号,第一个数据八位组是初始序号加 1。数据偏移不参加这个加法。确认号在 ACK 置位时,是发送方期望收到的下一个序号。窗口从确认号所指的那个八位组算起。这三句都在第 3.1 节。紧急指针是相对本段序号的正偏移,指向紧急数据之后的那个八位组,只有 URG 置位时才解释。本文不展开何时该置 URG。
现象对照
| 现象 | 更可能先看什么 |
|---|---|
| 把数据偏移当成头的字节数 | 单位是 32 位字。按图,5 对应选项之前的那些字段 |
| 数据偏移是 5,后面仍按选项去读 | 选项只在大于 5 时出现 |
| 数据偏移是 6,却留出 32 字节的选项区 | 公式乘的是 32。按字来读是 32 位,也就是 4 个八位组。公式行没有印出单位 |
| 选项列表结束之后不是 0 | 与 MUST-69 不符。那些字节必须是头填充 0 |
| 只改 MSS 的数值,数据偏移也变了 | 种类 2 的长度是 4。数值变了,长度不一定变。同时增删选项才会碰到字数 |
| 外层又套了一层隧道头 | 不因此改内层这个字段。它只描述内层 TCP 头 |
核对清单
- 数据偏移是 4 位字数:指出数据从哪里开始。按图,选项之前是 5 个字、20 个八位组,原文没有印出 20。第 6 节把位偏移 0 到 3 划给这个字段;
- 选项区是减 5 再乘 32:大于 5 才有选项。本文把 32 读成位,因为单位是 32 位字。公式行没有印出单位。记法草稿没有重读;
- 列表结束之后必须是 0:种类 0 和 1 没有长度字节。其余选项的长度含种类和长度自身。必须支持的只有 0、1 和长度为 4 的种类 2;
- 保留位的 Must 不是全大写:拆开图里这 4 位的勘误已于 2026 年 1 月 20 日被拒绝。位 7 曾经叫 NS,RFC 3540 和 RFC 8311 没有重读;
- 外层封装不改内层字数。扩展数据偏移的草稿在 2026 年 10 月 24 日的数据跟踪页上已经过期,正文没有读。已验证勘误不改第 3.1 节的字段定义。
RFC 9293 的元数据把它标成 Internet Standard,STD 7,没有列出更新者。已验证勘误 8167、8171、8126 和 8710 分别改第 3.10.7.3 节、附录 B、第 3.3.1 节和第 3.3.2 节的图,不改数据偏移的定义。Held for Document Update 的 8804 和 8805 都在第 3.10.7.4 节,本文没有读完。Rejected 的 8654 就是上面那条图。8760 和 8916 是另外两节,没有读成对第 3.1 节的改写。