TCP 窗口缩放、时间戳和 SACK( 二 )


TCP 响应方检查连接请求中包含的所有选项号 。 如果它遇到一个不能理解的选项号 , 则会跳过 该选项号附带的“长度”字节的数据 , 并检查下一个选项号 。 响应方忽略了从答复中无法理解的内容 。 这使发送方和接收方都够理解所支持的公共选项集 。
使用窗口缩放时 , 选项数据总是由单个数字组成 。
窗口缩放选项Window Scale option (WSopt): Kind: 3, Length: 3+---------+---------+---------+| Kind=3|Length=3 |shift.cnt|+---------+---------+---------+111窗口缩放 选项告诉对等方 , 应该使用给定的数字缩放 TCP 标头中的接收窗口值 , 以获取实际大小 。
例如 , 一个宣告窗口缩放因子为 7 的 TCP 发起方试图指示响应方 , 任何将来携带接收窗口值为 512 的数据包实际上都会宣告 65536 字节的窗口 。 增加了 128 倍(2^7) 。 这将允许最大为 8MB 的 TCP 窗口 。
不能理解此选项的 TCP 响应方将会忽略它 , 为响应连接请求而发送的 TCP 数据包(SYN-ACK)不会包含该窗口缩放选项 。 在这种情况下 , 双方只能使用 64k 的窗口大小 。 幸运的是 , 默认情况下 , 几乎每个 TCP 栈都支持并默认启用了此选项 , 包括 Linux 。
响应方包括了它自己所需的缩放因子 。 两个对等方可以使用不同的因子 。 宣布缩放因子为 0 也是合法的 。 这意味着对等方应该如实处理它接收到的接收窗口值 , 但它允许应答方向上的缩放值 , 然后接收方可以使用更大的接收窗口 。
与 SACK 或 TCP 时间戳不同 , 窗口缩放选项仅出现在 TCP 连接的前两个数据包中 , 之后无法更改 。 也不可能通过查看不包含初始连接三次握手的连接的数据包捕获来确定缩放因子 。
支持的最大缩放因子为 14 。 这将允许 TCP 窗口的大小高达 1GB 。
窗口缩放的缺点
在非常特殊的情况下 , 它可能导致数据损坏 。 但在你禁用该选项之前 , 要知道通常情况下是不可能损坏的 。 还有一种解决方案可以防止这种情况 。 不幸的是 , 有些人在没有意识到它与窗口缩放的关系的情况下禁用了该解决方案 。 首先 , 让我们看一下需要解决的实际问题 。 想象以下事件序列:

  1. 发送方发送段:s_1、s_2、s_3、... s_n 。
  2. 接收方看到:s_1、s_3、... s_n , 并发送对 s_1 的确认 。
  3. 发送方认为 s_2 丢失 , 然后再次发送 。 它还发送了段 s_n+1 中包含的新数据 。
  4. 接收方然后看到:s_2、s_n+1 , s_2:数据包 s_2 被接收两次 。
当发送方过早触发重新传输时 , 可能会发生这种情况 。 在正常情况下 , 即使使用窗口缩放 , 这种错误的重传也绝不会成为问题 。 接收方将只丢弃重复项 。
从旧数据到新数据
TCP 序列号最多可以为 4GB 。 如果它变得大于此值 , 则该序列会回绕到 0 , 然后再次增加 。 这本身不是问题 , 但是如果这种问题发生得足够快 , 则上述情况可能会造成歧义 。
如果在正确的时刻发生回绕 , 则序列号 s_2(重新发送的数据包)可能已经大于 s_n+1 。 因此 , 在最后的步骤(4)中 , 接收方可以将其解释为:s_2、s_n+1、s_n+m , 即它可以将 “旧” 数据包 s_2 视为包含新数据 。
通常 , 这不会发生 , 因为即使在高带宽链接上 , “回绕”也只会每隔几秒钟或几分钟发生一次 。 原始数据包和不需要的重传的数据包之间的间隔将小得多 。
例如 , 对于 50MB/s 的传输速度 , 重复项要迟到一分钟以上才会成为问题 。 序列号的回绕速度没有快到让小的延迟会导致这个问题 。
一旦 TCP 达到 “GB/s” 的吞吐率 , 序列号的回绕速度就会非常快 , 以至于即使只有几毫秒的延迟也可能会造成 TCP 无法检测出的重复项 。 通过解决接收窗口太小的问题 , TCP 现在可以用于以前无法实现的网络速度 , 这会产生一个新的 , 尽管很少见的问题 。 为了在 RTT 非常低的环境中安全使用 GB/s 的速度 , 接收方必须能够检测到这些旧的重复项 , 而不必仅依赖序列号 。


推荐阅读