TCP的RTT(往返时间)估计与Jacobson/Karels算法详解
一、问题描述
在TCP可靠传输中,发送方需要知道一个数据包从发出到收到其确认(ACK)所经历的往返时间(Round-Trip Time, RTT),以设置合理的重传超时(Retransmission Timeout, RTO) 值。RTO设置过短会导致不必要的重传,加剧网络拥塞;设置过长则会使丢包后的响应变慢,降低吞吐量。早期的TCP使用一个简单的平均RTT估计算法,但在面对RTT方差较大的网络时性能不佳。Jacobson/Karels算法 对此进行了关键改进,引入了对RTT变化的估计(RTTVAR),使RTO能更动态、更准确地适应网络延迟的变化。本知识点将详解RTT估计的核心挑战、经典算法以及Jacobson/Karels算法的原理与计算步骤。
二、背景与核心挑战
- 基本概念:RTT指从发送一个TCP报文段到接收到其对应的ACK所经过的时间。由于网络路径、拥塞状况时刻变化,RTT并非固定值,而是不断波动的。
- 核心目标:根据测量的RTT样本,估算出“平滑的RTT”和“RTT的变化范围”,从而计算出一个既能避免过早重传又能及时响应丢包的RTO。
- 经典算法(RFC 793)及其问题:
- 公式:
- 平滑RTT:
SRTT = α * SRTT + (1 - α) * RTT样本(α通常为0.875,即平滑因子高)。 - 重传超时:
RTO = β * SRTT(β通常为2)。
- 平滑RTT:
- 问题:此算法只估算了平均RTT,未考虑其变化(方差)。在RTT变化剧烈的网络中,固定的β倍SRTT作为RTO,要么因设置过小而频繁误重传,要么因设置过大而响应迟钝。
- 公式:
三、Jacobson/Karels算法详解(RFC 6298)
该算法在估算平滑RTT(SRTT)的基础上,新增了对RTT变化(RTTVAR)的估计,使RTO = SRTT + 4 * RTTVAR,能更灵活地跟随网络状况。
步骤1:初始化
在建立TCP连接、尚未获得第一个RTT测量样本时,需要设置初始值。假设初始RTO为1秒(一个典型值,如Linux中为1秒)。
- 令
SRTT= 未定义。 - 令
RTTVAR= 未定义。 - 令
RTO= 1秒。
步骤2:获得首个RTT测量样本
当发送第一个数据包并成功收到其ACK后,我们得到了第一个RTT测量值,记作 R。
- 这是算法的关键转折点,用第一个样本初始化两个状态变量:
SRTT = RRTTVAR = R / 2(将第一个样本值的一半作为初始变化估计是合理启发)
- 然后计算第一个正式的RTO:
RTO = SRTT + max(G, K * RTTVAR)。其中G是时钟粒度(通常很小,如1ms),K是倍数因子,通常为4。因此常见公式为:RTO = SRTT + 4 * RTTVAR。
步骤3:获得后续RTT测量样本
对于第二个及之后的每个有效RTT样本 R,按以下顺序更新状态变量和RTO。这里引入一个增益因子 α 通常为1/8(0.125),β 通常为1/4(0.25),用于控制新旧值的权重。
-
计算RTT的变化误差:本次样本
R与当前平滑估计SRTT的差值,代表了本次测量的“惊喜”程度。Err = R - SRTT
-
更新平滑RTT估计(SRTT):用当前误差的一部分来平滑地调整SRTT,使其缓慢地向最新样本靠近。
SRTT = SRTT + α * Err(即SRTT = SRTT + (1/8) * Err)- 这等价于
SRTT = 7/8 * SRTT + 1/8 * R。α=1/8意味着新样本的权重较低,估计值对瞬时波动不敏感,更稳定。
-
更新RTT变化估计(RTTVAR):这是算法的精髓。我们用误差的绝对值(因为变化可以是正或负,我们关心幅度)来平滑地更新对RTT变化范围的估计。
RTTVAR = RTTVAR + β * (|Err| - RTTVAR)(即RTTVAR = RTTVAR + (1/4) * (|Err| - RTTVAR))- 这等价于
RTTVAR = 3/4 * RTTVAR + 1/4 * |Err|。β=1/4意味着变化估计的更新比平均值稍快一些,以更好地跟踪波动。
-
计算新的RTO:基于更新后的SRTT和RTTVAR,重新计算RTO。同时,RFC规定了RTO的下界和上界,确保其合理性。
RTO = SRTT + max(G, K * RTTVAR),其中K=4。- 简化常用形式:
RTO = SRTT + 4 * RTTVAR。 - 边界限制:
RTO必须不小于某个最小值(如RFC 6298建议的1秒),同时,实现中通常也会设置一个最大值(如TCP规范建议的120秒)。即:RTO = max(下限, min(计算值, 上限))。
步骤4:处理重传二义性与Karn算法
当一个数据包被重传后,收到的ACK是针对第一次发送还是重传的包?这存在二义性。如果使用这个模糊的RTT样本,可能会严重干扰SRTT和RTTVAR的估计。
- Karn算法规则:对于重传过的数据包,不采用其RTT样本(即不用于更新SRTT和RTTVAR)。
- 退避策略:在发生重传时,RTO会采用指数退避方式增大(例如,
RTO = RTO * 2),以快速应对可能出现的严重拥塞。直到一个未经重传的数据包被成功确认后,才恢复正常RTT采样和RTO计算。
四、总结与示例
假设初始RTO=1秒,获得第一个RTT样本R1=0.5秒。
- 初始化:SRTT = 0.5秒, RTTVAR = 0.5/2 = 0.25秒。
- 计算RTO = 0.5 + 4*0.25 = 1.5秒。
- 获得第二个样本R2=0.8秒。
- Err = 0.8 - 0.5 = 0.3秒。
- SRTT = 0.5 + 0.125*0.3 = 0.5375秒。
- RTTVAR = 0.25 + 0.25*(|0.3| - 0.25) = 0.25 + 0.25*0.05 = 0.2625秒。
- RTO = 0.5375 + 4*0.2625 = 1.5875秒。
Jacobson/Karels算法的核心优势在于,它不仅跟踪RTT的平均值(SRTT),还通过RTTVAR跟踪其波动性。当网络延迟稳定时,RTTVAR小,RTO接近SRTT;当网络抖动大时,RTTVAR增大,RTO也随之自动增大,为重传预留更多时间,从而显著提升了TCP在各种动态网络环境下的健壮性和效率。它是现代TCP实现中RTT估计和RTO计算的标准算法。