计算机网络-TCP的流量控制

内容来源:小林coding

本文是对小林coding的TPC流量控制的精简总结


什么是流量控制

发送方不能无脑的发数据给接收方,要考虑接收方处理能力

如果一直无脑的发数据给对方,但对方处理不过来,那么就会导致触发重发机制

从而导致网络流量的无端的浪费

为了解决这种现象发生,TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制


操作系统缓冲区与滑动窗口的关系

前面的流量控制例子,我们假定了发送窗口和接收窗口是不变的

但是实际上,发送窗口和接收窗口中所存放的字节数,都是放在操作系统内存缓冲区中的

而操作系统的缓冲区,会被操作系统调整
当应用进程没办法及时读取缓冲区的内容时,也会对我们的缓冲区造成影响


操作系统缓冲区是如何影响发送窗口和接收窗口的呢?

例子一:应用程序没有及时读取缓存

当应用程序没有及时读取缓存时,发送窗口和接收窗口的变化。

考虑以下场景:

  • 客户端作为发送方,服务端作为接收方,发送窗口和接收窗口初始大小为 360;
  • 服务端非常的繁忙,当收到客户端的数据时,应用层不能及时读取数据

根据上图的流量控制,说明下每个过程:

  1. 客户端发送 140 字节数据后,可用窗口变为 220(360 - 140)。
  2. 服务端收到 140 字节数据,但是服务端非常繁忙,应用进程只读取了 40 个字节,还有 100 字节占用着缓冲区,于是接收窗口收缩到了 260(360 - 100),最后发送确认信息时,将窗口大小通告给客户端。
  3. 客户端收到确认和窗口通告报文后,发送窗口减少为 260。
  4. 客户端发送 180 字节数据,此时可用窗口减少到 80。
  5. 服务端收到 180 字节数据,但是应用程序没有读取任何数据,这 180 字节直接就留在了缓冲区,于是接收窗口收缩到了 80(260 - 180),并在发送确认信息时,通过窗口大小给客户端。
  6. 客户端收到确认和窗口通告报文后,发送窗口减少为 80。
  7. 客户端发送 80 字节数据后,可用窗口耗尽。
  8. 服务端收到 80 字节数据,但是应用程序依然没有读取任何数据,这 80 字节留在了缓冲区,于是接收窗口收缩到了 0,并在发送确认信息时,通过窗口大小给客户端。
  9. 客户端收到确认和窗口通告报文后,发送窗口减少为 0。

简单总结

1.应用进程没有及时读取部分数据,导致数据暂时留在了缓冲区

2.为了让接收窗口能成功我们缓冲区中还没读取的数据,所以我们的接收窗口要变小,告诉发送端我们不能再接收大于这个量的数据了(接收大于这个量的数据我们会丢失)

3.然后我们把接收端改变的窗口大小发送给发送端,发送窗口大小减少

可见最后窗口都收缩为 0 了,也就是发生了窗口关闭。

当发送方可用窗口变为 0 时,发送方实际上会定时发送窗口探测报文,以便知道接收方的窗口是否发生了改变


例子二:系统资源紧张然后减少了接收缓冲区大小,导致数据包丢失

当服务端系统资源非常紧张的时候,操作系统可能会直接减少了接收缓冲区大小

这时应用程序又无法及时读取缓存数据,那么这时候就有严重的事情发生了,会出现数据包丢失的现象

说明下每个过程:

  1. 客户端发送 140 字节的数据,于是可用窗口减少到了 220。
  2. 服务端因为现在非常的繁忙,操作系统于是就把接收缓存减少了 120 字节,当收到 140 字节数据后,又因为应用程序没有读取任何数据,所以 140 字节留在了缓冲区中,于是接收窗口大小从 360 收缩成了 100,最后发送确认信息时,通告窗口大小给对方。
  3. 此时客户端因为还没有收到服务端的通告窗口报文,所以不知道此时接收窗口收缩成了 100,客户端只会看自己的可用窗口还有 220,所以客户端就发送了 180 字节数据,于是可用窗口减少到 40。
  4. 服务端收到了 180 字节数据时,发现数据大小超过了接收窗口的大小,于是就把数据包丢失了
  5. 客户端收到第 2 步时,服务端发送的确认报文和通告窗口报文,尝试减少发送窗口到 100,把窗口的右端向左收缩了 80,此时可用窗口的大小就会出现诡异的负值。

所以,如果发生了先减少缓存,再收缩窗口,就会出现丢包的现象

为了防止这种情况发生,TCP 规定是不允许同时减少缓存又收缩窗口的

