1、TCP一般拥有两套独立机制来完成重传,一是基于时间,即超时重传,而是基于确认信息的构成,即快速重传。
2、RTT(Round Trip Time) 往返时延,数据包从发出到收到对应ACK的时间,每一条连接都有独立的RTT。RTO(Retransmission Time Out)重传超时,即超时时间。RTT和RTO都是动态变化的。
3、超时重传的时间间隔是不一样的,一般采用的是二进制指数退避策略,比如第一次间隔3s,第二次6s,然后12s、24s ...
4、计算RTO的经典方法
最初的TCP规范采用如下公式计算得到平滑的RTT估计值(SRTT):
SRTT <-- α(SRTT) + (1-α)RTT
α称为平滑因子,推荐值为0.8~0.9,这种估算方法为指数加权移动平均或低通滤波器。RTO计算公式如下
RTO = min(ubound, max(lbound, (SRTT)β))
β为时延离散因子,推荐值为1.3~2.0,ubound为RTO的上边界,lbound为下边界。
这种方法的一个明显的确定是在RTT变化较大的网络中,RTO的反应较慢。
5、计算RTO的标准方法
标准方法相对经典方法稍复杂些,这里仅列出公式,知道当RTT变化时,偏差的增量越大,RTO增长越快,且一些计算因子都取2的N次方,方便计算机计算,提高效率。
srtt <--(1-g)srtt + g(RTT)
rttvar <-- (1-h)rttvar + h(|RTT-srtt|)
RTO = srtt + 4rttvar
增量g为新RTT样本占srtt估计值的权重,取为1/8,增量h为新平均偏差样本占偏差估计值rttvar的权重,取为1/4。
6、重传二义性
在测量RTT样本的过程中若出现重传,就可能导致某些问题。假设一个包的传输出现超时,该数据包会被重传,接着收到一个确认的信息,那么该信息是第一次还是第二次传输的确认就存在二义性。解决这个问题的办法就是接收到重传数据的确认信息时不能更新RTT估计值。这是Karn算法的第一部分。Karn算法的第二部分是对重传过程的退避系数加倍。
而在使用TCP时间戳选项的情况下,是可以避免二义性问题的,因此Karn算法的第一部分并不适用
7、RTT的测量和RTO的设置还有大量的改进方法,有兴趣可以查看相应文献
8、基于计时器的重传
若在连接设定的RTO内,TCP没有收到被计时报文段的ACK,将会触发超时重传。它通过降低当前数据发送率来对此进行快速响应。
一是基于拥塞控制机制减小发送窗口大小
二是每当一个重传报文段被再次重传时,增大RTO的退避因子,即Karn算法的第二部分。特别是当同一个报文段出现多次重传时,RTO值乘上γ来形成新的超时退避值
RTO = γRTO
通常环境下γ值为1,随着多次重传,γ呈加倍增长:2,4,8。当然也有最大值,Linux中通过设置TCP_ROT_MAX来控制RTO的最大值,默认120s。一旦收到对应的ACK,γ重置为1
9、快速重传
看一个现象:发送端发送了1,2,3,4四个包,接收端都正常收到了并回复了A2,A3,A4,A5,接着发送端又发了5,6,7,8四个包,包5由于丢失或延迟使得接收端先收到了6,7,8,此时会回应给发送端A5,A5,A5,发送端收到了3个重复的ACK,认为包5丢失了,不必等计时器超时了,马上重传包5,这称为快速重传。上面的那个3称为重复ACK阈值或dupthresh,dupthresh是个常量,一般设为3。也有一些非标准化的实现方法(包括Linux)可基于当前的失序程度动态调节该值。
10、带选择确认的重传(SACK)
SACK选项只出现在连接建立时的SYN包中。只有双端都支持SACK时才可使用。
接收端可以通过SACK告知发送端,缺失的是哪些包。如上面9中的例子,在没有SACK的情况下,发送端会重传5,6,7,8包,但有了SACK之后,接收端会告知发送端只缺少5,发送端则会只重传5。当然也并不是绝对的,某些情况下SACK也会失效。具体信息查看相应文献,这里不列举。
11、伪超时与重传、包失序与包重复、目的度量、重新组包