对于每连接,TCP管理4个不同的定时器:
1.重传定时器适用于当希望收到另一端的确认
2.坚持(persist)定时器使窗口大小信息保持不断流动,即使另一端关闭了其接收窗口
3.keepalive定时器可检测到一个空闲连接的另一端何时崩溃或重启
4.2ML定时器测量一个连接处于TIME_WAIT状态的时间
当TCL发送端检查到一段时间没有收到ACK,就会重新发送这个报文段。
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之间。
Karn算法
使用经典算法会碰到一些问题,假设发生了超时重传,那么
1.使用第一次发送的时间和收到的ACK作为RTT
2.使用重传的时间和收到的ACK作为RTT
很明显这里会存在问题,左边的情况,当发生重传了,如果使用第一次发送的时间后收到的ACK时间那么RTT时间就会变长;右边的情况,虽然发生了重传,但是对方其实已经收到了第一次的数据只是ACK回来慢了,此时如果使用第二次重传的时间和收到的ACK时间那么RTT的时间又短了。
Karn算法规定忽略重传,不把重传的RTT作为采样
但是这样一来又会有一个新的问题,如果此时网络突然拥塞导致数据丢包结果重传,但是此时RTO很小不会被更新,这样就出现问题了。Karn算法增加了一个条件如果出现重传则RTO时间翻倍,也就是说为的
Exponential backoff,但这样对要求比较精准的RTT则不合适。
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协议中
乘积带宽延迟
Round-Trip Time(RTT)往返时间
Retransmission TimeOut(RTO)重传超时
计算通道的容量为:
capacity(bit) = bandwidth(b/s) * round-trip time(s)
RTT加倍可使管道容量增加一倍,也就是可以多发一倍的数据
带宽加倍可使容量增加一倍,同样也是可以多发一倍的数据
慢启动和拥塞控制
慢启动为发送方的TCP增加了一个窗口:拥塞窗口(congestion window),记做 cwnd
当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段(即另一端通告的报文段大小)。每
收到一个ACK,拥塞窗口就增加一个报文段(cwnd以字节为单位,但是慢启动以报文段大小为单位进行增
加)。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。
拥塞窗口是发送方使用的流量控制
滑动窗口是接收方使用的流量控制
慢启动的时间流逝图如下(使用指数增加的方式,慢慢的增加,如果网速很快,增长的也会很快)
拥塞避免算法和慢启动算法需要对每个连接维持两个变量: 一个拥塞窗口cwnd和一个慢启动门限ssthresh,这样得到的算法工作过程如下:
1.对一个给定的连接,初始化cwnd为1个报文段,ssthresh为65535个字节。
2.TCP输出列程的输出不能超过cwnd和接收方通告窗口的大小。
3.当拥塞发生时(超时或者受到重复确认),ssthresh被设置为当前窗口大小的一般(cwnd和接收方通告窗口大小的最小值,但最少为2个报文段)。此外如果是超时引起了拥塞,则cwnd被设置为1个报文段。
4.当新的数据被对方确认没增加cwnd,但增加的方法依赖于我们是否进行慢启动或拥塞避免。如果cwnd小于或等于ssthresh,则正在进行慢启动,否则正在进行拥塞避免。
对于慢启动的计算方式:
1.初始的cwnd=1,此时可以发送1个字节的MSS
2.每收到一个ACK,cwnd++,也就是线性增长
3.每经过一个RTT,也就是指数增长
4.当cwnd>=ssthresh时,就会进入 拥塞避免 算法
拥塞避免的计算方式:(慢启动阀值slow start threshold)
当cwnd>=ssthresh时,就进入拥塞避免状态
1.每收到一个ACK时,cwnd = cwnd + 1/cwnd
2.每过一个RTT时,cwnd = cwnd + 1
所以这是一个线性增长的算法
拥塞恢复算法:
1.设置sshthresh = cwnd /2
2.设置cwnd 重置为 1
3.进入慢启动过程
快速恢复算法:
1.设置cwnd = cwnd /2
2.设置sshthresh = cwnd
3.cwnd = sshthresh + 3 * MSS (3的意思是确认有3个数据包被收到了)
4.重传Duplicated ACKs指定的数据包
5.如果再收到 duplicated Acks,那么cwnd = cwnd +1
6.如果收到了新的Ack,那么,cwnd = sshthresh ,然后就进入了拥塞避免的算法了。
但是快速恢复算法依赖于三个重复的ACK,但是收到3个重复的ACK可能不止丢一个包可能就很多,而那些需要等到RTO超时才会重传
ICMP差错和重新分组
TCP能够遇到的最常见的ICMP差错就是源站抑制,主机不可达,网络不可达
1.一个接收到的源站抑制引起拥塞窗口cwnd被设置为1个报文段大小来发起慢启动,但是慢启动门限ssthresh没有变化,所以窗口将打开直至它或者开放了所有的通路(受窗口大小和往返时间的限制)或者发生了拥塞。
2.一个接收到的主机不可达或者网络不可达实际上都被忽略,因为这两个差错都被认为是短暂现象,这有可能是由于中间路由器被关闭而导致选录协议要花费数分钟才能稳定到另一个替换路由。
对端可能会因为不可达而由路由器返回ICMP差错,发送方经过N次重试后最终放弃。
当发送一个报文段A,没有成功(可能网络暂时故障),之后又发送了一个B,此时网络恢复可以发送了,TCP可能会将A和B合并成一个报文段一起发送
参考: