TCP 滑动窗口和 拥塞窗口

转http://coolshell.cn/articles/11609.htmlhtml

 

滑动窗口 -- 表征发送端和接收端的接收能力算法

拥塞窗口-- 表征中间设备的传输能力shell

 

TCP滑动窗口

须要说明一下,若是你不了解TCP的滑动窗口这个事,你等于不了解TCP协议。咱们都知道,TCP必须要解决的可靠传输以及包乱序(reordering)的问题,因此,TCP必须要知道网络实际的数据处理带宽或是数据处理速度,这样才不会引发网络拥塞,致使丢包。网络

因此,TCP引入了一些技术和设计来作网络流控,Sliding Window是其中一个技术。 前面咱们说过,TCP头里有一个字段叫Window,又叫Advertised-Window,这个字段是接收端告诉发送端本身还有多少缓冲区能够接收数据因而发送端就能够根据这个接收端的处理能力来发送数据,而不会致使接收端处理不过来。 为了说明滑动窗口,咱们须要先看一下TCP缓冲区的一些数据结构:数据结构

上图中,咱们能够看到:并发

  • 接收端LastByteRead指向了TCP缓冲区中读到的位置,NextByteExpected指向的地方是收到的连续包的最后一个位置,LastByteRcved指向的是收到的包的最后一个位置,咱们能够看到中间有些数据尚未到达,因此有数据空白区。
  • 发送端的LastByteAcked指向了被接收端Ack过的位置(表示成功发送确认),LastByteSent表示发出去了,但尚未收到成功确认的Ack,LastByteWritten指向的是上层应用正在写的地方。

因而:ssh

  • 接收端在给发送端回ACK中会汇报本身的AdvertisedWindow = MaxRcvBuffer – LastByteRcvd – 1;
  • 而发送方会根据这个窗口来控制发送数据的大小,以保证接收方能够处理。

下面咱们来看一下发送方的滑动窗口示意图:tcp

图片来源ide

上图中分红了四个部分,分别是:(其中那个黑模型就是滑动窗口)优化

  • #1已收到ack确认的数据。
  • #2发还没收到ack的。
  • #3在窗口中尚未发出的(接收方还有空间)。
  • #4窗口之外的数据(接收方没空间)

下面是个滑动后的示意图(收到36的ack,并发出了46-51的字节):

下面咱们来看一个接受端控制发送端的图示:

 

 

 

TCP的拥塞处理 – Congestion Handling

上面咱们知道了,TCP经过Sliding Window来作流控(Flow Control),可是TCP以为这还不够,由于Sliding Window须要依赖于链接的发送端和接收端,其并不知道网络中间发生了什么。TCP的设计者以为,一个伟大而牛逼的协议仅仅作到流控并不够,由于流控只是网络模型4层以上的事,TCP的还应该更聪明地知道整个网络上的事。

具体一点,咱们知道TCP经过一个timer采样了RTT并计算RTO,可是,若是网络上的延时忽然增长,那么,TCP对这个事作出的应对只有重传数据,可是,重传会致使网络的负担更重,因而会致使更大的延迟以及更多的丢包,因而,这个状况就会进入恶性循环被不断地放大。试想一下,若是一个网络内有成千上万的TCP链接都这么行事,那么立刻就会造成“网络风暴”,TCP这个协议就会拖垮整个网络。这是一个灾难。

因此,TCP不能忽略网络上发生的事情,而无脑地一个劲地重发数据,对网络形成更大的伤害。对此TCP的设计理念是:TCP不是一个自私的协议,当拥塞发生的时候,要作自我牺牲。就像交通阻塞同样,每一个车都应该把路让出来,而不要再去抢路了。

关于拥塞控制的论文请参看《Congestion Avoidance and Control》(PDF)

拥塞控制主要是四个算法:1)慢启动2)拥塞避免3)拥塞发生4)快速恢复。这四个算法不是一天都搞出来的,这个四算法的发展经历了不少时间,到今天都还在优化中。 备注:

  • 1988年,TCP-Tahoe 提出了1)慢启动,2)拥塞避免,3)拥塞发生时的快速重传
  • 1990年,TCP Reno 在Tahoe的基础上增长了4)快速恢复
慢热启动算法 – Slow Start

首先,咱们来看一下TCP的慢热启动。慢启动的意思是,刚刚加入网络的链接,一点一点地提速,不要一上来就像那些特权车同样霸道地把路占满。新同窗上高速仍是要慢一点,不要把已经在高速上的秩序给搞乱了。

慢启动的算法以下(cwnd全称Congestion Window):

1)链接建好的开始先初始化cwnd = 1,代表能够传一个MSS大小的数据。

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

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

4)还有一个ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入“拥塞避免算法”(后面会说这个算法)

