计算机网络-TCP的拥塞控制

内容来源:小林coding

本文是对小林coding的TPC拥塞控制的精简总结


为什么要有拥塞控制?

前面的流量控制是避免「发送方」的数据填满「接收方」的缓存,但是并不知道网络的中发生了什么

计算机网络都处在一个共享的环境,因此也有可能会因为其他主机之间的通信使得网络拥堵

在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大....

所以,TCP 不能忽略网络上发生的事,它被设计成一个无私的协议

当网络发送拥塞时,TCP 会自我牺牲,降低发送的数据量

拥塞控制的目的:避免「发送方」的数据填满整个网络

为了在「发送方」调节所要发送数据的量,定义了一个叫做「拥塞窗口」的概念


什么是拥塞窗口?和发送窗口有什么关系呢?

什么是拥塞窗口

拥塞窗口 cwnd 是发送方维护的一个的 状态变量

它会根据网络的拥塞程度 动态变化

我们在前面提到过发送窗口 swnd 和接收窗口 rwnd 是约等于的关系,那么由于加入了拥塞窗口的概念后

此时发送窗口的值是 swnd = min (cwnd, rwnd)

也就是拥塞窗口和接收窗口中的最小值


拥塞窗口 cwnd 变化的规则

1.只要网络中没有出现拥塞,cwnd 就会增大

2.但网络中出现了拥塞,cwnd 就减少


怎么判断当前网络出现了拥塞

其实只要「发送方」没有在规定时间内接收到 ACK 应答报文

也就是发生了 超时重传 ,就会认为网络出现了拥塞


拥塞控制有哪些控制算法

拥塞控制主要是四个算法:

  • 慢启动
  • 拥塞避免
  • 拥塞发生
  • 快速恢复

慢启动

慢启动是什么意思?

TCP 在刚建立连接完成后,首先是有个慢启动的过程

慢启动的意思:一点一点的提高发送数据包的数量

如果一上来就发大量的数据,这不是给网络添堵吗?


慢启动算法规则

慢启动的算法记住一个规则就行:

当发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1


图解例子

这里假定拥塞窗口 cwnd 和发送窗口 swnd 相等,下面举个栗子:

  • 连接建立完成后,一开始初始化 cwnd = 1,表示可以传一个 MSS 大小的数据。
  • 当收到一个 ACK 确认应答后,cwnd 增加 1,于是一次能够发送 2 个。
  • 当收到 2 个的 ACK 确认应答后,cwnd 增加 2,于是就可以比之前多发 2 个,所以这一次能够发送 4 个。
  • 当这 4 个的 ACK 确认到来的时候,每个确认 cwnd 增加 1,4 个确认 cwnd 增加 4,于是就可以比之前多发 4 个,所以这一次能够发送 8 个。

慢启动算法的变化过程如下图:

可以看出慢启动算法,发包的个数是指数性的增长


慢启动门限(我们慢启动增长到啥时候停止?)

有一个叫慢启动门限 ssthresh(slow start threshold)状态变量。

  • 当 cwnd < ssthresh 时,使用慢启动算法。
  • 当 cwnd >= ssthresh 时,就会使用「拥塞避免算法」

拥塞避免算法

怎么进入进入拥塞避免算法

前面说道,当拥塞窗口 cwnd「超过」慢启动门限 ssthresh 就会进入拥塞避免算法

一般来说 ssthresh 的大小是 65535 字节


拥塞避免算法规则

那么进入拥塞避免算法后,它的规则是:每当收到一个 ACK 时,cwnd 增加 1/cwnd


图解例子

接上前面的慢启动的栗子,现假定 ssthresh 为 8:

  • 当 8 个 ACK 应答确认到来时,每个确认增加 1/8,8 个 ACK 确认 cwnd 一共增加 1,于是这一次能够发送 9 个 MSS 大小的数据,变成了线性增长。

拥塞避免算法的变化过程如下图:

