前言
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 RTTRTTVAR
: 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
安全性
作者好像说了什么,但好像也没说什么,总之当作没看到吧
完
文章太短,就不需要总结了