网络协议–TCP 流量控制与拥塞控制

在了解流量控制之前,先来了解几个相关名词。

MSS:Maximum Segment Size,TCP一次传输发送的最大数据段长度。

RTT:Round-Trip Time,往返时延,表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认),总共经历的时延。

RWND: Receiver Window 接收方的窗口

CWND:congestion/kən'dʒestʃ(ə)n/ window 拥塞(se) 窗口,窗口单位是字节。(发送方维持的)

Advertised window: 通告窗口(接收方维持的)

MSL :maximum segment life 最大报文生存时间

TCP窗口大小值是TCP段中一个16bit字段,它代表窗口的字节容量。TCP窗口的最大值为2的16次方-1,即65535字节

如果发送方把数据发送得过快,接收方可能会来不及接收,这就会造成数据的丢失。

如果要防止这种情况,那就要进行流量控制。TCP的流量控制就是让发送方的发送速率不要太快,让接收方来得及接收。而TCP的流量控制用的是滑动窗口(Sliding Window).

     滑动窗口协议既保证了分组无差错、有序接收,也实现了流量控制。主要的方式就是接收方返回的 ACK 中会包含自己的接收窗口(RWND)的大小,并且利用大小来控制发送方的数据发送。

image.png

图片源自网络

                    

零窗口探测 Zero Window Probes  ZWP

当接收方的接收缓冲区满了以后,会把响应报文中的通告窗口字段置为0,从而阻止发送方的继续发送。

流量控制引发的死锁?怎么避免死锁的发生?

当发送者收到了一个窗口为0的应答,发送者便停止发送,等待接收者的下一个应答。但是如果这个窗口不为0的应答在传输过程丢失,发送者一直等待下去,而接收者以为发送者已经收到该应答,等待接收新数据,这样双方就相互等待,从而产生死锁。

为了避免流量控制引发的死锁,TCP使用了持续计时器(TCP Persist Timer)。每当发送者收到一个零窗口的应答后就启动该计时器。时间一到便主动发送报文询问接收者的窗口大小。若接收者仍然返回零窗口,则重置该计时器继续等待;若窗口不为0,则表示应答报文丢失了,此时重置发送窗口后开始发送,这样就避免了死锁的产生

计算持续计时器时采用了普通的TCP指数退避算法。比如对一个典型的局域网连接,首次超时时间算出来是1.5秒,那么第二次的超时时间值就增加一倍,为3秒,再下次就为6秒等。但是持续定时器总是在5到60秒之间。

白痴窗口综合症

滑窗机制有可能犯病,比如白痴窗口综合症 (Silly Window Syndrome)。假设这样一种情形:接收方宣布(advertise)一个小的窗口,发送方根据advertised window,发送一个小的片段。接收方的小窗口被填满,经过处理,接收方再宣布一个小的窗口…… 这就是“白痴窗口综合症”:TCP通信的片段中包含的数据量很小。在这样的情况下,TCP通信的片段所含的信息都很小,网络流量主要是TCP片段的头部,从而造成流量的浪费 (由于TCP头部很大,我们希望每个TCP片段中含有比较多的数据)。

如果发送方不断发送小的片段,也会造成“白痴窗口”。为了解决这个问题,需要从两方面入手。TCP中有相关的规定,要求:

1. 接收方通告的窗口必须达到一定的尺寸,否则等待。

2. 除了一些特殊情况,发送方发送的片段必须达到一定的尺寸,否则等待。特殊情况主要是指需要最小化延迟的TCP应用(比如命令行互动)。

TCP 拥塞控制 主要控制包丢失,包重传的现象

流量控制是端到端的交互,如果只是局域网内的两台设备交互,我想通过滑动窗口大概能控制得差不多,但是实际网络的情况非常复杂,发送方和接收方之间还有路由器和交换机,网络传输线路又复杂,这个时候就需要拥塞控制。

拥塞控制主要有四个算法:慢启动、拥塞避免、快速重传和快速恢复。

慢启动

讨论慢启动算法先来了解下拥塞窗口的概念,这是慢启动算法为TCP发送方新增的窗口,congestion window,简称cwnd。

对应上文,发送方取拥塞窗口和滑动窗口的最小值作为发送上限,即谁严格谁起决定因素。

1、  连接建立开始,发送方不了解网络的情况,cwnd初始化比较小的值,RFC建议2-4个MSS,具体视MSS的大小而定;

If (MSS <= 1095 bytes)

      then win <= 4 * MSS;

If (1095 bytes < MSS < 2190 bytes)

      then win <= 4380;

If (2190 bytes <= MSS)

      then win <= 2 * MSS; 

2、  如果发送出去的包都被ACK,说明还未到达拥塞点,则增加拥塞窗口,RFC建议的是每收到n个ACK,则cwnd新增n个MSS,呈指数关系增长,虽然这个过程看似比较快,但是基数比较低,所以被称为“慢启动”。生活中的例子,我们通过漏斗往瓶子里灌水,我们就知道,不能一桶水一下子倒进去,肯定会溅出来,要一开始慢慢的倒,然后发现总能够倒进去,就可以越倒越快。这就类似慢启动。

拥塞避免:

其实慢启动除了维护了cwnd,还维护了慢启动临界值ssthresh,一般将ssthresh(慢启动阈值)设置为65535字节。在cwnd<=ssthresh时,还是处于慢启动环节,一旦>ssthresh,开始进入拥塞避免。

RFC建议拥塞避免环节,无论一个RTT可以收到多少个ACK,每一次确认都只新增1个MSS,呈线性关系增长,避免快速的触碰到网络拥塞点。

快速重传:

进入拥塞避免之后,最终还是会碰到拥塞点,发送方此时迟迟得不到确认,当然得不到确认也有可能是因为延迟确认导致的。

发送方此时决定等待一段时间,如果一段时间后还是得不到确认,就发起重传,这个过程叫做超时重传。(从发出原始包到重传该包的时间叫做重传超时时间RTO(Retransmission TimeOut)。)

进入超时重传后,RFC建议将cwnd设置为1个MSS,而对于ssthresh,RFC5681建议的是发生拥塞时未被ACK的数据量的1/2,但必须大于等于2个MSS。然后重新进入慢启动环节。超时重传因为需要等待RTO之后才能进入新的恢复环节,所以对网络性能的影响是比较大的。

所以各路大神又想到了一个新的方式,看能否无需等待RTO,就发起重传,这种方式叫做快速重传。

快速重传规定在收到3个及以上重复ACK时就触发重传,不再进入慢启动环节,然后直接进入拥塞避免,这个就是快速恢复算法。

为什么是3个?因为1-2个重复ACK,很有可能是乱序,只有在3个及以上的时候才是有可能丢包了。

 参考链接:

            https://zhuanlan.zhihu.com/p/37379780

            https://aisxyz.iteye.com/blog/2365814

            http://www.eehello.com/?post=147

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注