TCP/IP 协议——十四章:TCP超时与重传
由于下层网络层(IP)可能出现丢失、重复或失序包的情况,TCP 协议提供可靠数据传输服务。为保证数据传输的正确性,TCP 重传其认为已经丢失的包。TCP 有两套重传机制,一是基于定时器(超时),二是基于确认信息的构成(快速重传)。
基于计时器的重传
TCP在发送数据时会设置一个计时器,若至计时器超时仍未收到数据确认信息(ACK),则会引发相应的超时或基于计时器的重传操作,计时器超时称为重传超时(Retansmission Timeouts,RTO)。
1、设置重传超时(RTO)
凭我们的直觉,RTO 应该比 RTT 稍大:
RTO=RTT+Δt
那么,RTT 怎么算呢:
SRTT=α(SRTT)+(1-α)RTTnew
SRTT(smooth RTT),RTTnew 是新测量的值。如上,为了防止 RTT 抖动太大,给了一个权值 a ,也叫平滑因子。a 的值建议在 80%~90%。举个例子,当前 SRTT=200ms,最新一次测量的 RTTnew=800ms,那么更新后的 SRTT=200×0.875+800×0.125=275ms.
根据前面求的SRTT计算出RTO:
RTO = min(ubound, max(lbound, (SRTT)β))
β是时延离散因子,推荐值1.3~2.0。 ubound是RTO上边界,lbound是下边界。这种计算方法就是经典方法。它是的RTO值设置为1s或约两倍的SRTT。
这种方法缺点就是没法适应大规模的变动(网络不稳定情况)。
2、退避指数
根据前面的公式,我们可以得到 RTO。一旦超过 RTO 还没收到 ACK,就会引起发送方重传。但如果重传后还是没有在 RTO 时间内收到 ACK,这时候会认为是网络拥堵,会引发 TCP 拥塞控制行为,使 RTO 翻倍。则第 n 次重传的 RTOn 值为:
RTOn=2^(n−1)×RTO1
下图是一个例子:
3、带时间戳的 RTT 测量
前面说了 RTO 的公式,它和 RTT 有关,那么每一次的 RTT 是如何得到的呢?
在之前 TCP 连接管理的时候讲过,TCP有一个 TSOPT (timestamp option) 选项,它包含两个时间戳值。它允许发送者在报文中带上一个32比特的时间戳值(TSV),然后接收方将收到的值原封不动的填入 ACK 报文段中 TSOPT 选项的第二部分,时间戳回显字段(TSER)。发送方收到 ACK 以后,将当前时间戳减去 TSOPT 选项的 TSER 就可得到精确的RTT值。
4、重传二义性与 Karn 算法
还有另一个重要的细节,如果测量 RTT 的样本出现了超时重传,导致我们收到 ACK 时无法分辨是对哪一次的确认,这时候 RTT 的值可能是不正确的。
因此,Karn 算法规定:此时不更新 RTTnew 的值。并且如果发生再次重传,则采用退避后的 RTO 的值,直到发送成功,退避指数重新设定为 1 。
基于确认信息的重传(快速重传)
在大多数情况下,计时器超时并触发重传是不必要的,也不是期望的,因为 RTO 通常是大于 RTT(约2倍或更大),因此基于计时器的重传会导致网络利用率降低。
快速重传机制基于接收端的反馈信息来引发重传,与超时重传相比,快速重传能更加及时有效的修复丢包情况。
如下图所示:
1、带选择确认的重传
TCP发送段的任务是通过重传丢失的数据来填补接收端缓存中的空缺,但同时也要尽可能保证不重传已正确接收到的数据。在很多环境下,合理采用SACK(Selective Acknowledgment)信息能更快地实现空缺填补,且能减少不必要的重传,原因在于其在一个RTT内获知多个空缺(最多3个)。
2、接收端的 SACK 行为
接收端在 TCP 连接建立期间收到 SACK 选项即可生成 SACK。通常来说,当收到失序报文段,接收方就会生成 SACK。
第一个 SACK 块包含的应该是最近收到的(most recently received)报文段的序列号范围。由于 SACK 选项空间有限,应尽可能向发送方提供最新信息。其余的 SACK 按先后顺序依次排列,也就是说,该 ACK 报文段除了包含最新接收的序列号信息,还应重复之前的 SACK 信息。这是因为 ACK 报文段是没有重发机制的,可能会丢失,重复提高了其鲁棒性。
3、发送端的 SACK 行为
发送方应该充分利用 SACK 信息来进行重传,称为选择性重传。发送方记录累积 ACK 信息和 SACK 信息,当接收到相应序列号范围内的 ACK 时,在其重传缓存中标记该报文段重传成功。
伪超时与重传
在很多情况下,即使没有出现数据丢失也可能引发重传。这种不必要的重传称为伪重传(spurious retransmission),其只要原因是伪超时(spurious timeout),即过早判定超时,其他因素如包失序、包重复,或ACK丢失也可能导致该现象。在实际RTT显著增长,超过当前RTO时,可能出现伪超时。