一、Flow Control 的机制
想象一下有两个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知道而已。
二、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的问题,这个时候必须降低发送速率,否则依然会出现数据包的丢失情况。
三、为什么不多余
其实看到这里,事情已经很明显了。那就是flow control和congestion control两者是相辅相成的。即,sender的发送速率要满足:
从而分别从sender和receiver两端来保证通信的通畅。所以题目中说两者等同是不准确的。
至于congestion control的几个阶段,譬如slow start,congestion avoidance和fast recovery就不是题主关注的重点了,在此略过。