摘要

  • 累积 ACK 可以证明接收字节的前沿已经移动;当同一序列空间被发送两次时,它却不能证明是哪一个传输实例带来了这些字节。
  • Karn 算法禁止用重传后的 ACK 采集 RTT;指数退避在有效证据缺席期间保留较谨慎的 RTO,直到新的无歧义交换出现。
  • TCP 时间戳可以为传输实例补上有限的标识,但回显和采样仍有严格条件;它既不会让每个 ACK 都成为合格样本,也不证明应用任务已经完成。

一个终点,两个可能的起点

发送端在零时刻发出序号 10000 到 10999 的字节,并启动重传计时器。计时器到期时仍未收到确认,于是相同的序列空间再次进入网络。很快,一个 ACK 表示接收端下一步期待 11000。

第一种可能是:原始段没有丢,只是延迟很长;重传之后到达的 ACK 实际由第一次发送触发。若从第二次发送计时,所得 RTT 会过短。第二种可能是:原始段已经丢失,真正到达的是重传;若从第一次发送计时,超时等待也被算入 RTT,结果会过长。

两个时间戳都是真的,差值也能正确计算,问题仍然没有解决。缺失的不是更精细的钟,而是“这次回复对应哪一次出发”的因果关系。普通 ACK 没有携带这种关系。

但它并非没有价值。ACK 可以推进 SND.UNA、释放已确认数据,并允许连接在流量控制和拥塞控制约束下继续。它对“字节前沿是否推进”有权作答,对“哪次发送花了多久”没有权作答。同一条信号在不同决策中的证据资格并不相同。

自适应计时器还需要样本入口

RFC 793 已经认识到,互联网路径的延迟差异太大,TCP 不能永远等待一个固定时长。它提出测量往返时间、平滑结果,再从中得到有上下界的重传超时。

这个反馈环是必要的:发送、观察、更新、再决定下一次等待多久。可任何反馈环都需要回答另一个问题——哪些观察有资格进入模型。若重传之后的每一个 ACK 都直接更新估计器,模型就会从起点不明的区间学习。

错误还会自我放大。假如原始段只是慢,软件却把 ACK 归给刚刚发出的副本,RTT 会显得很低。下一轮 RTO 因此缩短,更早的超时产生更多不必要重传;更多重传又产生更多无法归因的 ACK。测量规则改变了被测环境,再把自己制造的现象当成路径很快的证据。

RFC 1122 后来明确说,RFC 793 建议的 RTO 计算已经不充分。它要求主机 TCP 同时实现 Jacobson 算法和 Karn 算法。前者把 RTT 的变化程度纳入估计;后者决定一个 RTT 样本能否进入估计。一个管理计算方法,一个管理证据入口。

Karn 让“不知道”成为可执行状态

Karn 的核心规则很短:不要从发生过重传的段取得 RTT 样本。一段序列空间只要出现多个发送实例,普通累积 ACK 就不能指明促成接收的是哪一个实例。程序不能靠选择更顺眼的起始时间恢复缺失的标识。

拒绝样本不等于拒绝 ACK。TCP 仍然承认接收进展,清理发送队列,并继续处理窗口。被暂停的只有一项下游用途:这次 ACK 不能像拥有清楚因果历史那样更新 SRTT 和 RTTVAR。

这是一种很重要的边界。一个事实可以足以支持某个动作,却不足以支持另一个动作。路由通告可以显示可达性,却未必证明授权;登记记录可以显示当前内容,却未必证明形成过程。在这里,ACK 可以证明累积接收,却不能证明一对一的往返时间。

代价是明显的。路径正在变化时,重传区间恰好不能提供新的普通 RTT 样本。但拿一个被污染的数字填空不会降低不确定性,只会把不确定性藏进平均值,让后续控制和审计误以为它已经消失。

指数退避把不确定性保留下来

RFC 1122 还要求对同一段连续发生的 RTO 使用指数退避。RFC 2988 以及后来取代它的 RFC 6298 把规则写得更清楚:维护平滑往返时间 SRTT 和变化量 RTTVAR,由二者计算 RTO;计时器到期时重传最早未确认的段,并把 RTO 加倍。

退避常被描述为对拥塞的克制。放在 Karn 的历史里,它还有更窄的一层意义:当最新回复不能提供合格测量时,发送端不能用它宣称路径其实很快。已经放大的计时器继续承担谨慎假设。

RFC 6298 允许在得到新的 RTT 样本后让 RTO 回落。通常这要求后续的新数据未经重传便得到确认。恢复条件不是“过了一阵子”,而是“出现了一个起点和终点都能归属的交换”。时间本身不会清洗旧样本,新的证据才会。

标准允许实现比规定更保守,却不允许更激进。这种不对称体现了损失分配:过长等待主要让一条连接变慢;过短等待可能在本来就没有及时反馈的共享路径上增加副本和负荷。

RFC 6298 也把一般初始 RTO 从此前的三秒降到一秒,并为 SYN 或其 ACK 丢失规定特殊回退。参数可以随测量研究调整;禁止虚构因果配对的原则不依赖某一个秒数。