而是采用先收缩窗口,过段时间再减少缓存

这样就可以避免了丢包情况


窗口关闭

什么是窗口关闭

在前面我们都看到了,TCP 通过让接收方指明希望从发送方接收的数据大小(窗口大小)来进行流量控制

如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,直到窗口变为非 0 为止

这就是窗口关闭


窗口关闭的潜在危险(ACK报文丢失导致发送方一直等待非0窗口通知)

接收方向发送方通告窗口大小时,是通过 ACK 报文来通告的

那么,当发生窗口关闭时,接收方处理完数据后,会向发送方通告一个窗口非 0 的 ACK 报文

如果这个通告窗口的 ACK 报文在网络中丢失了,那麻烦就大了

这会导致发送方一直等待接收方的非 0 窗口通知,接收方也一直等待发送方的数据

如不采取措施,这种相互等待的过程,会造成了死锁的现象


TCP是如何解决窗口关闭时潜在的死锁现象的(通过持续计数器)

为了解决这个问题,TCP 为每个连接设有一个持续定时器

只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器。

如果持续计时器超时,就会发送窗口探测(Window probe)报文

而对方在确认这个探测报文时,给出自己现在的接收窗口大小

  • 如果接收窗口仍然为 0,那么收到这个报文的一方就会重新启动持续计时器
  • 如果接收窗口不是 0,那么死锁的局面就可以被打破了

窗口探测的次数一般为 3 次,每次大约 30 - 60 秒(不同的实现可能会不一样)

如果 3 次过后接收窗口还是 0 的话,有的 TCP 实现就会发 RST 报文来中断连接


糊涂窗口综合症

什么是糊涂窗口综合症

如果接收方太忙来不及取走接收窗口里的数据,那么就会导致发送方的发送窗口越来越小

到最后,如果接收方腾出几个字节并告诉发送方现在有几个字节的窗口

而发送方会义无反顾地发送这几个字节

这就是糊涂窗口综合症(接收方一有空间,发送方就会拼尽全力发送去争夺接收方那一点空间)


糊涂窗口综合症带来的问题

要知道,我们的 TCP + IP 头有 40 个字节,为了传输那几个字节的数据,要搭上这么大的开销,这太不经济了

就好像一个可以承载 50 人的大巴车,每次来了一两个人,就直接发车

除非家里有矿的大巴司机,才敢这样玩,不然迟早破产

要解决这个问题也不难,大巴司机等乘客数量超过了 25 个,才认定可以发车


糊涂窗口综合症的场景例子

现举个糊涂窗口综合症的栗子,考虑以下场景:

接收方的窗口大小是 360 字节,但接收方由于某些原因陷入困境,假设接收方的应用层读取的能力如下:

  • 接收方每接收 3 个字节,应用程序就只能从缓冲区中读取 1 个字节的数据(每次只能读三分之一)
  • 在下一个发送方的 TCP 段到达之前,应用程序还从缓冲区中读取了 40 个额外的字节;

每个过程的窗口大小的变化,在图中都描述的很清楚了,可以发现窗口不断减少了,并且发送的数据都是比较小的了。

总结:也就是我们的窗口不断减小,导致我们能发送的数据都在不断减小,用更多的请求发更少的数据造成了资源浪费

所以,糊涂窗口综合症的现象是可以发生在发送方和接收方:

  • 接收方可以通告一个小的窗口
  • 而发送方可以发送小数据

如何解决糊涂窗口综合症

要解决糊涂窗口综合症,就要同时解决上面两个问题:

  • 让接收方不通告小窗口给发送方
  • 让发送方避免发送小数据

MSS(Maximum Segment Size)最大报文段长度,指的是 TCP 报文段中数据部分的最大长度。它决定了每次传输的数据块大小,与网络传输效率相关。

缓存空间:接收方用于存储接收到但尚未被应用程序读取的数据的内存空间


如何让接收方不通告小窗口

接收方通常的策略如下:
当「窗口大小」小于 min (MSS,缓存空间 / 2),也就是小于 MSS 与 1/2 缓存大小中的最小值时会向发送方通告窗口为 0,也就阻止了发送方再发数据过来。
 

等到接收方处理了一些数据后,窗口大小 >=MSS,或者接收方缓存空间有一半可以使用 就可以把窗口打开让发送方发送数据过来


如何让发送方避免发送小数据

发送方通常的策略如下:
使用 Nagle 算法,该算法的思路是延时处理,只有满足下面两个条件中的任意一个条件,才可以发送数据:

  • 条件一:要等到窗口大小 >= MSS 并且数据大小 >= MSS
  • 条件二:收到之前发送数据的 ack 回包