所以,我们可以发现,拥塞避免算法就是将原本慢启动算法的指数增长变成了线性增长,还是增长阶段,但是增长速度缓慢了一些

就这么一直增长着后,网络就会慢慢进入了拥塞的状况了,于是就会出现丢包现象,这时就需要对丢失的数据包进行重传。

当触发了重传机制,也就进入了「拥塞发生算法」


拥塞发生算法

数据包重传的两种机制

当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种:

  • 超时重传
  • 快速重传

【超时重传】的拥塞发生算法规则

当发生了「超时重传」,则就会使用拥塞发生算法

这个时候,ssthresh 和 cwnd 的值会发生变化:

  • ssthresh 设为 cwnd/2,
  • cwnd 重置为 1(是恢复为 cwnd 初始化值,我这里假定 cwnd 初始化值 1)

怎么查看系统的cwnd初始值

Linux 针对每一个 TCP 连接的 cwnd 初始化值是 10,也就是 10 个 MSS

我们可以用 ss -nli 命令查看每一个 TCP 连接的 cwnd 初始化值

如下图


图解

接着,就重新开始慢启动,慢启动是会突然减少数据流的

这真是一旦「超时重传」,马上回到解放前。但是这种方式太激进了,反应也很强烈,会造成网络卡顿。

就好像本来在秋名山高速漂移着,突然来个紧急刹车,轮胎受得了吗。。。


【快速重传】的拥塞发生算法

还有更好的方式,前面我们讲过「快速重传算法」

当接收方发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速地重传,不必等待超时再重传。

TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,则 ssthresh 和 cwnd 变化如下:

  • cwnd = cwnd/2,也就是设置为原来的一半;
  • ssthresh = cwnd;
  • 进入快速恢复算法

快速恢复

快速恢复算法规则

快速重传和快速恢复算法一般同时使用

快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕

所以没有必要像 RTO 超时时那么强烈

正如前面所说,进入快速恢复之前,cwnd 和 ssthresh 已被更新了:

  • cwnd = cwnd/2,也就是设置为原来的一半;
  • ssthresh = cwnd;

快速恢复算法规则

然后,进入快速恢复算法如下:

  • 拥塞窗口 cwnd = ssthresh + 3 (3 的意思是确认有 3 个数据包被收到了);
  • 重传丢失的数据包;
  • 如果再收到重复的 ACK,那么 cwnd 增加 1;
  • 如果收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值,原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态;

图解

也就是没有像「超时重传」一夜回到解放前,而是还在比较高的值,后续呈线性增长


简单总结一下拥塞控制

为什么要有拥塞控制

流量控制是避免「发送方」的数据填满「接收方」的缓存,但是并不知道网络的中发生了什么

在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大....

当网络发送拥塞时,TCP 会自我牺牲,降低发送的数据量

拥塞控制的目的:避免「发送方」的数据填满整个网络


什么是拥塞窗口

拥塞窗口 cwnd 是发送方维护的一个的 状态变量, 它会根据网络的拥塞程度 动态变化

发送窗口 swnd

接收窗口 rwnd

这两个窗口是约等于的关系

发送窗口的值拥塞窗口和接收窗口 中的最小值, swnd = min (cwnd, rwnd)

拥塞窗口 cwnd 变化的规则

1.只要网络中没有出现拥塞,cwnd 就会增大

2.但网络中出现了拥塞,cwnd 就减少

怎么判断当前网络出现了拥塞

「发送方」没有在规定时间内接收到 ACK 应答报文

也就是发生了 超时重传 ,就会认为网络出现了拥塞


拥塞控制有哪些算法

拥塞控制主要是四个算法:

  • 慢启动
  • 拥塞避免
  • 拥塞发生
  • 快速恢复

慢启动

什么是慢启动

CP 在刚建立连接完成后,首先是有个慢启动的过程

慢启动的意思:一点一点的提高发送数据包的数量

如果一上来就发大量的数据,这不是给网络添堵吗?


慢启动规则

