四篇关于流量控制,拥塞控制,滑动窗口,拥塞窗口的参考文章,可以详细阅读,当然在原理方面可能还达不到深入理解的水平,但是对上面试官吹吹水还是没问题的
一、Flow Control 的机制
写在开头:纯属自己看完的感悟,若是不理解,就继续看下文详解
FlowControl:通过服务端施加限制条件来抑制缓冲区溢出
Congestion Control:通过客户端施加限制条件来抑制缓冲区溢出
流量控制实际上是一种为了考虑和平衡客服端和服务端发送接收数据不同步的情况,如果发送的慢还好,服务端大不了进入空闲等待,只不过浪费了资源,但若是客户端发送数据过快,另服务端读取数据的速度跟不上客服端,导致两端游速度差,这样就会在服务端缓冲区积累大量数据,直至数据溢出,而且由于服务端已经确认过该信号,那么溢出之后就会导致该数据丢失,会对该通信有重大影响。所以呢,就要用flow control机制来限制客服端的数据发送速度,所以一切的问题根源都是来自于服务端,是服务端增加了限制条件,考虑的问题角度是不同的
(说到这里,其实已经发现,对于在服务端积累大量数据这个问题,一种情况是可以由服务端发出限制条件来进行改善,那么我们同样可以利用客服端增加自身限制条件来防止缓冲区溢出,那这种方法就是第二个话题,拥塞控制了,所以两个控制方法是从不同角度出发的)
当客服端发送了100k数据,而服务端有400k的缓冲区,服务端的读数据速度为50k,那么显然,这100k都可以存进这个buff里,这时候发送确认信号ack和下一次可以发送数据的大小size=400-100,第二次呢我发送了200,由于服务端读取数据慢一些,就会有50k剩余,这个时候加上刚来的200k,一共250k,那么这个时候剩余空间仅为150,所以就对发送速率有了限制,再也不能像第二次那样随心所欲的发送200k了,这样就启到了流量控制作用,至于多发的,没有接收到的,那么就需要TCP的重发机制了,这个以后再说。另外对于滑动窗口的大小,可以记录服务端的上一时刻接受字节和读取字节,客户端记录发送字节和确认字节,根据相应公式即可确认
想象一下有两个host,host A和host B,它们现在通过TCP connection连接在一起,开始互相传数据。在这个connection的两端,也就是A和B上,都有一个receive buffer,从网络上来的数据(bytes)如果是正确且有序的,那么就会先放入到receive buffer里,然后在被host的应用层读取。注意,应用层读取数据并不是即时的,这意味着数据会在receive buffer里积压,如下图。
我们来假设一个比较极端的情景。现在A以一个很高的transmission rate向B传一个大文件,同时B的应用层从receive buffer里读取数据的速度很慢。这样的结果就是B的receive buffer很容易就overflow了。Flow control的目的就是在于防止这一情况的出现,即在这个例子里,不让A去overflow B的receive buffer。简单来说,flow control提供一个速度匹配(speed-matching)机制,即令host A发送数据的速度和host B读取数据的速度匹配,从而避免overflow的情况发生。同时,由于TCP是full-duplex的,所以这个机制是双向的。
overflow的坏处是什么呢?直接的后果就是数据会丢失,从而降低整个网络的传输效率。放到城市规划的领域来类比,就是两个路口A和B之间有一段路,而从路口B到路口A的交通流超过了一个路口A的通行能力,而车流还在源源不断地从路口B涌向路口A,最终导致AB路段的交通堵塞。
那么TCP是如何实现flow control的呢?
概括而言,主要为以下几个要点:
- sender维护一个变量,这个变量叫做receive window,即,rwnd,记录receive buffer的剩余空间;
- sender还会维护另外两个变量,分别叫做LastByteSent和LastByteAcked;
- receiver维护两个变量,叫做LastByteRead和LastByteRcvd;
其中,LastByteRead是receiver的应用层最近一次从receive buffer里读走的数据的byte数量,而LastByteRcvd则是receiver最近一次从网络收到的数据的byte数量。
同时,在TCP connection建立的时候,两边的host都会分配一个receive buffer,其大小用RcvBuffer表示。
TCP协议不允许overflow receive buffer,所以必须满足:
本质上讲,flow control的实现就是,receiver把自己的buffer还剩下多少空间以值rwnd的形式发回给sender,然后sender通过比较三个值:rwnd, LastByteSent和LastByteAcked之间的关系来控制自己发送数据的速度。即要满足以下关系:
如果继续沿用前边的例子的话,就是host B把rwnd的值放在transport layer segment的receive window 这个字段里发回给host A,然后host A通过比较上述三个值,来保证那些“还在路上”的数据数量是小于等于host B的receive buffer的剩余空间的。
现在,路口A和路口B之间增加了通信功能,路口B在还没有发生拥堵的时候,会向路口A发送信息,告诉它大概再来几辆车可能就要堵住了。这时候路口A就会开始限制通行的汽车的数量,从而保证在路段AB内的车始终可以比较通畅地通行。
这个过程用流量图来表示如下:
这里省略了一个技术细节,就是上图"read 2k to upper layer"那里。严格来说receiver不会主动发送这个信息给sender,而是sender在上一次知道rwnd为0之后会持续不断地发送1byte的数据包给receiver,这样当receive buffer又有空间的时候新的rwnd值就可以传给sender了。
以上就是flow control的机制。为了不要与后边的congestion control搞混,需要特别注意的是,虽然sender会维护一个叫做rwnd的变量,但是这个receive window实际上是在receiver端的,之所以sender可以得到这个值,是因为receiver会以把这个值放到传给sender的数据包里的方式让sender知道而已。
只有彻底了解flow control机制,才能更加深入的获悉拥塞控制原理机制
下面就介绍拥塞控制
二、Congestion Control 的机制
在flow control的基础上理解congestion control就比较容易了。
首先,congestion control的“官方定义“是这样子的:
控制sender向connection传输数据的速率,使这个速率为网络拥堵状况的函数。
核心的关键词:网络拥堵状况的函数,网络拥堵状况的函数,网络拥堵状况的函数,重要的事情强调三遍。
什么意思呢?就是sender发送数据的速率不仅要考虑receiver的buffer(flow control负责的事情),还要考虑网络状况。下面继续沿用前边交通的例子:
经过flow control之后,路口A和路口B之间的拥堵状况消失了。但是,天有不测风云,路段AB之间的某一处发生了一起车祸,本来4车道现在只有1条车道能用了,导致通行能力收到的极大的限制。这个时候虽然路口B远没有达到塞车的阈值,但是车开始堵在路段中间的某个位置了。这个时候,显然,路口A不能再按照原来的速率允许车从路口A进入AB路段了。
这就在sender端要维护一个congestion window,即cwnd的原因。cwnd如下图:
sender发送数据之后,会收到receiver的ack,如果总是收不到ack,而rwnd显示receive buffer还没有满,那么就说明是channel的问题,这个时候必须降低发送速率,否则依然会出现数据包的丢失情况。
上面那些话是什么意思呢???
可以这样理解:
虽然在buffer缓冲区没有满,但是由于网络通信不好,或者说限制速度了,就会导致大量的数据被堵塞在传输的道路上,这个时候,在发送端发送的数据在一定时间内并未有相应的ack确认信息,再查看buffer缓冲区发现,没有满,那么就知道数据堵在路上了,说明传输通道已经满了。接收端读取数据不变,发送端数据发送速度要瞬间降下来,留给足够多的时间来让接收端读取数据。
慢启动和快重传:
对应与发送端开始发送数据都是以2倍前一时刻数据量发送,第一次1个字节,第二次2个字节,第三次4个字节,第四次8个字节....
快重传,就是把发送速率降到拥塞时速率的一半
当深刻理解之后,再看下面的图会有不一样的理解,加深印象
三、为什么不多余
其实看到这里,事情已经很明显了。那就是flow control和congestion control两者是相辅相成的。即,sender的发送速率要满足:
从而分别从sender和receiver两端来保证通信的通畅。所以题目中说两者等同是不准确的。
至于congestion control的几个阶段,譬如slow start,congestion avoidance和fast recovery就不是题主关注的重点了,在此略过。
1. 慢开始、拥塞控制
2. 快重传、快恢复
一切的基础还是慢开始,这种方法的思路是这样的:
-1. 发送方维持一个叫做“拥塞窗口”的变量,该变量和接收端口共同决定了发送者的发送窗口;
-2. 当主机开始发送数据时,避免一下子将大量字节注入到网络,造成或者增加拥塞,选择发送一个1字节的试探报文;
-3. 当收到第一个字节的数据的确认后,就发送2个字节的报文;
-4. 若再次收到2个字节的确认,则发送4个字节,依次递增2的指数级;
-5. 最后会达到一个提前预设的“慢开始门限”,比如24,即一次发送了24个分组,此时遵循下面的条件判定:
*1. cwnd < ssthresh, 继续使用慢开始算法;
*2. cwnd > ssthresh,停止使用慢开始算法,改用拥塞避免算法;
*3. cwnd = ssthresh,既可以使用慢开始算法,也可以使用拥塞避免算法;
-6. 所谓拥塞避免算法就是:每经过一个往返时间RTT就把发送方的拥塞窗口+1,即让拥塞窗口缓慢地增大,按照线性规律增长;
-7. 当出现网络拥塞,比如丢包时,将慢开始门限设为原先的一半,然后将cwnd设为1,执行慢开始算法(较低的起点,指数级增长);
上述方法的目的是在拥塞发生时循序减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够的时间把队列中积压的分组处理完毕。慢开始和拥塞控制算法常常作为一个整体使用,而快重传和快恢复则是为了减少因为拥塞导致的数据包丢失带来的重传时间,从而避免传递无用的数据到网络。快重传的机制是:
-1. 接收方建立这样的机制,如果一个包丢失,则对后续的包继续发送针对该包的重传请求;
-2. 一旦发送方接收到三个一样的确认,就知道该包之后出现了错误,立刻重传该包;
-3. 此时发送方开始执行“快恢复”算法:
*1. 慢开始门限减半;
*2. cwnd设为慢开始门限减半后的数值;
*3. 执行拥塞避免算法(高起点,线性增长);
TCP 滑动窗口(发送窗口和接收窗口)
TCP的滑动窗口主要有两个作用,一是提供TCP的可靠性,二是提供TCP的流控特性。同时滑动窗口机制还体现了TCP面向字节流的设计思路。
TCP的Window是一个16bit位字段,它代表的是窗口的字节容量,也就是TCP的标准窗口最大为2^16-1=65535个字节。
另外在TCP的选项字段中还包含了一个TCP窗口扩大因子,option-kind为3,option-length为3个字节,option-data取值范围0-14。窗口扩大因子用来扩大TCP窗口,可把原来16bit的窗口,扩大为31bit。
来源:CSDN
作者:我真的不会Coding
链接:https://blog.csdn.net/qq_40086556/article/details/82669836