ACK 描述字节,不给数据报写传记

含糊并非偶然遗漏了一个调试字段,而是来自 TCP 的抽象。TCP 为字节流位置编号。重传可能使用不同的段边界覆盖同一批序号;接收端可以延迟 ACK,把多次到达合并确认,并丢弃重复字节而不向应用重复交付。

累积 ACK 只说下一个期待的字节。它不返回一个不可变的“网络包身份证”。若要求接收端为每次发送实例保存并汇报完整因果账本,共同协议的状态和语义都会扩大。

TCP 选择了较小的公共承诺:接收端报告流前沿;知道自己是否重传的发送端负责判断样本是否含糊。这种权限安排把筛选责任放在掌握本地发送历史的一方。

抓包界面容易制造另一种错觉。一个 ACK 在时间轴上紧跟重传,不等于重传就是原因;可能是原始段的迟到结果。邻近只能提出假设。没有正确协商和处理的时间戳,普通报文顺序无法给出唯一归因。

同理,ACK 也不证明远端进程已经读取、磁盘已经持久化或业务事务已经完成。传输层拥有序列状态,更高层结果需要由拥有该结果的组件给出回执。

时间戳补上有限的实例标签

RFC 6298 规定了一个重要例外:使用 TCP Timestamp 选项并消除实例含糊时,可以从重传数据取得 RTT 样本。RFC 7323 说明了 TSval 和返回方向中的 TSecr。

发送实例携带一个值,接收方在后续报文中回显它。发送端因而可以把返回值关联到接收端实际见到的传输时钟值。原始发送和重传之间缺少的标签,在连接内部得到补充。

但“携带时间戳”和“允许更新时间估计器”仍是两件事。RFC 7323 要求只有让发送窗口左边缘前进的返回值才可更新平均 RTT。延迟 ACK、序列空洞、重排序以及接收端选择回显哪个 TSval 的规则,都会影响样本。

样本过多也不是自动更好。RFC 6298 的权重隐含了大约每个 RTT 一次测量的历史尺度;若每个包都用相同权重更新,估计器可能太快忘掉路径在几个 RTT 内的变化,反而增加伪重传。实例身份解决后,采样频率仍需要治理。

Timestamp 也不是可信的全球时间。它只需近似按真实时间增长,并通过回显计算差值。它不认证对端身份,不证明两台机器的民用时钟同步,更不证明应用动作成功。

现代 TCP 仍然保留这条拒绝规则

RFC 9293 是现行基础 TCP 规范,仍要求按 RFC 6298 计算 RTO,其中明确包括 Karn 的样本规则。它也把 RTO 指数退避保留在避免拥塞崩溃的基本机制中。

一条主动丢弃观测的规则能够长期存在,原因不是它让眼前的连接看起来更快,而是它保护下一次决定。少一个样本的代价是暂时陈旧;接受一个自信但错误的样本,会让计时器系统性改变未来发送。

这并不表示所有现代实现、所有网络或所有 Timestamp 使用方式都一样。RFC 规定协议契约,不是部署普查。新的丢失检测和实现策略可以补充信号,却不能让一个仍然没有起点身份的区间成为真实 RTT。

规范之间的分工同样值得注意。基础 TCP 把详细计时器算法交给专门 RFC;计时器规范说明它与拥塞控制的关系,却没有把 RTO 和拥塞窗口混为一个变量;Timestamp 提供额外证据,却不接管应用结果。

空白也可以是一条高质量数据

监控系统偏爱连续曲线,产品界面也喜欢始终显示“当前 RTT”。当合格样本缺席时,沿用旧值、选择最近的发送时刻或插值都很方便。

Karn 提供了更诚实的状态:“这个重传区间不可用于普通 RTT 测量。”空白记录的是证据质量,不是采集程序失败。若它被一个貌似精确的数字替代,后来的操作者就再也看不见当时存在两种因果历史。

可审计的遥测应保存原始发送时间、重传次数、RTO 前后值、确认前沿、Timestamp 是否协商、实际采用的 TSecr,以及退避后第一个合格样本。只有最终平均数,无法解释路径变化和错误归因之间的区别。

Karn 的方法由三个动作组成:接受 ACK 对流进展的证明;拒绝它对传输实例 RTT 的越权解释;在新证据到来前用退避保留谨慎。协议的力量有时不在于从稀少信号中猜出更多,而在于明确知道哪些问题还没有答案。

来源与证据边界

RFC 793 给出早期自适应重传背景;RFC 1122 记录旧计算的不足,并要求 Karn、Jacobson 和指数退避;RFC 2988 与 RFC 6298 固化 RTO 和样本排除规则;RFC 7323 界定 Timestamp 的消歧与回显;RFC 9293 把要求保留在现代 TCP 中。

这些来源定义协议,不测量当前各类栈的部署比例、Timestamp 使用率或生产路径上的重传频率。本文也不把每次超时都归因于拥塞或攻击,不把 RTO 等同于拥塞窗口,更不把传输 RTT 当作应用成功证明。