发送方每收到一个 ACK,拥塞窗口cwnd 的大小就会加 1


慢启动门限

慢启动门限 :ssthresh(slow start threshold)状态变量

  • 当 cwnd < ssthresh 时(拥塞窗口小于慢启动门限时),使用慢启动算法
  • 当 cwnd >= ssthresh 时(拥塞窗口大于等于慢启动门限时),就会使用「拥塞避免算法」

拥塞避免

如何进入拥塞避免

当拥塞窗口 cwnd「超过」慢启动门限 ssthresh 就会进入拥塞避免算法

拥塞避免规则

每当收到一个 ACK 时, cwnd 增加 1/cwnd

拥塞避免算法就是将原本慢启动算法的指数增长变成了线性增长,还是增长阶段,但是增长速度缓慢了一些


重传机制的拥塞发生规则

数据包的两种重传机制

  • 超时重传
  • 快速重传

【超时重传】的拥塞发生算法规则

「超时重传」会使用拥塞发生算法

ssthresh(慢启动门限) 和 cwnd(拥塞窗口) 的值会发生变化:

  • 慢启动门限=拥塞窗口/2
  • 拥塞窗口重置为 1 (是恢复为 cwnd 初始化值,我这里假定 cwnd 初始化值 1)

如何查看TPC连接的CWND初始化值

复制代码
ss -nli

【快速重传】的拥塞发生算法规则

之前的「快速重传算法」

当接收方发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速地重传,不必等待超时再重传

TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,则 ssthresh 和 cwnd 变化如下:

  • 拥塞窗口变为原来的一半;
  • 慢启动门限=拥塞窗口;
  • 进入快速恢复算法

快速恢复

为什么需要快速恢复

慢启动是会突然减少数据流的

这真是一旦「超时重传」,马上回到解放前。但是这种方式太激进了,反应也很强烈,会造成网络卡顿


什么是快速恢复

快速重传和快速恢复算法一般同时使用

快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕

所以没有必要像 RTO 超时时那么强烈


快速恢复算法规则

快速恢复的前提步骤:

ssthresh(慢启动门限) 和 cwnd(拥塞窗口) 的值会发生变化:

  • ssthresh 慢启动门限=拥塞窗口/2
  • cwmd 拥塞窗口重置为 1 (是恢复为 cwnd 初始化值,我这里假定 cwnd 初始化值 1)

快速恢复算法

  • 拥塞窗口 cwnd = ssthresh + 3 (3 的意思是确认有 3 个数据包被收到了);
  • 重传丢失的数据包;
  • 如果再收到重复的 ACK,那么 cwnd 增加 1;
  • 如果收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值 ,原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态

图解总结

相关推荐
forestsea9 小时前
HTTP 黑科技
科技·网络协议·http
网络安全天地9 小时前
使用 Flutter 制作地图应用
websocket·网络协议·tcp/ip·http·网络安全·https·udp
to future_9 小时前
非阻塞IO,fcntl,多路转接,select,poll,epoll,reactor
linux·网络协议
榆榆欸9 小时前
14.主从Reactor+线程池模式,Connection对象引用计数的深入分析
linux·服务器·网络·c++·tcp/ip
车载测试工程师9 小时前
ARXML文件解析-1
经验分享·网络协议·tcp/ip·车载系统
JavaEdge.10 小时前
ssl.SSLCertVerificationError报错解决方案
网络·网络协议·ssl
kfepiza10 小时前
`accept_ra` 和 `autoconf` 和 `forwarding` 的关系 笔记250404
linux·网络·笔记·tcp/ip·智能路由器·ip·tcp
JhonKI11 小时前
【从零实现Json-Rpc框架】- 项目实现 - 客户端注册主题整合 及 rpc流程示意
c++·qt·网络协议·rpc·json
小宁爱Python12 小时前
Python从入门到精通4:计算机网络及TCP网络应用程序开发入门指南
网络·python·tcp/ip·计算机网络