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


重传策略
可能只是重复相同的序列:重新发送下一个数据包 , 直到接收方指示它已处理了直至 s_n 的所有数据包为止 。 这种方法的问题在于 , 它需要一个 RTT , 直到发送方知道接下来必须重新发送的数据包为止 。 尽管这种策略可以避免不必要的重传 , 但要等到 TCP 重新发送整个数据窗口后 , 它可能要花几秒钟甚至更长的时间 。
另一种方法是一次重新发送几个数据包 。 当丢失了几个数据包时 , 此方法可使 TCP 恢复更快 。 在上面的示例中 , TCP 重新发送了 s_3、s_4、s_5、... , 但是只能确保已丢失 s_3 。
从延迟的角度来看 , 这两种策略都不是最佳的 。 如果只有一个数据包需要重新发送 , 第一种策略是快速的 , 但是当多个数据包丢失时 , 它花费的时间太长 。
即使必须重新发送多个数据包 , 第二个也是快速的 , 但是以浪费带宽为代价 。 此外 , 这样的 TCP 发送方在进行不必要的重传时可能已经发送了新数据 。
通过可用信息 , TCP 无法知道丢失了哪些数据包 。 这就是 TCP 选择性确认 (SACK)的用武之地了 。 就像窗口缩放和时间戳一样 , 它是另一个可选的但非常有用的 TCP 特性 。
SACK 选项
TCP Sack-Permitted Option: Kind: 4, Length 2+---------+---------+| Kind=4| Length=2|+---------+---------+支持此扩展的发送方在连接请求中包括 “允许 SACK” 选项 。 如果两个端点都支持该扩展 , 则检测到数据流中丢失数据包的对等方可以将此信息通知发送方 。
TCP SACK Option: Kind: 5, Length: Variable+--------+--------+| Kind=5 | Length |+--------+--------+--------+--------+|Left Edge of 1st Block|+--------+--------+--------+--------+|Right Edge of 1st Block|+--------+--------+--------+--------+||/. . ./||+--------+--------+--------+--------+|Left Edge of nth Block|+--------+--------+--------+--------+|Right Edge of nth Block|+--------+--------+--------+--------+接收方遇到 s_2 后跟 s_5 ... s_n , 则在发送对 s_2 的确认时将包括一个 SACK 块:
+--------+-------+| Kind=5 |10|+--------+------+--------+-------+| Left edge: s_5|+--------+--------+-------+------+| Right edge: s_n|+--------+-------+-------+-------+这告诉发送方到 s_2 的段都是按顺序到达的 , 但也让发送方知道段 s_5 至 s_n 也已收到 。 然后 , 发送方可以重新发送那两个数据包(s_3、s_4) , 并继续发送新数据 。
神话般的无损网络
从理论上讲 , 如果连接不会丢包 , 那么 SACK 就没有任何优势 。 或者连接具有如此低的延迟 , 甚至等待一个完整的 RTT 都无关紧要 。
在实践中 , 无损行为几乎是不可能保证的 。 即使网络及其所有交换机和路由器具有足够的带宽和缓冲区空间 , 数据包仍然可能丢失:

  • 主机操作系统可能面临内存压力并丢弃数据包 。 请记住 , 一台主机可能同时处理数万个数据包流 。
  • CPU 可能无法足够快地消耗掉来自网络接口的传入数据包 。 这会导致网络适配器本身中的数据包丢失 。
  • 如果 TCP 时间戳不可用 , 即使一个非常小的 RTT 的连接也可能在丢失恢复期间暂时停止 。
使用 SACK 不会增加 TCP 数据包的大小 , 除非连接遇到数据包丢失 。 因此 , 几乎没有理由禁用此功能 。 几乎所有的 TCP 协议栈都支持 SACK —— 它通常只在不进行 TCP 批量数据传输的低功耗 IOT 类的设备上才不存在 。
当 Linux 系统接受来自此类设备的连接时 , TCP 会自动为受影响的连接禁用 SACK 。
总结本文中研究的三个 TCP 扩展都与 TCP 性能有关 , 最好都保留其默认设置:启用 。


推荐阅读