传输层协议TCP—拥塞控制(12)

1 拥塞控制简介

      拥塞控制讲述的则是从如何避免网络拥塞的视角或者网络已经拥塞的情形下,TCP 对应的算法和处理机制。TCP 拥塞控制(对应 RFC 5681)包括4个算法(机制):慢速启动、拥塞避免、快速重传、快速恢复。

慢速启动和拥塞避免

     慢速启动和拥塞避免,是成对出现的两个算法。我们先介绍一个名词:cwnd(拥塞窗口)。我们知道,TCP 发送方可以连续发送多个 Segment,而不必等待对方的 ACK。理想情况下,TCP 甚至可以连续发送完最大窗口(230)所包含的字节。但是在拥塞控制这个场景下,TCP 不能闭着眼连续发送多个报文,因为这很可能会造成网络拥塞。

       从理论上讲,即使没有拥塞控制一说,TCP 也不能永远发送下去,中间也必须要换弹夹(收到ack报文),因为弹夹的容量是有限的(发送窗口最大是 2的30次幂,一次最多能连续发送2的31次幂个字节)。如果我们把这个最大窗口看成巨无霸弹夹的话,cwnd 存在的意义就是为了避免 TCP 一上来就闭着眼把巨无霸弹夹里的子弹一扫而光,这就是所谓的拥塞控制场景(之一)。当刚刚创建完连接或者长时间空闲,TCP 对于网络情形几乎是一无所知,此时它如果要发送数据,就有必要考虑网络是否拥塞:如果网络是拥塞的,TCP 将巨无霸弹夹一扫而光只会带来“丢包/重传/丢包/重传......”的恶性循环,它必须得悠着点,换1个较小的弹夹;但是如果网络是不拥塞的,TCP 却一直采用较小的弹夹,这又会影响发送效率。

       上述这种换弹夹的操作只是举了个例子。慢速启动和拥塞避免,就是边发送数据边“探测”网络拥塞状况边动态调整 cwnd(弹夹)的过程,如图5-141所示:

        

       ssthresh(slow start threshold)指的是慢速启动门限值。当 cwnd < ssthresh 时,cwnd 随时间变化的函数称为慢速启动;当 cwnd > ssthresh 时,cwnd 随时间变化的函数称为拥塞避免。至于 cwnd = ssthresh 时,cwnd 的变化可以选择两个函数(算法)的任意1个。需要注意的一点是慢速启动其cmnd相比与拥塞避免阶段而言,其增速要快于拥塞避免阶段。因此,慢速启动指的并不是 cwnd 的变化率,而是 cwnd 的初始值。

2.1 初始拥塞窗口

       初始拥塞窗口(IW,Initial Window)指的是图5-141中的 IW 点,本质上是对网络的一种试探(探测)。毕竟,如果网络是拥塞的,一下子发送那么多,与己与人(网络)都没有好处。与己:只会产生丢包、重传;与人(网络):只会增大网络的拥塞程度。

      1、RFC 5681 关于 IM 的定义是这样的:

  • (1)If SMSS > 2190 bytes:IW = 2 * SMSS bytes and MUST NOT be more than 2 segments;
  • (2)If (SMSS > 1095 bytes) and (SMSS <= 2190 bytes),IW = 3 * SMSS bytes and MUST NOT be more than 3 segments;
  • (3)if SMSS <= 1095 bytes,IW = 4 * SMSS bytes and MUST NOT be more than 4 segments;

     SMSS(SENDER MAXIMUM SEGMENT SIZE)这个概念与 MSS(Maximum Segment Size)没有本质区别,只不过前面加了一个限定词 SENDER,强调是发送方所能发送的最大 Segment 的 Size。

     2、Linux 3.0 就是按照google论文的建议,将 IW 设置为 10 * SMSS。

      无论按照哪种方式,我们可以粗略的把 IW 理解为 4K(或者10K)。前文说过,如果 TCP 的 Window 不放大(没有 Window Scale Option),则 Window 最大可以为64K,如果考虑放大系数,则 Window 最大可以为1G。而 IW 仅仅相当于 4K(或者10K),可以看到它确实比较小。在随后的数据发送过程中,如果网络比较通畅,cwnd 还是会比较快速地增大。

2.2 慢速启动算法

       慢速启动算法,首先设置1个门限值 ssthresh,当 cwnd 小于(等于) ssthresh 时,cwnd 的增大算法就是慢速启动算法。慢速启动算法也比较简单,就是:每当发送方收到1个相应的 ACK 报文时,cwnd 增加 SMSS 字节,即 cwnd = cwnd + SMSS,如图5-142所示:

      

       1个 RTT 时间,增加 1个 SMSS ,实际上并不慢。我们假设 SMSS = 1K,IW = 1K,从发送 cwnd 个字节的数据到收到 ACK 报文为0.01秒,那么经过1万秒以后,CWND 将达到 1G(对应的理论带宽是 1Tbps(传输距离是1500km)),或者说10秒以后,理论带宽将达到 1Gbps(传输距离是1500km)——这已经是非常快的增长速度。

       当 cwnd 达到 ssthresh 时,cwnd 的增长速度就会变慢(即采用拥塞避免算法)。ssthresh 应该等于多少?拥塞避免算法又是什么?这两部分放在后面进行讲解。这里继续看对慢速启动算法的修正。

