因为网络类型的多样性和复杂性,以及中间系统使用TCP协议的范围不同,重传超时时间(Retransmission Timeout,RTO)必须动态地计算。
RFC 1122: Requirements for Internet Hosts 规定允许重传的包和原来的包一模一样 (数据边界,任何头部信息均未改变),因此,可能使用相同的IPv4标识字段(Identification Field)。
任何TCP实现均不能依赖该字段进行交互,因为该字段既不标识重复发送的TCP报文,也不用于标识重复接收的TCP报文。
1.1 重传算法
本部分内容基于 RFC 6298: Computing TCP’s Retransmission Timer 以及 TCP协议中的RTO计算方式分析 - Nut’s Blog
TCP使用 重传计时器
(Retransmission Timer) 来确保对未收到相应确认的数据的传输。该时间称为RTO(Retransmission TimeOut)
1.1.1 基本算法
为了计算当前的RTO,TCP发送端需要维护两个状态变量(State Variables):SRTT
(Smoothed round-trip time) & RTTVAR
(Round-Trip Time Variation,往返时间差异)
调整SRTT
,RTTVAR
,RTO
的规则如下:
F ( R T T ) − > R T O F(RTT)->RTO F(RTT)−>RTO , F ( ) F() F() 逻辑如下,我们假设, F ( ) F() F()会将一些关于当前连接的变量保存在RAM中。
-
当RTT的值在 发送端-接收端 之间的报文传输时被确定,发送端应该将RTO设置为 1 秒。注意,RFC 6298 文档之前的版本规定RTO应该被初始化为3秒;一些TCP实现可能仍然使用这个值,或者其他大于1的值。至于为什么将RTO设置的更小,一会说(1.1.6)
-
当
首个
(First)RTT的值 R 产生时,主机(Host)必须调整以下的内容S R T T = R SRTT=R SRTT=R
R T T V A R = R / 2 RTTVAR=R/2 RTTVAR=R/2
R T O = S R T T + m a x ( G , K × R T T V A R ) w h e r e K = 4 , G i s c l o c k g r a n u l a r i t y i n s e c o n d s RTO=SRTT+max(G,K\times RTTVAR)\space \space where\space K=4 ,G\space is\space clock\space granularity\space in\space seconds RTO=SRTT+max(G,K×RTTVAR) where K=4,G is clock granularity in seconds
关于G:G是一个常量,我们必须保证RTO的方差至少为G。后面还会提到1.1.3
-
若不是首个 RTT 值,则
$RTTVAR=(1-\beta)\times RTTVAR+\beta\times |SRTT-R’| $
S R T T = ( 1 − β ) × S R T T + α × R ′ SRTT = (1-\beta)\times SRTT+\alpha\times R' SRTT=(1−β)×SRTT+α×R′
其中, R T T V A R RTTVAR RTTVAR 计算所使用的左值 S R T T SRTT SRTT 为 S R T T SRTT SRTT 更新之前的值,即上述两个公式实现时必须确保其计算顺序。Congestion Avoidance and Control 建议参数为 α = 1 / 8 , β = 1 / 4 \alpha=1/8,\space \beta=1/4 α=1/8, β=1/4
在经过上述计算后,更新RTO: R T O = S R T T + m a x ( G , K × R T T V A R ) RTO=SRTT+max(G,K\times RTTVAR) RTO=SRTT+max(G,K×RTTVAR)
SRTT 与 RTTVAR 的计算是移动平均值算法。这样的方式也使得越久远的数据对当前结果影响越小,越近的数据对当前结果影响越大,既避免了网络状况抖动对估算结果的影响,又使得该数据尽可能反应当前网络状况
/* Given a new RTT measurement `RTT' */
if (/*RTT is the first measurement made on this connection*/) {
SRTT := RTT
RTTVAR := RTT / 2
RTO := SRTT + max(G, 2 * RTT) /* G is clock granularity in seconds */
// RTTVAR = RTT/2 , 4*RTTVAR = 2*RTT
} else {
SRTT' := SRTT + 1/8 * (RTT - SRTT)
RTTVAR' := 3/4 * RTTVAR + 1/4 * |RTT - SRTT|
RTO := SRTT' + max(G, 4 * RTTVAR')
}
- 无论何时,都应该确保RTO至少为1秒,如果真实计算的RTO小于1秒,则取上整为1秒。TCP实现使用 coarse-grained clocks (粗粒度时钟) 进行RTT测量,这导致RTO的最小值往往非常大,研究建议,应该让RTO的最小值大一点,这会使得TCP尽可能保守(conservative),并避免不必要的重传。因此,将RTO的最小值给大一点是一个比较
保守
的做法。也许未来的研究可能发现使用更小一点的值也是可接受的,甚至得到更优的性能。 - RTO 的最大值可以是至少 60 秒
1.1.2 RTT 采样
TCP实现时必须使用 Karn 算法 118544.118549 来采集RTT样本,因此不能根据TCP重传的数据包来生成RTT,当只有重传的数据包使用 Timestamp Option 时,才可以根据重传数据包来生成RTT。
TCP实现必须在同一时间生成一个RTT,然而使用 Timestamp Option 时,每一个ACK报文都可以作为一个RTT样本。RFC 1323: TCP Extensions for High Performance 建议 使用大拥塞窗口的
TCP连接应该为每个数据窗口使用多个RTT样本来避免RTT估计时的混叠效应
(aliasing effects).
在统计、信号处理和相关领域中,混叠是指取样信号被还原成连续信号时产生彼此交叠而失真的现象。 当混叠发生时,原始信号无法从取样信号还原。 而混叠可能发生在时域上,称做时间混叠,或是发生在频域上,被称作空间混叠
TCP 实现必须为每个RTT进行至少一次RTT测量。
对于较小的拥塞窗口,研究表明,测量每个报文并不会使得RTT更加准确。
1.1.3 Clock Granularity
没有必要使用 Clock Granularity
用于 RTT 与 RTTVAR 的计算。如果
K
×
R
T
T
V
A
R
K\times RTTVAR
K×RTTVAR 项计算值等于0,则方差必须四舍五入到 G 秒。即上述公式
R T O : = S R T T + m a x ( G , 4 ∗ R T T V A R ) RTO := SRTT + max(G, 4 * RTTVAR) RTO:=SRTT+max(G,4∗RTTVAR)
经验表明,更细粒度的 G 值(如 小于等于100 ms)使得算法有更好的性能。
Note: Jacobson, V. , Congestion Avoidance and Control 列出了其他技巧用于在 粗粒度的时钟(Coarse Granularity Timer) 下也能得出比较精确的结果,这些技巧在现代TCP实现中往往是必备的。
1.1.4 管理 RTO Timer
TCP 实现必须管理重传计时器(Retransmission Timers) ,以确保报文不会过早被重传。
以下是推荐的RTO管理算法:
Note: 计时器和计时器的状态是相互独立的,关闭计时器并不意味着清空计时器状态
- 每次包含数据的数据包被发送(也包括重传的),如果计时器没有运行,则启动它。并以上述(1.1.1)算法进行计时器状态初始化。
- 当所有发送的数据都被确认时,则关闭计时器
- 当收到确认新数据的ACK时,则重启计时器
当重传计时器超时时,执行以下步骤:
-
重传
最早的没有收到确认的报文
( The earliest segment that has not been acknowledged by TCP Receiver.) -
主机必须将 将RTO 设置为两倍 即 R T O : = R T O × 2 RTO :=RTO\times 2 RTO:=RTO×2 (退避计时器 , back off the timer)
上述说过,RTO有一个最大值,RTO的最大值至少是60秒 (1.1.1)
-
开启重传计时器,计时器将在 R T O × 2 RTO\times 2 RTO×2秒后超时
-
如果在三次握手期间等待对SYN的
ACK报文
超时了,并且TCP实现所使用的RTO小于3秒,那么当数据开始传输时,RTO必须重新初始化为3秒(即在三次握手之后)
注意,当重传成功后(即,成功发送并收到ACK),将产生新的RTT,此时执行1.1.1中所述的算法。
TCP实现 可能重置 RTT 和 RTTVAR (即清空计时器状态)在若干次对计时器进行back off (退避)后,因为退避若干次后的RTO可能不再适用于当前的网络环境。重置后,应该使用首次连接建立连接时
的算法进行初始化。
1.1.5 安全性考虑
TCP需要在重传未确认前等待一段时间,攻击者可以通过让数据包延迟从而让TCP发送端计算出很大的RTO,然而添加额外的延迟往往与报文丢失的情况相吻合,因此,除了丢弃部分 TCP 连接数据包外,我们很难看出攻击者能从这种可能对网络造成更大破坏的
攻击中获得什么。
攻击者还可能造成TCP节点在网络拥堵条件下更加频繁地发送数据——伪造ACK报文。从而使得发送方将RTO降低到不安全的值。能做到如此是极为困难的,需要正确地构建ACK报文,这就需要攻击者实时监测数据流。相比于其他的攻击手段,这种攻击造成的影响不大。因为实际是,网络确实拥堵,TCP发送端仍然会因为拥塞导致收到不正确的数据包而 back off 其超时计时器。
back off : R T O : = 2 × R T O RTO := 2\times RTO RTO:=2×RTO
1.1.6 为什么建议使用更小的值初始化RTO
选择合适的初始RTO是需要考虑两个竞态条件的:
1. RTO 应该足够大,来避免网络的故障或者拥堵的时间段(几秒?几十秒?),因为如果RTO不够大,那么你发送的数据包还是会因为网络问题而重传
2. RTO一方面应该足够小,以确保接收方尽快地收到丢失的报文。
其他之前的文档
习惯将TCP的RTO初始值设置为3s,那么这里(RFC 6298)呼吁将改值设置为更小的1s。
RFC 1122: Requirements for Internet Hosts - Communication Layers & RFC 2988: Computing TCP’s Retransmission Timer
原因如下:
-
现代普通的通信网络的速率与之前
定义RTO为3s时代的最牛逼(State-of-the-art) 的
网络相比,已经快了不少。那个时代,按照1122、2988文档发布时间来看,指的是(1989-2000年)
-
大规模调查研究发现,现代 97.5 % 97.5\% 97.5% 的网络连接的 RTT (往返时间)小于1s。
-
另外,研究还发现 三次握手(3WHS)的期间报文重传几率大概为 2 % 2\% 2% ,所以减少RTO的初始值连接性能的显著提升。
-
Tuning TCP Parameters for the 21st Century 的报告显示,只有约 2.5 % 2.5\% 2.5% 的TCP连接的RTO值大于 1s。对于这些连接,1 秒的初始 RTO 可保证在连接建立期间进行一次重传(无论是否需要)。
当出现这种情况时,这里要求在数据传输阶段恢复到 3 秒的初始 RTO。对于这种性能较低或环境较差的的网络,显然 1秒的RTO势必会造成不必要的重传,但其实影响不大。