(六)深入浅出TCPIP之TCP拥塞控制

目录

 

什么是网络拥塞

如何避免拥塞

拥塞点

避免拥塞

慢启动算法

算法思想

慢启动解析

启动过程

拥塞避免算法

启动过程

快速重传算法

快速恢复

总结

连环发问:


专栏其他文章:

 

理论篇:

(一)深入浅出TCPIP之理解TCP报文格式和交互流程

  (二)深入浅出TCPIP之再识TCP,理解TCP三次握手(上)

  (三)深入浅出TCPIP之再识TCP,理解TCP四次挥手(上)

  (四)深入浅出TCPIP之TCP三次握手和四次挥手(下)的抓包分析

  (五)深入浅出TCPIP之TCP流量控制

  (六)深入浅出TCPIP之TCP拥塞控制

  (七)深入浅出TCPIP之深入浅出TCPIP之TCP重传机制

 (八)深入浅出TCPIP之TCP长连接与短连接详解

 (九)深入浅出TCPIP之网络同步异步

 (十)深入浅出TCPIP之网络阻塞和非阻塞

(十一)深入浅出TCPIP之TCP粘包问题

  (十二)深入浅出TCPIP之Nagle算法

  (十三) 深入浅出TCPIP之TCP套接字参数

  (十四)深入浅出TCPIP之初识UDP理解报文格式和交互流程

  (十五)非常全面的TCPIP面试宝典-进入大厂必备总结

 (十六)深入浅出TCPIP之Hello CDN

 ....

(二十)深入浅出TCPIP之epoll的一些思考

实践篇:

   深入浅出TCPIP之实战篇—用c++开发一个http服务器(二十一)

其他实践篇+游戏开发中的网络问题疑难杂症解读 正在完善。。。

什么是网络拥塞

当网络中存在过多的数据包时,网络的性能就会下降,这种现象称为拥塞
在网络发生拥塞时,会导致吞吐量下降,严重时会发生“拥塞朋溃" (congestion clpse)现象。 

拥塞产生
       崩溃发生在网络负载增加而导致网络效率降低的时候。如图1所示,源端A发送数据给目的端B,途经若干中间节点,而中间节点不只为A和B服务,还为甲、乙、丙服务,如果甲、乙。丙没有发送数据的时候,整体链路为A和B独有,当它们开始发报文时,也不会通知A和B.那么为A-B服务的通道带宽会变小,当达到一定程度后,链路上的中间节点就出现丢包,根据传统IP行为其QoS(Quality of Service,服务质量)能力较弱。这时B发现有丢包,于是通知A重发,这个通知重发,这个通知重发的报文也占带宽,通知重发报文到达A之后,A不得已真的重发,于是反复多次,带宽终于被这种没有意义的重发报文占满。这导致了A. B及甲、乙、丙也得不到正常的网络服务,其RI和R2之间的有效带宽远远达不到实际的物理带宽,对客户的网络投资是一种极大的浪费。

 

如何避免拥塞

拥塞点

实际的网络环境是非常复杂的,每一跳路由的带宽可能都不一样,如果一下接收到太多的数据网络就会发送拥塞,拥塞的结果就是丢包。导致网络拥塞的数据量称为拥塞点。 拥塞点是一个在不断变化的动态值,TCP需要在尽可能大的发送窗口下,避免触碰到拥塞点。

避免拥塞

TCP主要使用以下几种算法来避免拥塞的发生:

  • 慢启动算法
  • 拥塞避免算法
  • 快重传算法
  • 快恢复算法

慢启动算法

算法思想

