TCP的流量控制和拥塞控制

0.  前言

从朋友分享的面经来看,TCP的拥塞机制在今年腾讯面试中被提及了,可见不论是什么研发岗位,计算机网络的知识总是那么的重要。本科时候学的都忘了=。= 今天打算总结TCP的流量控制和拥塞控制。网上查了下相关资料,发现都一模一样的,而且写的逻辑很乱。本篇对网上互相抄袭的版本进行精炼,逻辑就按照我理解的来写了,图就不自己画了。转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/53198282

 

1.  TCP的流量控制

1.1  流量控制概述

首先我们得理解TCP为什么需要流量控制,或者说流量控制的意义何在?那就是如果发送方把数据发送得过快,接收方可能会来不及接收,这就会造成数据的丢失。

TCP的流量控制是利用滑动窗口机制实现的,接收方在返回的ACK中会包含自己的接收窗口的大小,以控制发送方的数据发送。

 

1.2  流量控制实例

如上图所示A向B发送数据。在连接建立时,B告诉A接收窗口rwnd(receiver window)= 400,单位字节,因此发送方A的发送窗口不能400。

(可以看出,B向A发送的三个报文段都设置了 ACK = 1以保证字段有效,后面的rwnd值就是接收方对发送方的三次流量控制。)

第一次把窗口设置为300 ,第二次100 ,最后一次为 0,即不允许发送方再发送数据的状态。

但是当某个ACK报文丢失了,就会出现A等待B确认,并且B等待A发送数据的死锁状态。为了解决这种问题,TCP引入了持续计时器(Persistence timer),当A收到rwnd=0时,就启用该计时器,时间到了则发送一个1字节的探测报文,询问B是很忙还是上个ACK丢失了,然后B回应自身的接收窗口大小,返回仍为0(A重设持续计时器继续等待)或者会重发rwnd=x。

 

维基百科上的流量控制介绍:

流量控制用来避免主机分组发送得过快而使接收方来不及完全收下,一般由接收方通告给发送方进行调控。

TCP使用滑动窗口协议实现流量控制。接收方在“接收窗口”域指出还可接收的字节数量。发送方在没有新的确认包的情况下至多发送“接收窗口”允许的字节数量。接收方可修改“接收窗口”的值。

 
TCP包的序号与接收窗口的行为很像时钟。

当接收方宣布接收窗口的值为0,发送方停止进一步发送数据,开始了“保持定时器”(persist timer),以避免因随后的修改接收窗口的数据包丢失使连接的双侧进入死锁,发送方无法发出数据直至收到接收方修改窗口的指示。当“保持定时器”到期时,TCP发送方尝试恢复发送一个小的ZWP包(Zero Window Probe),期待接收方回复一个带着新的接收窗口大小的确认包。一般ZWP包会设置成3次,如果3次过后还是0的话,有的TCP实现就会发RST把链接断了。

如果接收方以很小的增量来处理到来的数据,它会发布一系列小的接收窗口。这被称作愚蠢窗口综合症,因为它在TCP的数据包中发送很少的一些字节,相对于TCP包头是很大的开销。解决这个问题,就要避免对小的window size做出响应,直到有足够大的window size再响应:

  • 接收端使用David D Clark算法:如果收到的数据导致window size小于某个值,可以直接ack把window给关闭了,阻止了发送端再发数据。等到接收端处理了一些数据后windows size大于等于了MSS,或者接收端buffer有一半为空,就可以把window打开让发送端再发数据过来。
  • 发送端使用著名的Nagle算法来延时处理,条件一:Window Size>=MSS 或是 Data Size >=MSS;条件二:等待时间或是超时200ms,这两个条件有一个满足,才会发数据,否则就是在积累数据。Nagle算法默认是打开的,所以对于一些需要小包场景的程序——比如像telnet或ssh这样的交互性程序,需要关闭这个算法。可以在Socket设置TCP_NODELAY选项来关闭这个算法。
Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段。 所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。
  Nagle算法的规则(可参考tcp_output.c文件里tcp_nagle_check函数注释):
