什么是拥塞
我们都知道计算机网络中的资源是有限的。某段时间内网络中对资源的需求超过了网络中的可用部分,而导致网络性能下降的情况就是拥塞。
通俗点说就是发送的数据包太多网络中的设备处理不过来,而导致网络性能下降的情况。
TCP 为什么要进行拥塞控制
网络中的路由器会有一个数据包处理队列,当路由器接收到的数据包太多而一下子处理不过来时,就会导致数据包处理队列过长。此时,路由器就会无条件的丢弃新接收到的数据封包。
这就会导致上层的 TCP 协议以为数据包在网络中丢失,进而重传这些数据包,而路由器又会丢弃这些重传的数据包,如此以往,就会导致网络性能急剧下降,引起网络瘫痪。
因此,TCP 需要控制数据包发送的数量来避免网络性能的下降。
拥塞控制与流量控制的区别
引用书上的答案:
拥塞控制就是防止过多的数据注入到网络中,这样可以防止网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机、所有的路由器,以及与降低网络传输性能有关的所有因素。
流量控制往往指点对点通信量的控制,是个端到端的问题。流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收。
拥塞控制的方法
慢开始
拥塞窗口
拥塞窗口(cwnd)是指发送方维护的一个根据网络状况动态变化的窗口。一般来说,发送方会让自己的发送窗口等于拥塞窗口的大小。
如果考虑到流量控制的话,发送窗口也有可能小于拥塞窗口的大小。
传输轮次
一个传输轮次是指发送方把自己的发送窗口内的数据全部发送出去并收到对最后一个字节的确认。
例如,A将自己的发送窗口内的数据全部连续发送给了B,而B收到这些数据后向A发送了对这些数据的确认,A收到这个确认后,一个传输轮次就算是完成了。
慢开始算法
慢开始算法中的主要方法就是有小到大逐渐增大发送窗口。
那么,具体是怎么增大的呢?
简单来说就是每个传输轮次后将 cwnd 大小加倍。
举个例子:
首先,发送方设置
cwnd=1(为方便理解,这里用报文段的个数作为窗口大小的单位),在收到接收方发来的确认后(也就是下个传输轮次),设置cwnd=2,然后将发送窗口的数据发送出去。在一次收到接收方发来的确认后,发送方设置cwnd=4,再讲发送窗口中的数据发送出去。然后再重复上面的过程。
这里就应该清楚,慢开始算法中的慢不是说 cwnd 增长的慢,而是相对一下子发送大量数据而言,这种一次先发送少量的数据包的方式要慢许多。
当然,cwnd 的大小肯定不可能一直以这种指数的方式增长下去,要不然很快就会增长到引起网络瘫痪的程度了。
所以,经过一定时间或条件,我们就要换成拥塞避免算法来发送数据。
拥塞避免
慢开始门限 ssthresh
像上面所说,不能任由慢开始算法中的 cwnd 任意增长,所以我们引入一个慢开始门限(ssthresh)的阈值来控制 cwnd 的增长。
具体作用如下:
cwnd < ssthresh , 使用慢开始算法
cwnd = ssthresh , 使用慢开始算法或拥塞避免算法都可以
cwnd > ssthresh , 使用拥塞避免算法呢
还有一个问题就是这个 ssthresh 是怎么设置的呢?
TCP/IP 中规定无论是在慢开始阶段还是在拥塞避免阶段,只要发现网络中出现拥塞(没有按时收到确认),就要把ssthresh设置为此时发送窗口的一半大小(不能小于2)。
拥塞避免过程
拥塞避免算法也是逐渐的增大 cwnd 的大小,只是采用的是线性增长而不是像慢开始算法那样的指数增长。
具体来说就是每个传输轮次后将 cwnd 的大小加一(加法增大),如果发现出现网络拥塞的话就按照上面的方法重新设置ssthresh的大小(乘法减小)并从cwnd=1开始重新执行慢开始算法。
如下面的图片所示:
(图片来源于网络)
快重传
前面复习到过,TCP 的可靠传输的原理就是超时重传机制。配合上面的慢开始和拥塞避免使用就是发送发发送完数据后设置一个定时器,如果在定时器时间内没有收到对接收方发来的确认的话就去执行上述的乘法减小过程并重新开始慢开始算法。
而快重传则是允许发送方再连续收到 3 个重复的确认后就可以开始执行乘法减小过程而不必再等待所设置的重传计时器到时。
这就需要接收方没收到一个失序的报文段就立即发出重复确认以让发送发及早知道有报文段丢失,而不是等待自己发送数据的时候进行捎带确认。
快恢复
快恢复算法是与快重传算法配合使用的一个算法。
使用了快恢复算法后与原来不同的一点是当发现网络出现拥塞并执行了乘法减小过程后,并不是设置cwnd=1并重新开始执行慢开始算法,而是让 cwnd =乘法减小后的ssthresh并开始执行拥塞避免算法。
因为此时发送发能连续接收到三个重复的确认就可以认为此时网络很可能没有发生拥塞。
使用了快重传和快恢复的拥塞避免过程如下:
(图片来源于网络)
TCP 的发送窗口到底是多大
发送窗口的上限值 = Min{rwnd, cwnd}
rwnd:接收方接收窗口
cwnd:发送方拥塞窗口