因此,咱们能够看到,若是网速很快的话,ACK也会返回得快,RTT也会短,那么,这个慢启动就一点也不慢。下图说明了这个过程。

这里,我须要提一下的是一篇Google的论文《An Argument for Increasing TCP’s Initial Congestion Window》Linux 3.0后采用了这篇论文的建议——把cwnd 初始化成了 10个MSS。 而Linux 3.0之前,好比2.6,Linux采用了RFC3390,cwnd是跟MSS的值来变的,若是MSS< 1095,则cwnd = 4;若是MSS>2190,则cwnd=2;其它状况下,则是3。

 拥塞避免算法 – Congestion Avoidance

前面说过,还有一个ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入“拥塞避免算法”。通常来讲ssthresh的值是65535,单位是字节,当cwnd达到这个值时后,算法以下:

1)收到一个ACK时,cwnd = cwnd + 1/cwnd

2)当每过一个RTT时,cwnd = cwnd + 1

这样就能够避免增加过快致使网络拥塞,慢慢的增长调整到网络的最佳值。很明显,是一个线性上升的算法。

拥塞状态时的算法

前面咱们说过,当丢包的时候,会有两种状况:

1)等到RTO超时,重传数据包。TCP认为这种状况太糟糕,反应也很强烈。

    • sshthresh =  cwnd /2
    • cwnd 重置为 1
    • 进入慢启动过程

2)Fast Retransmit算法,也就是在收到3个duplicate ACK时就开启重传,而不用等到RTO超时。

    • TCP Tahoe的实现和RTO超时同样。
    • TCP Reno的实现是:
      • cwnd = cwnd /2
      • sshthresh = cwnd
      • 进入快速恢复算法——Fast Recovery

上面咱们能够看到RTO超时后,sshthresh会变成cwnd的一半,这意味着,若是cwnd<=sshthresh时出现的丢包,那么TCP的sshthresh就会减了一半,而后等cwnd又很快地以指数级增涨爬到这个地方时,就会成慢慢的线性增涨。咱们能够看到,TCP是怎么经过这种强烈地震荡快速而当心得找到网站流量的平衡点的。

快速恢复算法 – Fast Recovery

TCP Reno

这个算法定义在RFC5681。快速重传和快速恢复算法通常同时使用。快速恢复算法是认为,你还有3个Duplicated Acks说明网络也不那么糟糕,因此没有必要像RTO超时那么强烈。 注意,正如前面所说,进入Fast Recovery以前,cwnd 和 sshthresh已被更新:

  • cwnd = cwnd /2
  • sshthresh = cwnd

而后,真正的Fast Recovery算法以下:

  • cwnd = sshthresh  + 3 * MSS (3的意思是确认有3个数据包被收到了)
  • 重传Duplicated ACKs指定的数据包
  • 若是再收到 duplicated Acks,那么cwnd = cwnd +1
  • 若是收到了新的Ack,那么,cwnd = sshthresh ,而后就进入了拥塞避免的算法了。

若是你仔细思考一下上面的这个算法,你就会知道,上面这个算法也有问题,那就是——它依赖于3个重复的Acks。注意,3个重复的Acks并不表明只丢了一个数据包,颇有多是丢了好多包。但这个算法只会重传一个,而剩下的那些包只能等到RTO超时,因而,进入了恶梦模式——超时一个窗口就减半一下,多个超时会超成TCP的传输速度呈级数降低,并且也不会触发Fast Recovery算法了。

一般来讲,正如咱们前面所说的,SACK或D-SACK的方法可让Fast Recovery或Sender在作决定时更聪明一些,可是并非全部的TCP的实现都支持SACK(SACK须要两端都支持),因此,须要一个没有SACK的解决方案。而经过SACK进行拥塞控制的算法是FACK(后面会讲)

TCP New Reno

因而,1995年,TCP New Reno(参见 RFC 6582 )算法提出来,主要就是在没有SACK的支持下改进Fast Recovery算法的——

  • 当sender这边收到了3个Duplicated Acks,进入Fast Retransimit模式,开发重传重复Acks指示的那个包。若是只有这一个包丢了,那么,重传这个包后回来的Ack会把整个已经被sender传输出去的数据ack回来。若是没有的话,说明有多个包丢了。咱们叫这个ACK为Partial ACK。
  • 一旦Sender这边发现了Partial ACK出现,那么,sender就能够推理出来有多个包被丢了,因而乎继续重传sliding window里未被ack的第一个包。直到再也收不到了Partial Ack,才真正结束Fast Recovery这个过程

咱们能够看到,这个“Fast Recovery的变动”是一个很是激进的玩法,他同时延长了Fast Retransmit和Fast Recovery的过程。

算法示意图

下面咱们来看一个简单的图示以同时看一下上面的各类算法的样子:

相关文章
相关标签/搜索