摘要

  • RFC 3517 把累计确认与 SACK 块写入发送方的记分板,再由 SetPipe() 估算仍应视作在途的字节;它没有在路由器之间安装一只计数器。
  • 对尚未判定丢失便已重传的数据,原副本与重传副本可能被双重计数。cwnd - pipe 由此决定能否再发,而不宣称知道哪个副本仍在网络中。

一次 TCP 窗口里出现多处丢失时,累计 ACK 只能告诉发送方连续收到的左边界。接收方可能已经拿到更高序号的若干数据岛,却无法用一个累计值表达这些岛。RFC 2018 引入 SACK-Permitted 与 SACK 块,允许接收方报告这些不连续区间,让发送方避免盲目重传所有内容。

但这份报告不是不可撤销的保管凭证。接收方可以丢弃此前已经 SACK 的数据,所以发送方在累计确认真正越过它之前,不能从重传缓冲区释放该段。SACK 提高了信息量,却没有改变证据的作用域。

RFC 3517 的工作是把这些有限证据变成可执行的恢复程序。HighACK 记录累计确认的最高位置,HighData 记录已发送的最高位置,HighRxt 约束重传范围,RecoveryPoint 划定本轮恢复的终点。Update() 更新记分板,IsLost() 根据更高位置出现的离散 SACK 块或字节阈值推断某段已经丢失。

“推断丢失”并不等于观察到丢包发生在哪台设备、哪个队列或哪个时刻。它只是发送方在现有证据下达到了一项判定阈值。RFC 3517 把这项局部判断与网络事实分开,才让后面的控制动作有清楚边界。

SetPipe() 从 HighACK 遍历到 HighData。没有被 SACK、也没有被 IsLost() 判为丢失的字节会增加 pipe,因为算法假定它们仍在路径上。已经重传且位于 HighRxt 以下的字节也会增加这个值。标准明确称它为估算。

最关键的细节是双重计数。如果发送方在尚未把原副本判定为丢失时又发出一个副本,它无法确定原副本、重传副本或两个副本是否已经离开网络。若把两者都当作已经退出,pipe 就可能偏低,随后又注入过多数据。因此标准宁可在特定情况下把同一字节计两次。

这种做法不是在修饰一个传感器读数,而是在选择不确定性由谁承担。保守高估可能让单条流暂时少发,低估却可能在共享拥塞期间增加压力。算法把未知状态转化为克制,而不是转化为虚假的精度。

进入快速恢复时,RecoveryPoint 被设为 HighData,拥塞窗口按当时的规则收缩,首个推定丢失段被重传,然后运行 SetPipe()。后续每个 ACK 都可能更新记分板并重新计算。只要 cwnd - pipe 至少留下一个 SMSS,NextSeg() 才选择下一段丢失数据、允许的新数据,或特定条件下的救援重传。

这个差值是发送许可,不是路径测量。发送后增加 pipe,只表示发送方更新了自己的账本。它没有证明数据进入某个队列、仍停留在链路、抵达接收方,更没有证明应用读取。累计 ACK 越过 RecoveryPoint 才结束这一恢复阶段,而应用结果仍是另一层证据。

相邻标准展示了不同控制面。RFC 3042 的 Limited Transmit 可以利用最初两个重复 ACK 发送新数据;RFC 2883 的 D-SACK 可以报告重复到达,帮助发现误重传或乱序;RFC 5681 给出拥塞控制框架;RFC 6582 则让 NewReno 通过部分 ACK 进行恢复。它们改进发送方的判断与动作,却都不是网络内部的库存表。

重传超时仍然是后备边界。一旦 RTO 发生,发送方必须面对接收方可能反悔的事实,旧 SACK 信息不能继续当作永久事实。看似精细的记分板始终是会过期、会降级的证据投影。

2012 年的 RFC 6675 取代 RFC 3517,修正了丢失阈值、进入恢复和救援重传等规则。它保留了同一条认识边界:pipe 仍是估算,SACK 仍具有建议性。后来的 RFC 6937 以 Proportional Rate Reduction 提供另一种恢复节奏,说明“在途”计数本身也是控制政策,而非天然唯一的真值。

RFC 9293 现在承载 TCP 基础规范,IANA 的 TCP 参数表记录 SACK-Permitted 与 SACK 选项编号。登记证明编号权威,却不证明某条连接成功协商、正确实现、发生特定丢失或完成应用交付。

RFC 3517 留下的历史价值正在这里:互联网协议常常必须在没有全景观测时行动。好的标准会说明证据来自哪里、变量控制什么、未知部分仍是什么。pipe 足以安排下一次发送,却不足以宣布网络已经被数清。

来源