(1)如果包长度达到MSS,则允许发送;
(2)如果该包含有FIN,则允许发送;
(3)设置了TCP_NODELAY选项,则允许发送;
(4)未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;
(5)上述条件都未满足,但发生了超时(一般为200ms),则立即发送。

 

2.  TCP的拥塞控制

2.1 拥塞控制概述

我们仍然需要理解为什么需要拥塞控制。网络中的链路容量、交换结点中的缓存、处理机等等都有着工作的极限,当网络的需求超过它们的工作极限时,就出现了拥塞。拥塞控制就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。

 

2.2 慢开始(Slow-Start)和拥塞避免(Congestion Avoidance)结合

在这种机制中,发送方维护一个叫做拥塞窗口的变量,只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的数据发送出去。但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络中的数据。

 

2.2.1 两种算法机制介绍

慢开始算法是指开始发送数据时,并不清楚网络的负荷情况,会先发送一个1字节的试探报文,当收到确认后,就发送2个字节的报文,继而4个,8个以此指数类推。

需要注意的是,慢开始的“慢”并不是指拥塞窗口的增长速率慢,而是指在TCP开始发送报文时先设置拥塞窗口=1。

拥塞避免算法是让拥塞窗口缓慢地增大,即cwnd加1,而不是如慢开始算法一样加倍。

2.2.2 两种算法的结合的实例分析

根据上图的实例进行分析,一开始的慢开始算法的指数增长是很恐怖的,所以为了防止拥塞窗口cwnd增长过快需要设置一个门限ssthresh,这里是16。

(1)当 cwnd < ssthresh 时,使用上述的慢开始算法。

(2)当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。

(3)当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞控制避免算法。

无论在慢开始阶段还是在拥塞避免阶段,只要发送方没有收到确认,就认为这时候拥塞了,就要把慢开始门限ssthresh设置为此时发送方窗口值的一半(上例中是把发送方窗口值24修改为12)。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法。

这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕。转载请注明出处为SEU_Calvin的博客

 

2.3  快重传(Fast Retransmit)和快恢复(Fast Recovery)结合

快重传是指,如果发送端接收到3个以上的重复ACK,不需要等到重传定时器溢出就重新传递,所以叫做快速重传,而快速重传以后,因为走的不是慢启动而是拥塞避免算法,所以这又叫做快速恢复算法。

如果没有快速重传和快速恢复,TCP将会使用定时器来要求传输暂停。在暂停这段时间内,没有新的数据包被发送。所以快速重传和快速恢复旨在快速恢复丢失的数据包。

 

2.3.1  具体介绍

快重传的机制还是比较好理解的,如图所示,接收方发现M3丢失,则立即发送对M2的重复确认。一旦发送方一连收到三个M2的重复确认就应当立即重传M3,也就是发送方收到第四个对M2的确认时。

 

 

与快重传配合使用的还有快恢复算法,结合上图的实例来分析,其过程有以下两个要点:

(1)当发送方在cwnd=24时连续收到三个重复确认,就把慢开始门限ssthresh减半(就是上图中的24修改为12)。

(2)接下来不执行慢开始算法,而是把cwnd值设置为门限ssthresh减半后的数值(即cwnd不是设置为1而是设置为12),然后开始执行的是拥塞避免算法,使拥塞窗口缓慢地线性增大

这里为什么替换掉了慢开始算法呢?

这是因为收到重复的ACK不仅仅告诉我们一个分组丢失了,由于接收方只有在收到另一个报文段时才会产生重复的ACK,所以还告诉我们该报文段已经进入了接收方的缓存。也就是说,在收发两端之间仍然有流动的数据,而我们不想执行慢启动来突然减少数据流。转载请注明出处为SEU_Calvin的博客

 

posted @ 2018-05-13 17:05  LloydDracarys  阅读(271)  评论(0)    收藏  举报