前言

RFC6298讨论重传定时器相关的算法。因为首次涉足内核协议栈实现时,注释里总是提到各种 RFC,因此需要过一遍。对于文中提到的MUST,或者SHOULD,将会用颜色标注;其它仅强调的语气(MAY)会加粗处理

背景介绍

RFC1122 指出,RTO 的计算方式应当遵循 Jac88

[Jac88] Jacobson, V., “Congestion Avoidance and Control”, Computer Communication Review, vol. 18, no. 4, pp. 314-329, Aug. 1988.

而 RFC6298 要求发送端使用这些算法可以相对保守点,但是绝对不能更加激进

基本算法

求出 RTO 的算法需要维护两个变量:

  • SRTT: smoothed RTT
  • RTTVAR: RTT variation

还有两个常量:

  • G: clock granularity (<= 100 msec)
  • K: 4

初始阶段

在初始阶段,RTO 的初值被设为 1s。注意在 RFC2988 中提到 RTO 初值是 3s,而在 RFC6298 中改为了 1s,但是如果发生 SYN 丢失或者 ACK of SYN 丢失的话,将会回退到 3s,直到开始传输数据

首次 RTT 计算

当首次测量到 RTT 数值(设为 R)的时候,必须使用如下算法:

SRTT = R
RTTVAR = R / 2
RTO = SRTT + max(G, K * RTTVAR)

后续 RTT 计算

当后续测量到 RTT 数值(设为 R')的时候,必须使用如下算法:

RTTVAR = (1-β) * RTTVAR + β * (SRTT - R')
SRTT = (1-α) * SRTT + α * R'
RTO = SRTT + max(G, K * RTTVAR)

作者认为公式中列出的两个因子应当取 \(\alpha = \frac{1}{8}, \beta = \frac{1}{4}\)

另外,如果 RTO 小于 1s,那么应当上取整到 1s

作者提到以前是用粗粒度时钟 G 使得最小 RTO 值足够大来避免过度重传,事实上 G 的取值没有硬性要求,只是文中给出了一个参考的数

个人认为,RTTVAR 是处理突发情况的 RTT 偏差修正值;而 RTO 是一个应比 RTT 略大的值,因此参考了 G 和 K

RTT 样本的选择

TCP必须使用 Karn 算法进行 RTT 采样:

  • 采样对象:RTT 不得根据已重传的数据段进行采样,除非使用了 timestamp option
  • 采样时机:当使用了 timestamp 时,可以每次 ACK 都进行 RTT 采样;而 TCP 实现必须在每一次 RTT 范围内进行至少一次 RTT 采样

我顺手贴一下以前上学时做的计网笔记,脑子不够用笔记来凑数

Karn 算法的提出是基于这样的背景:

  • 对于一个分组 p,它曾被重传过,也就是说,对端(接收端 B)能收到至少 2 份相同的分组(p1、p2……)
  • 那么当原来的发送端 A 现在收到 B 发来的报文确认时,该如何判定是对哪一份的重复分组的确认(p1 还是 p2?)
  • 这种歧义影响到了 RTT 的测量

而 Karn 算法的核心很简单:就是当发生报文重传时,不对它进行 RTT 采样(既不参与运算)

RTO 定时器的维护

状态转移的感性认知还是比较简单的:

  • 启动:含数据的包首次发出,就启动重传定时器,达到 RTO 时间后超时
  • 重设:如果有 ACK,重传定时器重新设为当前 RTO
  • 关闭:如果全部发出的数据都 ACK,关闭定时器
  • 更新:下次重传时更新 RTO = RTO * 2,最大阈值可能是 60s
    • 这种做法称为 RTO backoff
    • 当发生 backoff 时,前面维护的 2 个变量可能清零,此后的计算仍需区分首次和后续两个阶段
  • 采样:如果有采样到 RTT,用前面的计算方式覆盖当前 RTO
    • 这种情况只发生在发出新的数据包和 ACK

安全性

作者好像说了什么,但好像也没说什么,总之当作没看到吧

文章太短,就不需要总结了