窗口机制和拥塞避免算法

窗口机制

滑动窗口是 TCP 协议中非常重要的一个概念。

TCP 在解决可靠传输以及包乱序这个问题的时,引入了 Sliding Window 。

滑动窗口分为接受窗口和发送窗口。

8EBAD15B-5F50-4536-8E31-0FD0BBBCBB1D

TCP 头有一个字段叫 Window ,这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。Window 是一个 16bit 位字段,代表的是窗口字节容量,也就是TCP的标准窗口最大为 2^16 -1 = 65535 个字节。

7F44F015-BE82-4486-BAE6-3FD6EBE863AC

  • LastByteAcked 被接收端 Ack 过的位置,只要被 Ack 过才算是真正的发送成功
  • LastByteSent 表示已经发送了,但是没有收到 Ack
  • LastByteWritten 上层应用正在写的地方

  • LastByteRead 缓冲区中读到的位置
  • NextByteExpected 收到的连续包的最后一个位置
  • LastByteRcved 收到包的最后一个位置,中间可能有一些数据没有到达

  • 接收端在 ACK 中返回自己的 Window 为 最大缓存区大小 MaxRcvBuffer - LastByteRcvd -1
  • 发送端根据该字段大小控制数据发送的多少

对于发送方来说,任何时候它缓存区中的数据都可以分为四类:

  1. 已经发送并且收到了发送端 ACK 的
  2. 发送但是没有收到发送端 ACK 的
  3. 未发送但是发送端允许发送的
  4. 未发送且发送端不允许发送的

B4F9CEA8-7F2D-45BD-AE13-FC4C3A86590C

当收到发送端的 ACK 时,对之前的数据重新分类:

96CB0775-1CE7-4927-BADE-A09B6843455A

当收到的 ACK = 36 时窗口滑动。

拥塞避免算法

为什么会设计这么一个算法?

TCP 通过滑动窗口来控制流量,但是控制的仅仅是收发两端的流量数据,并不知道当前网络中发生了什么,如果不清楚当前网络的情况,当网络出现延时情况时,大量数据都需要重传,网络中的将出现非常多的重传数据,这样又会导致更多的包丢失。

TCP 就是像一个交通法则,管理约束着所有在公路网上行驶的车辆,避免出现大规模的堵塞现象。

该规则主要有四个算法:

(cwnd 全称 Congestion Window)

慢启动 Slow Start

表面意思比较好理解,

买了一个馒头,吃了之后发现不够饱。又买了两个,还是不够饱,再买四个吃… 别问我为什么一开始不买十个,一开始我也不知道我要吃几个才饱。只能是慢慢试探自己的胃。

0C04D101-E84D-4649-8249-547CAB75A118

具体一开始买几个吃,以及吃不饱之后再买几个吃,这个就比较有讲究了。目前知道是这个增长速度是指数形式的。

拥塞避免 Congestion Avoidance

ssthresh (slow start threshold),是一个上限,当 cwnd >= ssthresh 时,就会进入拥塞避免算法,该值一般是 65535 字节。

之前说过,慢算法是指数上涨,而拥塞避免算法是线性上涨。

还是吃馒头,当吃到一定个数时,觉得有点饱了,但是还没到完全吃不下的地步,可又能再按照之前的方式增加,否则就太浪费了,那么就调整算法,相对的减缓增加的数量。

拥塞发生

当 RTO (Retransmission TimeOut) 超时,重传数据包,此时参数发生改变:

  1. sshthresh = cwnd/2
  2. cwnd 重置为 1
  3. 重新进入慢启动过程

因为丢包现象严重时,需要快速减少在网络上传输的包,然后重新观察网络状况。

Fast Retransmit 算法,收到 3 个 duplicate ACK 时就开启重传,因为还能收到 ACK 所以认为没那么严重,减少的量也没上上面那种大:

  1. cwnd = cwnd/2
  2. sshthresh = cwnd
  3. 进入快速恢复算法 —— Fast Recovery

快速恢复 Fast Recovery

TCP Reno :

快速重传和快速恢复算法一般同时使用。快速恢复算法是认为,你还有 3 个 Duplicated Acks 说明网络也不那么糟糕,所以没有必要像 RTO 超时那么强烈。此前的参数:

  1. cwnd = cwnd/2
  2. sshthresh = cwnd

Fast Recovery 算法启动:

  1. cwnd = sshthresh + 3 * MSS ( 3 的意思是确认有 3 个数据包被收到了)
  2. 重传 Duplicated ACKs 指定的数据包
  3. 如果再收到 duplicated Acks,那么 cwnd = cwnd + 1
  4. 如果收到了新的 Ack,那么,cwnd = sshthresh ,然后就进入了拥塞避免的算法了

参考链接:

https://www.zhihu.com/question/32255109
http://www.tcpipguide.com/free/t_TCPSlidingWindowAcknowledgmentSystemForDataTranspo-6.htm
https://coolshell.cn/articles/11609.html
https://www.cnblogs.com/luoquan/p/4886345.html