在深谈TCP/IP三步握手&四步挥手原理及衍生问题—长文解剖IP (11)

由于TCP看不到网络的状况,那么拥塞控制是必须的并且需要采用试探性的方式来控制拥塞,于是拥塞控制要完成两个任务:[1]公平性;[2]拥塞过后的恢复。

重介绍一下Reno算法(RFC5681),其包含4个部分:

    [1]慢热启动算法 – Slow Start 

    [2]拥塞避免算法 – Congestion Avoidance; 

    [3]快速重传 - Fast Retransimit; 

    [4]快速恢复算法 – Fast Recovery。

慢热启动算法 – Slow Start

我们怎么知道,对方线路的理想速率是多少呢?答案就是慢慢试。

开始的时候,发送得较慢,然后根据丢包的情况,调整速率:如果不丢包,就加快发送速度;如果丢包,就降低发送速度。慢启动的算法如下(cwnd全称Congestion Window):

连接建好的开始先初始化cwnd = N,表明可以传N个MSS大小的数据。

每当收到一个ACK,++cwnd; 呈线性上升

每当过了一个RTT,cwnd = cwnd*2; 呈指数让升

还有一个慢启动门限ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入"拥塞避免算法 - Congestion Avoidance"

根据RFC5681,如果MSS > 2190 bytes,则N = 2;如果MSS < 1095 bytes,则N = 4;如果2190 bytes >= MSS >= 1095 bytes,则N = 3;一篇Google的论文《An Argument for Increasing TCP’s Initial Congestion Window》建议把cwnd 初始化成了 10个MSS。Linux 3.0后采用了这篇论文的建议(Linux 内核里面设定了(常量TCP_INIT_CWND),刚开始通信的时候,发送方一次性发送10个数据包,即"发送窗口"的大小为10。然后停下来,等待接收方的确认,再继续发送)

在深谈TCP/IP三步握手&四步挥手原理及衍生问题—长文解剖IP

拥塞避免算法 – Congestion Avoidance

慢启动的时候说过,cwnd是指数快速增长的,但是增长是有个门限ssthresh(一般来说大多数的实现ssthresh的值是65535字节)的,到达门限后进入拥塞避免阶段。在进入拥塞避免阶段后,cwnd值变化算法如下:

每收到一个ACK,调整cwnd 为 (cwnd + 1/cwnd) * MSS个字节

每经过一个RTT的时长,cwnd增加1个MSS大小。

TCP是看不到网络的整体状况的,那么TCP认为网络拥塞的主要依据是它重传了报文段。前面我们说过TCP的重传分两种情况:

出现RTO超时,重传数据包。这种情况下,TCP就认为出现拥塞的可能性就很大,于是它反应非常'强烈'

调整门限ssthresh的值为当前cwnd值的1/2。

reset自己的cwnd值为1

然后重新进入慢启动过程。

在RTO超时前,收到3个duplicate ACK进行重传数据包。这种情况下,收到3个冗余ACK后说明确实有中间的分段丢失,然而后面的分段确实到达了接收端,因为这样才会发送冗余ACK,这一般是路由器故障或者轻度拥塞或者其它不太严重的原因引起的,因此此时拥塞窗口缩小的幅度就不能太大,此时进入快速重传。

在深谈TCP/IP三步握手&四步挥手原理及衍生问题—长文解剖IP

快速重传 - Fast Retransimit 做的事情有:

 调整门限ssthresh的值为当前cwnd值的1/2。

 将cwnd值设置为新的ssthresh的值

 重新进入拥塞避免阶段。

在快速重传的时候,一般网络只是轻微拥堵,在进入拥塞避免后,cwnd恢复的比较慢。针对这个,“快速恢复”算法被添加进来,当收到3个冗余ACK时,TCP最后的[3]步骤进入的不是拥塞避免阶段,而是快速恢复阶段。

在深谈TCP/IP三步握手&四步挥手原理及衍生问题—长文解剖IP

快速恢复算法 – Fast Recovery :

快速恢复的思想是“数据包守恒”原则,即带宽不变的情况下,在网络同一时刻能容纳数据包数量是恒定的。当“老”数据包离开了网络后,就能向网络中发送一个“新”的数据包。既然已经收到了3个冗余ACK,说明有三个数据分段已经到达了接收端,既然三个分段已经离开了网络,那么就是说可以在发送3个分段了。于是只要发送方收到一个冗余的ACK,于是cwnd加1个MSS。快速恢复步骤如下(在进入快速恢复前,cwnd 和 sshthresh已被更新为:sshthresh = cwnd /2,cwnd = sshthresh):

把cwnd设置为ssthresh的值加3,重传Duplicated ACKs指定的数据包

如果再收到 duplicated Acks,那么cwnd = cwnd +1

如果收到新的ACK,而非duplicated Ack,那么将cwnd重新设置为【3】中1)的sshthresh的值。然后进入拥塞避免状态。

细心的同学可能会发现快速恢复有个比较明显的缺陷就是:它依赖于3个冗余ACK,并假定很多情况下,3个冗余的ACK只代表丢失一个包。但是3个冗余ACK也很有可能是丢失了很多个包,快速恢复只是重传了一个包,然后其他丢失的包就只能等待到RTO超时了。超时会导致ssthresh减半,并且退出了Fast Recovery阶段,多个超时会导致TCP传输速率呈级数下降。出现这个问题的主要原因是过早退出了Fast Recovery阶段。为解决这个问题,提出了New Reno算法,该算法是在没有SACK的支持下改进Fast Recovery算法(SACK改变TCP的确认机制,把乱序等信息会全部告诉对方,SACK本身携带的信息就可以使得发送方有足够的信息来知道需要重传哪些包,而不需要重传哪些包),具体改进如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjfgz.html