慢启动算法的思想是为发送方增加了一个拥塞窗口(Congestion Window),记为cwnd。

        拥塞窗口指的是在收到对端的ACK时还能发送的最大MSS数。拥塞窗口是发送端维护的一个值,不会像接收方窗口(rwnd)那样通告给对端,发送方窗口的大小是cwnd和rwnd的最小值。目前的linux的拥塞窗口初始值为10个MSS。

        慢启动算法,每经过一个RTT,cwnd变为之前的两倍。发送方开始时发送initcwnd个报文段(假设接收方窗口没有限制然后等待ACK。当收到该ACK时,拥塞窗口扩大为initcwnd*2,即可以发送initcwnd*2个报文段。当收到这发出报文段的ACK时,拥塞窗口继续扩大为initcwnd*4,这是一种指数增加的关系。

慢启动解析

          发送方一开始便向网络发送多个报文段,直至达到接收方通告的窗口大小为止。当发送方和接收方处于同一个局域网时,这种方式是可以的。但是如果在发送方和接收方之间存在多个路由器和速率较慢的链路时,就有可能出现一些问题。一些中间路由器必须缓存分组,并有可能耗尽存储器的空间。
         现在,TCP需要支持一种被称为“慢启动(slow start)”的算法。该算法通过观察到新分组进入网络的速率应该与另一端返回确认的速率相同而进行工作。
慢启动为发送方的TCP增加了另一个窗口:拥塞窗口(congestion window),记为cwnd。当与另一个网络的主机建立T C P连接,拥塞窗口被初始化为 1个报文段(即另一端通告的报文段大小)。每收到一个ACK,拥塞窗口就增加一个报文段( c w n d以字节为单位,但是慢启动以报文段大小为单位进行增加)。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。拥塞窗口是发送方使用的流量控制,而通告窗口则是接收方使用的流量控制。发送方开始时发送一个报文段,然后等待 A C K。当收到该A C K时,拥塞窗口从1增加为2,即可以发送两个报文段。当收到这两个报文段的 A C K时,拥塞窗口就增加为4。这是一种指数增加的关系。

启动过程

慢启动算法过程如下:

  1. 通信开始时,发送方的拥塞窗口大小为 1。每收到一个 ACK 确认后,拥塞窗口翻倍。
  2. 由于指数级增长非常快,很快地,就会出现确认包超时。
  3. 此时设置一个“慢启动阈值”,它的值是当前拥塞窗口大小的一半。
  4. 同时将拥塞窗口大小设置为 1,重新进入慢启动过程。
  5. 由于现在“慢启动阈值”已经存在,当拥塞窗口大小达到阈值时,不再翻倍,而是线性增加。
  6. 随着窗口大小不断增加,可能收到三次重复确认应答,进入“快速重发”阶段。
  7. 这时候,TCP 将“慢启动阈值”设置为当前拥塞窗口大小的一半,再将拥塞窗口大小设置成阈值大小(也有说加 3)。
  8. 拥塞窗口又会线性增加,直至下一次出现三次重复确认应答或超时。

    

拥塞避免算法

拥塞避免算法和慢启动算法是两个不同的算法,但是他们都是为了解决拥塞,在实际中这两个算法通常是在一起实现的。相比于慢启动算法拥塞避免算法多维护了一个慢启动阈值ssthresh。当cwnd<ssthresh时,拥塞窗口使用慢启动算法,按指数级增长。 当cwnd>ssthresh时,拥塞窗口使用拥塞避免算法,按线性增长。

拥塞避免算法每经过一个RTT,拥塞窗口增加initcwnd

启动过程

拥塞避免算法和慢启动算法需要对每个连接维持两个变量:一个拥塞窗口 cwnd和一个慢启动门限ssthresh。这样得到的算法的工作过程如下:
1) 对一个给定的连接,初始化cwnd为1个报文段,ssthresh为65535个字节。
2) TCP输出例程的输出不能超过 cwnd和接收方通告窗口的大小。拥塞避免是发送方使用的流量控制,而通告窗口则是接收方进行的流量控制。前者是发送方感受到的网络拥塞的估
计,而后者则与接收方在该连接上的可用缓存大小有关。
3) 当拥塞发生时(超时或收到重复确认),ssthresh被设置为当前窗口大小的一半( c w n d和接收方通告窗口大小的最小值,但最少为 2个报文段)。此外,如果是超时引起了拥塞,则
cwnd被设置为1个报文段(这就是慢启动)。
4) 当新的数据被对方确认时,就增加cwnd,但增加的方法依赖于我们是否正在进行慢启动或拥塞避免。如果 cwnd小于或等于ssthresh,则正在进行慢启动,否则正在进行拥塞避免。
慢启动一直持续到我们回到当拥塞发生时所处位置的半时候才停止(因为我们记录了在步骤 2中给我们制造麻烦的窗口大小的一半),然后转为执行拥塞避免。
慢启动算法初始设置 cwnd为1个报文段,此后每收到一个确认就加 1。这会使窗口按指数方式增长:发送 1个报文段,然后是2个,接着是4个⋯⋯。
拥塞避免算法要求每次收到一个确认时将 cwnd增加1 /cwnd。与慢启动的指数增加比起来,这是一种加性增长(additive increase)。我们希望在一个往返时间内最多为cwnd增加1个报文段(不管在这个RT T中收到了多少个ACK),然而慢启动将根据这个往返时间中所收到的确认的个数增加cwnd。

         下图是慢启动和拥塞避免的一个可视化描述。我们以段为单位来显示cwnd和ssthresh,但它们实际上都是以字节为单位进行维护的。










         在该图中,假定当cwnd为32个报文段时就会发生拥塞。于是设置 ssthresh为16个报文段,而cwnd为1个报文段。在时刻 0发送了一个报文段,并假定在时刻 1接收到它的 ACK,此时cwnd增加为2。接着发送了2个报文段,并假定在时刻2接收到它们的ACK,于是cwnd增加为4(对每个ACK增加1次)。这种指数增加算法一直进行到在时刻 3和4之间收到8个ACK后cwnd等于ssthresh时才停止,从该时刻起, cwnd以线性方式增加,在每个往返时间内最多增加 1个报文段。