只要上面两个条件都不满足,发送方一直在囤积数据,直到满足上面的发送条件

Nagle 伪代码如下:

if 有数据要发送 {if 可用窗口大小 >= MSS and 可发送的数据 >= MSS {立刻发送MSS大小的数据} else {if 有未确认的数据 {将数据放入缓存等待接收ACK} else {立刻发送数据}}
}

PS:如果接收方不能满足「不通告小窗口给发送方」,那么即使开了 Nagle 算法,也无法避免糊涂窗口综合症

因为如果对端 ACK 回复很快的话(达到 Nagle 算法的条件二),Nagle 算法就不会拼接太多的数据包,这种情况下依然会有小数据包的传输,网络总体的利用率依然很低。

所以,接收方得满足「不通告小窗口给发送方」+ 发送方开启 Nagle 算法,才能避免糊涂窗口综合症

另外,Nagle 算法默认是打开的,如果对于一些需要小数据包交互的场景的程序,比如,telnet 或 ssh 这样的交互性比较强的程序,则需要关闭 Nagle 算法。

可以在 Socket 设置 TCP_NODELAY 选项来关闭这个算法(关闭 Nagle 算法没有全局参数,需要根据每个应用自己的特点来关闭)

setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&value, sizeof(int));

简单总结一下流量控制

什么是流量控制

发送方不能无脑的发数据给接收方,要考虑接收方处理能力

不然就会导致触发重发机制造成网络流量的无端浪费


发送窗口和接收窗口中所存放的字节数,都是放在操作系统内存缓冲区中的,所以说和缓冲区大小有关

操作系统缓冲区影响发送窗口和接收窗口(未及时读取or资源紧张导致窗口收缩)

应用程序没有及时读取缓存

因为应用层没有及时读取数据导致我们的接收窗口的量不断减少

因为接收窗口不断减少,导致我们的发送窗口量不断减少

系统资源紧张然后减少了接收缓冲区大小,导致数据包丢失

如果发生了先减少缓存,再收缩窗口,就会出现丢包的现象

为了防止这种情况发生,TCP 规定是不允许同时减少缓存又收缩窗口的

而是采用先收缩窗口,过段时间再减少缓存

这样就可以避免了丢包情况


窗口关闭

什么是窗口关闭

如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,直到窗口变为非 0 为止

窗口关闭的危险

当发生窗口关闭时,接收方处理完数据后,会向发送方通告一个窗口非 0 的 ACK 报文,如果这个通告窗口的 ACK 报文在网络中丢失了,这会导致发送方一直等待接收方的非 0 窗口通知,接收方也一直等待发送方的数据

如不采取措施,这种相互等待的过程,会造成了死锁的现象

TCP是如何解决窗口关闭时潜在的死锁现象的(通过持续计数器)

TCP 为每个连接设有一个持续定时器

只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器

如果持续计时器超时,就会发送窗口探测(Window probe)报文

  • 如果接收窗口仍然为 0,那么收到这个报文的一方就会重新启动持续计时器
  • 如果接收窗口不是 0,那么死锁的局面就可以被打破了

窗口探测的次数一般为 3 次,每次大约 30 - 60 秒(不同的实现可能会不一样)

如果 3 次过后接收窗口还是 0 的话,有的 TCP 实现就会发 RST 报文来中断连接


窗口糊涂综合症

什么是窗口糊涂综合征

(接收方一有空间,发送方就会拼尽全力发送去争夺接收方那一点空间)

窗口糊涂综合征带来的问题

太浪费了,那么点空间我们还拼尽全力去发送还发送不完全

一个可以承载 50 人的大巴车,每次来了一两个人,就直接发车

除非家里有矿的大巴司机,才敢这样玩,不然迟早破产

要解决这个问题也不难,大巴司机等乘客数量超过了 25 个,才认定可以发车

糊涂窗口综合症是怎么发生的

糊涂窗口综合症的现象是可以发生在发送方和接收方:

  • 接收方可以通告一个小的窗口
  • 而发送方可以发送小数据

如何解决糊涂窗口综合征

两个问题,两个概念

我们要解决两个问题:

1.让接收方不通告小窗口给发送方

2.让发送方避免发送小数据

两个概念

1.MSS(Maximum Segment Size)最大报文段长度,指的是 TCP 报文段中数据部分的最大长度。它决定了每次传输的数据块大小,与网络传输效率相关

2.缓存空间:接收方用于存储接收到但尚未被应用程序读取的数据的内存空间


如何让接收方不通告小窗口

当「窗口大小」小于 min (MSS,缓存空间 / 2),也就是小于 MSS 与 1/2 缓存大小中的最小值时会向发送方通告窗口为 0,也就阻止了发送方再发数据过来。
 

等到接收方处理了一些数据后,窗口大小 >=MSS,或者接收方缓存空间有一半可以使用 就可以把窗口打开让发送方发送数据过来


如何让发送方避免发送小数据

使用 Nagle 算法,该算法的思路是延时处理,只有满足下面两个条件中的任意一个条件,才可以发送数据:

  • 条件一:要等到窗口大小 >= MSS 并且数据大小 >= MSS
  • 条件二:收到之前发送数据的 ack 回包

PS:如果接收方不能满足「不通告小窗口给发送方」,那么即使开了 Nagle 算法,也无法避免糊涂窗口综合症

因为如果对端 ACK 回复很快的话(达到 Nagle 算法的条件二),Nagle 算法就不会拼接太多的数据包,这种情况下依然会有小数据包的传输,网络总体的利用率依然很低。

所以,接收方得满足「不通告小窗口给发送方」+ 发送方开启 Nagle 算法,才能避免糊涂窗口综合症

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/76633.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot 七种事务传播行为只有 REQUIRES_NEW 和 NESTED 支持部分回滚的分析

Spring Boot 七种事务传播行为支持部分回滚的分析 支持部分回滚的传播行为 REQUIRES_NEW:始终开启新事务,独立于外部事务,失败时仅自身回滚。NESTED:在当前事务中创建保存点(Savepoint),可局部…

突破反爬困境:SDK开发,浏览器模块(七)

声明 本文所讨论的内容及技术均纯属学术交流与技术研究目的,旨在探讨和总结互联网数据流动、前后端技术架构及安全防御中的技术演进。文中提及的各类技术手段和策略均仅供技术人员在合法与合规的前提下进行研究、学习与防御测试之用。 作者不支持亦不鼓励任何未经授…

C++数据排序( 附源码 )

一.冒泡排序 原理:自左向右依次遍历,若相邻两数顺序错误,则交换两数. 这样,每一轮结束后,最大/最小的数就会到最后. Code: #include <iostream> #include <cstdio> using namespace std; const int N1e51; int n,a[N],in; void PrintArray(int a[],int n){for…

I2C 读写 AT24C02

根据AT24C02的 Datasheet 可知AT24C02有2K bit&#xff0c;即256B&#xff0c;分为32页,每页8个字节&#xff0c;结合数据手册和原理图可以得知&#xff0c;板载AT24C02的读地址为0xA2&#xff0c;写地址为0xA3&#xff1a; #define AT24C02_ADDR_WRITE 0xA2 #define AT24C02_…

K8S学习之基础七十四:部署在线书店bookinfo

部署在线书店bookinfo 在线书店-bookinfo 该应用由四个单独的微服务构成&#xff0c;这个应用模仿在线书店的一个分类&#xff0c;显示一本书的信息&#xff0c;页面上会显示一本书的描述&#xff0c;书籍的细节&#xff08;ISBN、页数等&#xff09;&#xff0c;以及关于这本…

Linux 查找文本中控制字符所在的行

参考资料 ASCIIコード表 目录 一. 业务背景二. 遇到的问题三. 分析3.1 url编码的前置知识3.2 出现控制字符的transactionid分析3.3 16进制分析 四. 从文本中查找控制字符所在的行五. 控制字符一览 一. 业务背景 ⏹在项目中&#xff0c;业务请求对应着下URL http://www.test.…

python将pdf文件转为图片,如果pdf文件包含多页,将转化的多个图片通过垂直或者水平合并成一张图片

要将PDF文件转换为图片&#xff0c;并将多页PDF垂直合并成一张图片&#xff0c;可以使用PyMuPDF&#xff08;也称为fitz&#xff09;库来读取PDF文件&#xff0c;并使用Pillow库来处理和合并图片。以下是一个示例代码&#xff0c;展示了如何实现这个功能&#xff1a; 首先&…

HarmonyOS 基础组件和基础布局的介绍

1. HarmonyOS 基础组件 1.1 Text 文本组件 Text(this.message)//文本内容.width(200).height(50).margin({ top: 20, left: 20 }).fontSize(30)//字体大小.maxLines(1)// 最大行数.textOverflow({ overflow: TextOverflow.Ellipsis })// 超出显示....fontColor(Color.Black).…

FrameWork基础案例解析(四)

文章目录 单独拉取framework开机与开机动画横屏Android.mk语法单独编译SDKmake 忽略warning单独修改和编译Camera2单独编译Launcher3Android Studio 导入、修改、编译Settings导入 Android Studio 导入、修改、编译Launcher3android 开机默认进入指定Launcher植入自己的apk到系…