1、修正方案1

       cwnd = cwnd + min(N, SMSS)。其中,N 为图5-142中 ACK 报文中所确认的数据字节数。例如,考虑这样一种情形,如图5-143所示:

       

       图5-143中,假设 SMSS = 1000,A 给 B 发送了1个报文 SEG1,包含了1000个字节的数据,但是 B 对于这1000个字节的数据是分批确认,比如图5-143中的 ACK 报文,只确认了500个字节。于是 TCP 就只将 cwnd 增大500。RFC 5681之所以这么做,一个是为了适应这种分批确认的情形,另一个也是处于相对保守的目的,不让 cwnd 增加得太快。

2、修正方案2

      它规定:每经过1个 RTT 时间,cwnd = 2 * cwnd。表面上看这仅仅是乘以2,实际上它是“每次都加倍”,也就是说 cwnd 其实是一个指数级的增长。假设不考虑 ssthresh,那么 cwnd 从1K增加到1G,也只需要20次 RTT——如果1次 RTT 是0.01秒,也就需要0.2秒。

2.3 拥塞避免算法

     拥塞避免算法,可以分为两部分:

  • (1)从 cwnd 大于(等于) ssthresh 开始,到网络未拥塞的这段时间内,cwnd 的增长算法。
  • (2)当拥塞开始后,cwnd 和 ssthresh 的调整算法。

      那么什么叫拥塞呢?对于 TCP 来说,就是它“感觉”到第1个丢包。也就是说,发送了一个(批)报文以后,时间已经超过了 RTO,但是 TCP 发送方还是没有收到对方的相应的 ACK 报文。在第1阶段(cwnd 大于(等于) ssthresh,而且网络还未拥塞),RFC 5681 关于 cwnd 的增长算法,并没有严格规定哪个公式,而是有几个建议:

  • (1)在1个 RTT 内,cwnd = cwnd + SMSS
  • (2)或者在1个 RTT 内,cwnd = cwnd + min(N, SMSS)

      这与其建议的慢速启动算法并无本质区别。幸好,RFC 5681 还有1个建议:

  • (3)在1个 RTT 内,cwnd = cwnd + min(SMSS * SMSS / cwnd, 1)

       因为“SMSS * SMSS / cwnd”有可能小于1,所以用“min(SMSS * SMSS / cwnd, 1)”进行修正,表示最少增加1个字节。针对以上(1)(2)(3)的RFC5681的三个拥塞避免算法,简化(省略掉了 min、max)后总结如下:

        

2.4 拥塞避免算法:ssthresh 和 cwnd 的调整

       当 TCP 遇到拥塞时,cwnd 肯定不能再继续增加了。不仅不能继续增加 cwnd,而且 ssthresh 也需要调整,如下图5-144所示:

       

     t1时刻,TCP 发送端感知到了阻塞,这时候它需要做3件事情:

  • 第1件事,调整 ssthresh。上图5-114中,ssthresh0 是初始化的 ssthresh,ssthresh1 是调整后的 ssthresh。调整算法是:ssthresh = max(FlightSize/2, 2 * SMSS)。其中,FlightSize 指的是 TCP 已经发送的但是还未得到 ACK 的数据的 size(字节数)。
  • 第2件事,cwnd 重新初始化(上图5-144中的 IW1)。IW1 具体等于多少,仍然是一个经验数据,RFC 5681 并没有明确规定,只是它强调,IW1 不能大于 SMSS。这么重新设置 IW 背后的含义仍然是:既然阻塞了,那就低到尘埃,尽量将 cwnd 调到最低。
  • 第3件事情,根据网络状况增大 cwnd。如何增大呢?就是继续重复慢速启动算法、拥塞避免算法......直到下一次的网络拥塞再次来到。

     总的来说,网络拥塞后,TCP 所做的事情其实是(1)调整 ssthresh 和 cwnd 的初始值;(2)然后继续重复慢速启动算法、拥塞避免算法......

     

       按上图来看,做了一些简化,方便提现慢启动和拥塞避免的循环。(1)将每次拥塞发生时的 cwnd 简化为相同;(2)将每次拥塞发生时的 ssthresh 简化为相同;(3)将每次循环减缓为相同的周期(实际上每次循环的时间间隔都基本不可能相同)。之所以做这些简化,是为了能更加直观地体现慢速启动和拥塞避免的循环。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值