对于每连接,TCP管理4个不同的定时器:
1.重传定时器适用于当希望收到另一端的确认
2.坚持(persist)定时器使窗口大小信息保持不断流动,即使另一端关闭了其接收窗口
3.keepalive定时器可检测到一个空闲连接的另一端何时崩溃或重启
4.2ML定时器测量一个连接处于TIME_WAIT状态的时间
当TCL发送端检查到一段时间没有收到ACK,就会重新发送这个报文段。
TCP重传有 两套独立的方式完成
1.基于时间,发送数据后会启动一个计时器超时RTO,就会引发重传
2.基于确认信息的构成,比如收到了快速重传信息,或者选择确认信息SACK,则会引发重传
RTT测量
RTT(Round Trip Time)是指一个数据包从发送到确认的时间,也就是发送的时间t1,接收到ACK的时间t2,然后t2-t1就得到了RTT时间之后TCP会跟踪每个发送的数据包,如果超时了就会重发
RTO(Retransmission TimeOut)超时重传,由于网络环境不同,这个值是动态计算的,但如果计算时间短了就会频繁的重发,导致网络更多的拥塞;如果时间设置长了导致丢失很长时间才重发效率下降。
RTT的时间一般用定时器做计算,每500毫秒算一次(一个滴答),比如经过了550毫秒就算两个滴答,就计算为1000毫秒,如果是1.061秒就算作3个滴答。
RTO的经典算法如下:
1)先采样RTT,记下最近好几次的RTT值。
2)然后做平滑计算SRTT(Smoothed RTT)。公式为:(其中的 α 取值在0.8 到 0.9之间,这个算法英文叫Exponential weighted moving average,中文叫:加权移动平均)
SRTT = ( α * SRTT ) + ((1- α) * RTT)
3)开始计算RTO。公式如下:
RTO = min [ UBOUND, max [ LBOUND, (β * SRTT) ] ]
其中:
UBOUND是最大的timeout时间,上限值
LBOUND是最小的timeout时间,下限值
β 值一般在1.3到2.0之间。
Jacobson算法
前面两种算法用的都是“加权移动平均”,这种方法最大的毛病就是如果RTT有一个大的波动的话,很难被发现,因为被平滑掉了。最新的RTT的采样和平滑过的SRTT的差距做因子来计算。 公式如下:(其中的DevRTT是Deviation RTT的意思)
SRTT = SRTT + α (RTT – SRTT) —— 计算平滑RTT
DevRTT = (1-β)*DevRTT + β*(|RTT-SRTT|) ——计算平滑RTT和真实的差距(加权移动平均)
RTO= µ * SRTT + ∂ *DevRTT —— 最终值
在Linux下,α = 0.125,β = 0.25, μ = 1,∂ = 4
这个算法在被用在今天的TCP协议中
Karn算法
使用经典算法会碰到一些问题,假设发生了超时重传,那么
1.使用第一次发送的时间和收到的ACK作为RTT
2.使用重传的时间和收到的ACK作为RTT
很明显这里会存在问题,左边的情况,当发生重传了,如果使用第一次发送的时间后收到的ACK时间那么RTT时间就会变长;右边的情况,虽然发生了重传,但是对方其实已经收到了第一次的数据只是ACK回来慢了,此时如果使用第二次重传的时间和收到的ACK时间那么RTT的时间又短了。
Karn算法规定忽略重传,不把重传的RTT作为采样
但是这样一来又会有一个新的问题,如果此时网络突然拥塞导致数据丢包结果重传,但是此时RTO很小不会被更新,这样就出现问题了。Karn算法增加了一个条件如果出现重传则RTO时间翻倍,也就是说为的
Exponential backoff,但这样对要求比较精准的RTT则不合适。
Linux使用经典算法的改造版本
乘积带宽延迟
Round-Trip Time(RTT)往返时间
Retransmission TimeOut(RTO)重传超时
计算通道的容量为:
capacity(bit) = bandwidth(b/s) * round-trip time(s)
RTT加倍可使管道容量增加一倍,也就是可以多发一倍的数据
带宽加倍可使容量增加一倍,同样也是可以多发一倍的数据
基于计时器的重传
在设定的RTO内,TCP没有受到被计时器报文段的ACK就会出触发重传
当发生重传后TCP会降低当前发送率来对此快速响应
1.基于拥塞控制机制减小 发送窗口大小
2.每当一个重传保温段被再次重传时,则增大RTO的退避因子
当同一个报文段多次重传时,RTO的值乘上y来形成新的退避算法
ROT = y * RTO
y会呈加倍增长2,4,8等等,但不能超过TCP_ROT_MAX,一旦接受到相应的ACK,y会重置为1
重传是TCP连接提供的最后一道保障,但RTO的时间通常是RTT的2倍,会导致网络利用率下降
快速重传
基于接收端的反馈信息引发的重传
默认是收到了3个重复的ACK确认信息后会引发重传,linux可以根据当前环境动态调节重复的次数
发送端收到了3个重复的ACK后立刻重传,而不用等到超时,同样会触发拥塞控制机制
接收端发送的重复ACK表明他的滑动窗口中有不连续的空缺位
发送方根据第一次重传建立一个 恢复点,只有等接收到的序号大于或等于 恢复点时才会从重传中恢复
当网络中出现轻微的失序后,会出现重复的ACK,但不会到达重复的阈值,不会引发快速重传
只有当网络中失序比较严重后才会触发重复ACK的阈值,触发快速重传
带选择确认的重传
利用一个SACK可以在一个RTT时间内填补多个空缺
SACK在TCP头部有一个扩展,记录空缺的左边和右边的边界,如
SACK:25032-26601
目的度量
TCP连接会不断学习记录当前网络状态,但当TCP连接断开后这些信息就没了
较新的TCP实现会记录这些度量信息,下次再连接相同的网络地址时会使用之前的度量值
伪超时与重传
过早判定超时,失序,包重复,ACK丢失也可能会引发重传,这种的重传是伪重传
这会引发回退N行为
使用Eifel检测算法可以避免这些问题
还有前移RTO恢复算法
ICMP差错和重新分组
TCP能够遇到的最常见的ICMP差错就是源站抑制,主机不可达,网络不可达
1.一个接收到的源站抑制引起拥塞窗口cwnd被设置为1个报文段大小来发起慢启动,但是慢启动门限ssthresh没有变化,所以窗口将打开直至它或者开放了所有的通路(受窗口大小和往返时间的限制)或者发生了拥塞。
2.一个接收到的主机不可达或者网络不可达实际上都被忽略,因为这两个差错都被认为是短暂现象,这有可能是由于中间路由器被关闭而导致选录协议要花费数分钟才能稳定到另一个替换路由。
对端可能会因为不可达而由路由器返回ICMP差错,发送方经过N次重试后最终放弃。
当发送一个报文段A,没有成功(可能网络暂时故障),之后又发送了一个B,此时网络恢复可以发送了,TCP可能会将A和B合并成一个报文段一起发送
参考: