流量控制和拥塞控制的相同点:

  • 都是为了控制发送数据量的大小。

不同点:

  • 流量控制是根据接收者能力控制发送数据量大小。
  • 拥塞控制是根据网络的拥塞状态控制数据量的发送大小。

最后实际要发送的数据量取决于流量控制和拥塞控制计算出来发送量的较小者。rwnd表示接收方的接收能力,cwnd表示链路还能承受的数据量能力。
图片说明

流量控制算法

在流量控制算法中,对端的接收能力是指还能接收多少数据这个数值体现在对方发送给你的TCP报文的首部字段winsize中。它只能接收 100 个字节,那你就只能再给它发送不超过 100 字节的数据。如果你要发送的数据超过了 100 字节,抱歉,除去 100 字节剩下的部分就只能先暂存在本地的发送缓冲区中。

滑动窗口算法

关于流量控制的经典算法,就是滑动窗口算法了。
在TCP协议中,有一个窗口大小字段,它表示目前还能接收多少字节的数据。TCP每次收到对方送来的报文,都会检查窗口大小的字段。
图片说明
知道了对方的窗口大小后,就知道对方目前还能接收多少数据,接收的数据字节序号是 TCP 段中的 ack 的值到 ack + 窗口大小,即 [ack,ack+窗口大小)[ack,ack+窗口大小).

比如,你给对方发送了一个段携带字节序号为 [400, 500) 的数据。对方回送了一个 TCP 段,ack = 500, win = 100,就表示,我已经收到 [400, 500) 的数据我还能接收字节序号为 [500, 600) 之间的数据。
如果对方回送了一个 TCP 段,ack = 500, win = 0,就表示,我已经收到了 [400, 500) 的数据,但是我现在不能再接收数据了,你待会再发。

滑动窗口的目的

原因在于你的接受能力有限,不是说你无法记忆很多数字,只是在短期内,你记不住,你需要一段一段的记忆(一段一段的将数据放入缓冲区)。

所以,在 TCP 中,滑动窗口是为了实现流量控制。如果对方发送数据过快,接收方就来不及接收(你来不急记住),接收方就需要通告对方,减慢数据的发送。

拥塞控制算法

拥塞控制算法的目的,就是为了防止网络在拥塞的情况下,还在疯狂的向网络中发送大量数据。那么这里就有一个值得关心的问题:发送者,如何知道网络拥塞?

慢启动

在建立完连接后,发送方有办法知道网络的拥堵情况吗?显然不能,那怎样才能知道?想必你能猜出来,没错,只能试探。TCP 采取的策略就是试探,而且把这种方法取名为慢启动。

如果在发送过程中,遇到了重复 ACK 或者超时的情况,需要减慢发送速度:

  • 连续收到三次对方重复的ACK确认
  • 超时未收到ACK,说明极有可能阻塞
  • 对方可能没收到报文
  • 对方可能收到报文,但ACK丢了

一定要严格区分,重复 ACK 和超时这两种情况,它影响了 TCP 拥塞算法做何种决策!!!

慢启动算法

最经典的慢启动算法是这样的:
在程序中,维护一个变量cwnd,表示拥塞窗口大小,单位是字节。在最开始,cwnd有一个初始值,RFC 2581规定,它的大小不超过2MSS。为了方便以后的描述,当我说 cwnd = 2 时,实际上是说 cwnd = 2MSS,后面的 MSS 就省略掉。(MSS表示最大报文段长度,一般在1400字节左右)
为了方便描述这个算法,不妨约定 cwnd 初始值为 1(实际大多你看到的是 2)。

  • 先发送一个cwnd=1的报文。
  • 发送方每收到一个确认就把cwnd值加一。

图片说明

拥塞避免算法

为了防止慢启动过程中 cwnd 增长的过大,TCP 中还维护了另一个变量 ssthresh,单位为字节。它称之为慢启动门限,这是一个阈值,当 cwnd 超过这个值的时候,慢启动算法结束,进入拥塞避免算法!
这时候,TCP 发送 cwnd 个报文后,如果接收到了所有确认报文,cwnd 的值总和只是加 1,而不是加倍(也就是每收到一个确认报文,cwnd 加 1/cwnd)。这样,拥塞窗口 cwnd 就会按线性规律缓慢增长。
有文献将这个过程称为 “加法增大”。

<center>慢启动(中间出现超时)</center>

拥塞检测过程

无论是在慢启动阶段,还是在拥塞避免阶段,只要发送方判断网络可能出现拥塞(依据就是没有按时收到确认,或者收到三次重复的 ACK),就要把 ssthresh 设置为出现拥塞时的 cwnd 值的一半)。
对于超时和收到三次重复 ACK,需要分别进行考虑,这两者之间是有区别的,而且需要严格区分。、

  1. 超时(根据上图(慢启动出现超时)):
    如果计时器超时,出现拥塞的可能性就非常大(连重复的 ACK 都收不到),此时 TCP 反应强烈。
  • 这时候把ssthread设置为当前cwnd的值的一半
  • cwnd值再设置成1
  • 接下来重新从慢启动开始
    这样做的目的是要迅速减少主机发送到网络中的分组数,使得发生拥塞的中间设备有足够的时间把缓冲区中积压的分组处理完毕。
  1. 连续收到三次重复的 ACK
    初步可以判定网络没有拥塞,只是大概率丢失了一个报文。为什么能判定为没有拥塞呢?因为对方在收到失序报文的时候,就会立即返回一个 ACK(这种情况不受 Delay ACK 机制的影响,注意,是立即返回。)既然对方能一连串返回三个重复的 ACK,说明对方应该是连续收到三次的失序报文。你都能连续收到三次失序报文了,说明网络并不差。

失序报文:比如,接收方期望接收 100 号报文,但却收到了其它序号的报文(没有按照应该有的顺序收到)。
这个时候,发送方收到了三次重复ACK,应该立即重传丢失的报文,而不是等待重传计时器超时。这个策略被称为快重传。
发生快重传的时候,虽然网络可能没有拥塞,但是也要降低数据发送速率,只是 TCP 反应较弱,执行快恢复算法:

  • 把ssthread设置为当前的cwnd的值的一半
  • cwnd设置为ssthread
  • 进入拥塞避免阶段
    图片说明 <center>快速恢复算法</center>

MSS 与 MTU

MSS全称为Maximum Segment Size,即最大报文段长度。三次握手其中一个目的也是为了协商MSS的大小。
一般来说,TCP 报文段携带的数据当然是越多越好。
如果 TCP 报文段传输的数据只有一个字节,在 IP 层传输的数据报大小就是 40 + 1 = 41 字节(至少 20 字节的 IP 头 + 20 字节的 TCP 头 + 1 字节数据)。这样网络的利用率就只有 1/41. 传输 n 字节的数据利用率就是 n/(40+n),显然 TCP 报文段传输的数据如果越大,网络利用率就越高。
但是实际上并非如此。因为网络传输数据时,数据是最终是要交付到链路层协议上的,也就是说最后要封装成“帧”。二型以太网(Ethernet Type 2)中规定,帧的大小不能超过 1518 个字节(14 字节的帧头 + 4 字节帧校验和 + 最多 1500 字节数据)。所以 IP 数据报的大小如果超过了 1500 字节,要想交付给链路层就必须进行“分片”。
“分片”指的是一个 IP 数据报太大,需要拆分成一个一个的小段,变成多个 IP 数据报。这种分片显然是不利的,有一定的开销。为了避免分片开销,我们希望 IP 数据报的大小不超过 1500 字节。除去 IP 数据报的首部 20 字节,也就是希望 TCP 报文段不超过 1480 字节。再减去 TCP 报文段首部 20 字节,也就是 TCP 携带的数据不超过 1460 字节。
实际上,链路层对这种帧数据长度的限制称为最大传输单元(Maximum Transmission Unit, MTU)。不同的链路层协议,对 MTU 的值也有所规定,通常这个值可以进行更改。使用命令 netstat -i 可以看到自己的网卡的 MTU 值。
MSS和MTU之间关系:

  • MSS是软件层的概念,它由软件控制
  • MTU是硬件(比如网卡出口)的属性,是指二层链路层帧携带的数据最大大小。

再举个例子,MTU 的大小就好像一座桥的承重吨位,而桥就相当于网卡。事先给定 MSS,可以防止因为你货车载货过多,要进行分批运输。如果不指定 MSS,一旦你货车超载,吨位超过桥的承重能力,你就得把货拆分成几批运过去,运过去之后你还得组装,这是得不偿失的。

https://blog.csdn.net/q1007729991/article/details/70142341
https://gitbook.cn/books/5d74eca65903006d8a5192b2/index.html