正如我们在这个图中看到的那样,术语“慢启动”并不完全正确。它只是采用了比引起拥塞更慢些的分组传输速率,但在慢启动期间进入网络的分组数增加的速率仍然是在增加的。只有在达到ssthresh拥塞避免算法起作用时,这种增加的速率才会慢下来。

超时重传对传输性能有严重影响。原因之一是在RTO阶段不能传数据,相当于浪费了一段时间;原因之二是拥塞窗口的急剧减小,相当于接下来传得慢多了。

快速重传算法

有时候拥塞比较轻微,只有少量包丢失,后续的包能够正常到达。当后续的包到达接收方时,接收方会发现其Seq号比期望的大,所以它每收到一个包就Ack一次期望的Seq号,以此提醒发送方重传。当发送方收到3个或以上重复确认(Dup Ack)时,就意识到相应的包已经丢了,从而立即重传它。这个过程称为快速重传。

为什么要规定凑满3个呢?这是因为网络包有时会乱序,乱序的包一样会触发重复的Ack,但是为了乱序而重传没有必要。由于一般乱序的距离不会相差太大,比如2号包也许会跑到4号包后面,但不太可能跑到6号包后面,所以限定成3个或以上可以在很大程度上避免因乱序而触发快速重传。

还有一个问题,如下图:

如果2号和3号包都丢失了,但是后面4,5,6,7号都正常收到了,并触发了三次Ack2。在重传了2号包之后该传哪个包那,是全部需要重传还是只传2号包?

为了解决这种问题,TCP在发送重复的Ack包的时候,会告诉接收方收到的已经收到包的序号,如下图:

这样发送方就知道该重传哪个包了,这种方式被称为选择性确认(Selective Acknowledgement)

快速恢复

如果在拥塞阶段发生了快速重传就没有必要像超时重传那样处理拥塞窗口了,因为此时的拥塞并不是很严重。RFC5681建议此时的慢启动阈值ssthreh设置为没有被确认包的1/2,但是不小于2个MSS。拥塞窗口设置为慢启动阈值加3个MSS。这个过程被称为快速恢复

总结

超时重传对性能的影响最大,因为在RTO期间不能传输任何数据,而且拥塞窗口会急剧减小。所以应该尽量避免超时重传。

丢包对极小文件的影响比大文件严重,因为小文件可能不能触发三次重复的Ack,导致无法快速重传。

 

连环发问:

1. 发送端如何知道已经丢包?

  定时器超时 收到三个重复ACK。

2.既然有流量控制了,为什么还会有拥塞控制?

      首先要注意拥塞控制与流量控制的区别:

       拥塞控制是防止过多的数据注入网络中,使得网络中路由器或链路不致过载,有一个前提是,网络能够承受现有的网络负荷,是一个全局性过程;

      流量控制是指点对点通信的控制,做的是抑制发送端发送数据的速率,便于接收端来得及接收。

      流量控制虽然可以高效可靠的传送大量的数据,但是如果在刚开始阶段就发送大量的数据,可能会导致网络拥堵,因为网络上的计算机太多了,而且网络之间的请求和转发数据又是非常复杂和庞大的,因此这种复杂的网络环境中会出现拥塞。

3.一般在工作中如何检查当前服务器是否可能存在拥塞?

      这里不得不提到拥塞的表现:  丢包或者延时变长,因此我们会用ping ip/hostname -t 来查看当前主机的丢包和延时情况,或者用tcpdump来查看连续两个报文之间的时间差是否较大。

4.流量控制和拥塞控制的区别
1.相同点

(1)现象都是丢包;
(2)实现机制都是让发送方发的慢一点,发的少一点

2.不同点

    (1)丢包位置不同
             流量控制丢包位置是在接收端上
             拥塞控制丢包位置是在路由器上
    (2)作用的对象不同
              流量控制的对象是接收方,怕发送方发的太快,使得接收方来不及处理
           拥塞控制的对象是网络,怕发送发发的太快,造成网络拥塞,使得网络来不及处理




3.联系

       拥塞控制
             拥塞控制通常表示的是一个全局性的过程,它会涉及到网络中所有的主机、
             所有的路由器和降低网络传输性能的所有因素
      流量控制
            流量控制发生在发送端和接收端之间,只是点到点之间的控制



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章