拥塞控制
什么是拥塞控制?
在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变换,叫做拥塞。
若是对于TCP这种有重传机制的传输协议,当发生数据丢失时,重传数据将延长数据到达的时间;同时,高频率的重传,也将导致网络的拥塞得不到缓解。拥塞控制,就是在网络中发生拥塞时,减少向网络中发送数据的速度,防止造成恶性循环;同时在网络空闲时,提高发送数据的速度,最大限度地利用网络资源。说的简单点,就和堵车差不多,路就这么宽,来的车多,自然过的就慢,所以在必要的时候要限号。
拥塞控制可以分为开环控制和闭环控制
1.开环控制,在设计网络时把因素考虑到
2.闭环控制,基于反馈环路,使用拥塞的信息来进行调整网络
拥塞控制的解决方法:
总共四种:慢开始,拥塞避免,快重传,快恢复
1.慢开始和拥塞避免
发送方维持一个叫做拥塞窗口cwnd,根据网络来进行动态的调整大小,网络拥塞的时候,路由器会丢弃报文,当发送方没有按时收到确认报文,那么就知道网络发生了拥堵。
现在结合拥塞窗口cwnd的变化来看一下上述两个方法
慢开始的“慢”指的是,初始cwnd=1(此时表示的是报文段的个数,而不是真正传输时使用的字节流)
1.开始时发送方cwnd=1,发送报文段M1,如果收到确认M1,那么此时增大cwnd=2,并发送M2,M3
2.要注意,发送方每收到一个确认报文段,cwnd*2(不包括缺失重传的确认)
也就是说,每经过一个传输伦次(RTT时间),cwnd加倍。
但是,为了防止拥塞窗口cwnd增长过大而引起网络拥塞,设置一个慢开始门限ssthresh。
不同情况下使用的算法是不同的:
1.当cwnd<ssthresh,使用上述的慢开始算法
2.当cwnd>ssthresh,停止使用慢开始,使用拥塞避免算法
3.当cwnd==ssthresh,两者都可以使用
那么理所当然的,现在我们要来看一下拥塞避免算法。
拥塞避免算法的思路是让拥塞窗口cwnd缓慢增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd+1,而不是加倍(也就是收到两个,四个确认,仍然+1),这样cwnd就按线性增大
此时我们假设ssthresh为16,也就是超过这个值就要转化为拥塞避免算法,并且假设到24的时候,网络出现拥堵。
此时我们注意两个词:
1.乘法减小:也就是说,当拥塞避免算法增长到24之后,我们更新ssthresh=24/2=12,之后再重新执行之前的两个算法。乘法减小,指的就是ssthresh减半
2.加法增大:指的是,执行拥塞避免算法之后,cwnd线性的进行增长,防止很快就遇到网络拥塞状态
2.快重传和快恢复
快重传的核心:
当接收方收到了一个失序的报文,马上报告给发送方,我没收到,赶紧重传(天下武功唯快不破),加入M2收到了,M3没有收到,之后的M4,M5,M6又发送了,此时接收方一共连续给发送方反馈了4个M2确认报文。那么快重传规定,发送方只要连续收到3个重复确认,立即重传对方发来的M3
那么我们再来看一下快恢复:
1.当发送方连续收到三个重复确认,执行乘法减小,ssthresh减半
2.由于发送方可能认为网络现在没有拥塞,因此与慢开始不同,把cwnd值设置为ssthresh减半之后的值,然后执行拥塞避免算法,线性增大cwn
其实呢,对于接收方也是用限额的,有一个rwnd,也就是接收窗口,那么实际上,发送方窗口的上限=min(rwnd,cwnd)
拥塞控制的本质是控制哪个字段?
header中有一个Window Size字段,它其实是指接收端的窗口,即接收窗口。用来告知发送端自己所能接收的数据量,从而达到一部分流控的目的。
tcp流量控制(滑动窗口机制)
滑动窗口机理和作用
作用
(1)滑动窗口允许发送方连续发送多个报文
(2)根据对方接收窗口大小限制发送方的发送速率,防止分组丢失
原理
TCP是全双工通信,因此每一方的滑动窗口都包括了接收窗口+发送窗口,接收窗口负责处理自己接收到的数据,发送窗口负责处理自己要发送出去的数据。滑动窗口的本质其实就是维护几个变量,通过这些变量将TCP处理的数据分为几类,同时在发送出一个报文、接收一个报文对这些变量做一定的处理维护。
发送窗口如上图 :
(1)N是发送窗口的起始字节,也就是说:字节序号 < N的字节都已经发送出去且已经收到ack,确认无误了;
(2)nextSeq就是下一次发送报文的首部Seq字段(Seq即b第一个字节的序号,这些这里不讲了),表示字节序号在 [N,nextSeq)区间的都已经使用过,发送出去了,但是还未收到ack确认;
(3) N+size就是窗口的最后一个可用字节序号,size是发送窗口的大小,就是每次接收到的报文中的Win字段的值,Win字段其实就是对方接收窗口的大小。
如何让维护这几个值呢?
(1)每接收到一个一个报文要做如下事情:检查接收报文的ack,将N 置为 ack,即往前移到ack这个值;读取报文中的Win字段值,即对方的最新接收窗口大小,从而更新N+size的值。
(2)每发送一个报文,就更改nextSeq的值,发送了多少个字节就把nextSeq往前移多少,但是不要超出N+size。
同样是维护几个关于字节序号的变量,与发送窗口类似,只不过接收窗口的字节序号都是接收到的字节。几个变量的意义如下:
(1)J1表示:字节序号 < J1的字节已经接收到了,即已经发出ack确认了,也可以说可以给程序使用了。程序读取接收缓冲区k个字节,J1会增加k。
(2)J2表示:[J1,J2)区间的字节已经完整、有序的接收到了,但还在缓冲区,没有发出ack进行确认,随时可以供程序读取。
(3)J3就是接收窗口的最后一个可接收字节,由J1+接收窗口的大小算出。超过J3的字节发来是拒绝接收的(一般也不会收到,因为发送窗口的大小是根据接收窗口来的,不可能会超过)。
滑动窗口原理总结:
因为窗口的起始值在开始后就会慢慢的增加,也就是右移,所以这也是滑动窗口名字的由来。实际上就是在接收报文、发送报文维护几个关于ack 、字节序号、seq(报文起始字节序号)的变量值。关于ack、seq、WinSize总结如下:
(1)发送报文ack是怎么来的,接收到报文时ack又是怎么用的:
发送报文时从接收窗口拿J2的值填到报文首部的 ack;接收报文时拿到ack后将发送窗口的起始值N更新为ack。
(2)发送报文seq是怎么来的,接收到报文时seq又是怎么用的:
发送报文时从发送窗口拿到nextSeq的值填到报文首部的seq字段;接收到报文后查看报文的seq字段是否是接收窗口的J2,是就将J2连续右移报文长度个单位。
(3)发送报文Win是怎么来的,接收到报文时Win又是怎么用的:
发送报文时,从接收窗口拿到 (J3-J2)的这个差值填到首部的Win字段;接收到报文时拿到首部的Win字段的值,假设为size,则更新发送窗口的结束位置为 N+size
拥塞控制和流量控制的区别:
拥塞控制:
拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况;常用的方法就是:
( 1 )慢开始、拥塞避免
( 2 )快重传、快恢复。
流量控制:
流量控制是作用于接收者的,它是控制发送者的发送速度从而使接收者来得及接收,防止分组丢失的。
当提供的负载继续增大到某一数值时,网络的吞吐量就下降到零,网络无法工作,产生所谓的死锁。解决的方法就是滑动窗口。