基于vscode(GDB)调试ros2节点

一、环境准备 必备vscode插件 1&#xff09;Docker Docker - Visual Studio Marketplace 2&#xff09;Dev Containers Dev Containers - Visual Studio Marketplace 3&#xff09;GDB GDB Debug - Visual Studio Marketplace 二、进去docker镜像 1&#xff09;docker安…

基于springboot的考研成绩查询系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 这些年随着Internet的迅速发展&#xff0c;我们国家和世界都已经进入了互联网大数据时代&#xff0c;计算机网络已经成为了整个社会以及经济发展的巨大动能&#xff0c;考研成绩查询管理事务现在已经成为社会关注的重要内容&#xff0c;因此运用互联网技术来提高考研成绩…

C++:算术运算符

程序员Amin &#x1f648;作者简介&#xff1a;练习时长两年半&#xff0c;全栈up主 &#x1f649;个人主页&#xff1a;程序员Amin &#x1f64a; P   S : 点赞是免费的&#xff0c;却可以让写博客的作者开心好久好久&#x1f60e; &#x1f4da;系列专栏&#xff1a;Java全…

PyQt6实例_A股日数据维护工具_使用

目录 前置&#xff1a; 下载预备更新的数据 使用工具更新 用工具下载未复权、前复权、权息数据 在PostgreSQL添加两个数据表 工具&视频 前置&#xff1a; 1 本系列将以 “PyQt6实例_A股日数据维护工具” 开头放置在“PyQt6实例”专栏 2 日数据可在“数据库”专栏&…

REST 方法

FUNCTION ZFM_INTERFACE_LOG. *"---------------------------------------------------------------------- *"*"本地接口&#xff1a; *" IMPORTING *" REFERENCE(IV_DSTART) TYPE EDI_UPDDAT *"---------------------------------------…

QT 中的元对象系统(五):QMetaObject::invokeMethod的使用和实现原理

目录 1.简介 2.原理概述 3.实现分析 3.1.通过方法名调用方法的实现分析 3.2.通过可调用对象调用方法的实现分析 4.使用场景 5.总结 1.简介 QMetaObject::invokeMethod 是 Qt 框架中的一个静态方法&#xff0c;用于在运行时调用对象的成员函数。这个方法提供了一种动态调…

Unity3D开发AI桌面精灵/宠物系列 【三】 语音识别 ASR 技术、语音转文本多平台 - 支持科大讯飞、百度等 C# 开发

Unity3D 交互式AI桌面宠物开发系列【三】ASR 语音识别 该系列主要介绍怎么制作AI桌面宠物的流程&#xff0c;我会从项目开始创建初期到最终可以和AI宠物进行交互为止&#xff0c;项目已经开发完成&#xff0c;我会仔细梳理一下流程&#xff0c;分步讲解。 这篇文章主要讲有关于…

Java 状态模式 详解

状态模式详解 一、状态模式概述 状态模式(State Pattern)是一种行为型设计模式&#xff0c;它允许一个对象在其内部状态改变时改变它的行为&#xff0c;使对象看起来似乎修改了它的类。 核心特点 状态封装&#xff1a;将每个状态的行为封装到独立的类中状态转换&#xff1a…

Nginx 配置 HTTPS 与 WSS 完整指南

Nginx 配置 HTTPS 与 WSS 完整指南 本教程将手把手教你如何为网站配置 HTTPS 加密访问&#xff0c;并通过反向代理实现安全的 WebSocket&#xff08;WSS&#xff09;通信。以 https://www.zhegepai.cn 域名为例&#xff0c;完整流程约需 30 分钟完成。 一、前置准备 1.1 域名…

双向链表的理解

背景 代码中经常会出现双向链表&#xff0c;对于双向链表的插入和删除有对应的API函数接口&#xff0c;但直观的图表更容易理解&#xff0c;所以本文会对rt-thread内核代码中提供的双向链表的一些API函数操作进行绘图&#xff0c;方便后续随时查看。 代码块 rt-thread中提供…

大文件上传源码,支持单个大文件与多个大文件

大文件上传源码&#xff0c;支持单个大文件与多个大文件 Ⅰ 思路Ⅱ 具体代码前端--单个大文件前端--多个大文件前端接口后端 Ⅰ 思路 具体思路请参考我之前的文章&#xff0c;这里分享的是上传流程与源码 https://blog.csdn.net/sugerfle/article/details/130829022 Ⅱ 具体代码…