摘要

  • 每个端点在 SYN 中声明适用于自身接收窗口的二进制移位数,因此同一连接的两个方向可以采用不同尺度。
  • 握手后,移位数为 7 时,字段值 65,535 表示 8,388,480 字节;但 SYN 内的同一字段永远不缩放。
  • 缩放扩大的是接收额度的表达范围。它不会创造缓冲内存、放宽拥塞窗口,也不承担 SACK、时间戳或 PAWS 的职责。

TCP 的 Window 字段不是线路速率。它是接收方给发送方的即时边界:在已确认位置之外,我还能接收多少数据。早期报头只给这个边界 16 位,最大值是 65,535。只要链路的带宽时延积小于它,限制并不显眼;当一条路径需要上百万字节才能保持忙碌时,字段本身就会压低吞吐量。

1988 年的 RFC 1072 没有主张重画 TCP 报头。它设计了一个三字节选项,并把选项放进可靠传送的 SYN。选项携带一个以 2 为底的指数。后续报文仍传输原来的 16 位字段,接收该字段的一方根据握手记住的指数左移,还原更大的窗口。

为何必须在 SYN 中决定?普通 ACK 可能丢失,而且不会仅因携带新选项就被可靠重传。如果尺度能在这种 ACK 中改变,一端可能已经换了单位,另一端却继续按旧单位读数。握手会重传,因此适合在数据流开始前固定语义。

两个方向,两把刻度尺

A 发送的指数描述 A 以后将公布的接收窗口。B 保存它,用来解释来自 A 的 Window 字段;B 的指数则由 A 保存。二者不必相同,因为两端的内存和策略可能不同。

窗口缩放是“要约”,不是单方命令。只有双方的 SYN 都带有选项时,缩放才在两个方向启用。指数零也是有效要约,表示支持机制但倍率为一。若一侧没有发送,两个保存的移位数都保持为零。

带 SYN 的报文还有一条关键例外:其中的 Window 字段不参与缩放。握手之后,65,535 << 7 等于 8,388,480 字节;在 SYN 中,65,535 就只表示 65,535。字段没有改变,连接上下文改变了它的意义。

RFC 1323 在 1992 年取代实验性的 RFC 1072,把高性能扩展带入标准轨道。RFC 7323 又在 2014 年取代 RFC 1323,并把窗口解释明确为 30 位。移位数上限为 14,最大可表达值略小于 1 GiB;收到更大的指数时必须按 14 处理,以维护 32 位序列号空间里的安全比较。

范围变大也意味着粒度变粗。移位数为 7 时,字段每增加一,真实边界增加 128 字节。端点内部仍可精确管理缓冲区,但把额度编码进报头时必须接受量化。

更大的额度不等于更大的发送权

接收窗口由接收方的缓冲区和消费速度约束;拥塞窗口由发送方对路径状况的判断约束。发送方必须服从两者中较小的值。即使对端公布了 8 MB,拥塞控制只允许 64 KB,发送方也不能借窗口缩放越过后者。

窗口缩放也不报告缺口之后已经到达的字节块,那是 SACK 的主题。它不通过时间戳判断报文是否来自序列号旧周期,那是 PAWS 的主题。它也不解决 RTT 样本与重传定时。几个机制曾共同出现在“高性能 TCP”的历史里,却提供不同证据。

2022 年的 TCP 基础规范 RFC 9293 将窗口缩放列为常用、建议为高性能实现的选项,但不是基本互通所必需。连接可以在没有缩放时成功建立,只是随后无法公布足够大的额度。成功握手证明能通信,不证明能有效填满路径。

观察者也必须记住握手

若抓包从握手之后才开始,单独看到 Window=32,768 并不能得出真实窗口。它可能是 32,768 字节,也可能是数兆字节。解释者需要同一方向的 SYN 选项。

有状态防火墙同样受此约束。RFC 7323 警告,中间设备删除或修改 SYN、SYN-ACK 中的选项,会让端点持有不一致的参数。无法确定尺度的设备,不应继续用原始 16 位值执行基于窗口的过滤。

这项扩展的历史价值正在这里:它没有用更长报头压倒兼容性,而是让可靠的连接建立过程分配字段含义。两端分别保存两个方向的读法,范围受序列空间约束,真实额度仍归接收方控制。数字之所以变大,不是因为位数增加,而是因为握手先规定了单位。

资料来源