【网络原理】TCP协议提高效率的秘密-滑动窗口机制

news2024/11/24 20:46:48

💐个人主页:初晴~

📚相关专栏:计算机网络那些事


        如果我们严格依照“确认应答”机制,针对每一个发送的数据段,都需要一个ACK确认应答,当收到ACK应答报文后,才继续发下一个报文。这样的发送效率势必是非常低下的,某个数据传输时间过长时,就会直接影响后续所有数据包的传输,传输性能非常差。

为了应对这一问题,尽可能地提高tcp传输的效率,于是就引入了“滑动窗口”机制。

一、何为滑动窗口

说的通俗一点就是把 “发送一个等待一个” 变成 “发送一批等待一批” 。相当于将多次等待的时间重叠在一起了。将等待时间从所有报文等待时间之和转变为了这批报文中最长的等待时间,效率肉眼可见地提升了许多。

理论上,批量发送的数据越多,效率也越高。但是理想很美好,现实很骨感。受网络带宽、接收方数据处理能力等的影响,不可能会让发送端无限制的扩大窗口的。

于是,实际应用中,在TCP头部的 “窗口大小” 字段中,接收端会告知发送端其当前可用的缓冲区空间。这个值是以字节为单位的,表示接收端还能接受多少字节的数据。通常来讲,窗⼝越⼤, 则⽹络的吞吐率就越⾼,传输的效率越高。

上图中的窗口大小就为4000字节。

 那么发送端是等到这一批数据全部传输完再传下一批吗?

事实上并不是这样的。这样也体现不出滑动窗口的 “滑动” 了。我们接着以窗口大小为4000,每个数据段长度为1000时举例:

  • 发送前四个数据段的时候,不需要等待任何ACK,直接发送
  • 当第四个数据段发送完后,发送端发现 发送缓冲区 内的数据已经达到窗口大小了,就会停止继续发送,开始等待 ACK
  • 收到第⼀个 2001 ACK后,说明 1001~2000 的数据得到应答了,滑动窗⼝就会向后滑动,继续发送第五个段 5001~6000 的数据,依次类推
操作系统内核为了维护这个滑动窗⼝,需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答。只有确认应答过的数据,才能从缓冲区删掉

 像这样,就保证了在传输过程中,窗口的大小保持一定,窗口所在的位置不断滑动的效果了。最大程度上利用了窗口的资源,达到最佳效率。


二、滑动窗口的一些优化机制

1、快速重传

我们得注意,完成上述滑动窗口的前提条件是可靠性。但现实中网络传输受种种因素的影响,很可能会出现丢包的现象。应对丢包问题,又该做出哪些优化?让我们接着往下看

情况一:数据包成功抵达,但ACK丢包了

事实上,这种情况对后续传输的进行不会有啥影响。

为啥呢?让我们回忆一下确认序号的含义:表示的是接收端接收到的数据的最后一个字节的序号+1。也就是说,确认序号之前的所有数据,都已经成功送达了

假如说此时确认序号为 1001 的ACK丢包了,但是 2001 的ACK成功送到。发送方收到 2001 后,就意味着着序号为 2001 之前的所有数据都已经收到了。

我们不难发现,后一个 ACK 可以涵盖前一个 ACK 的意义。不管返回的 ACK 再怎么丢包,也不可能全部都丢了,总有包能成功送达 发送端,只要接收到某一个 ACK 包,就能确定在其之前的数据全都送达了,这样在这之前无论丢了多少 ACK包 都不会有啥影响了。

情况二:数据包出现丢包

如上图,B 收到了数据 1~1000、2001~3000,其中 1001~2000 的数据发生了丢失。

此时,当 B 收到数据 2001~3000 的时候,返回的 ACK 的确认序号不是3001,而是1001。

这也很好理解,因为1001~2000数据的丢包,3001之前的数据并没有全部都收到,只有序号 1001 之前的所有数据才全部被收到了

而接下去,B 收到的 3001~4000/4001~5000等数据返回的确认序号也都是 1001,就好像在提醒发送端“我要的是1001”一样。

这样,作为发送端的 A,所看到的情况就是连续收到了好几个确认序号为1001的ACK。当连续三次收到确认序号相同的 ACK时,就会重新发送对应数据。

当 1001~2000 的数据重传过来后,由于之前的 2001~7000的数据都以成功接收,1001~2000 相当于补全了之前的空缺。此时对于接收端而言,就意味着1~7000 的数据都收到了,于是接下来返回的 ACK中的确认序号就为 7001 了。

上述操作行云流水,快速地定位到了是哪个数据包发生了丢包,并针对性地重传,其它顺利到达的数据会在接收端的缓冲区中等待,无序重传。这种机制就被称之为 "快速重传"(也叫 "⾼速重发控制")

注意:
我们之前介绍过 确认应答/超时重传 机制,与之对应的就是刚刚介绍的 滑动窗口/快速重传 机制。事实上,这两者是并不冲突的。有着各自适合的应用场景:

  • 单位时间内发送的数据量比较少:采用 确认应答/超时重传 机制
  • 单位时间内发送的数据量比较多:采用 滑动窗口/快速重传 机制

2、流量控制

我们在上文也有提到过一点,虽然窗口越大,传输的效率越高,但窗口也不可能无限大下去。发送方一个劲儿的发是没有意义的,得确保接收方能处理的过来。

其实,在接收端就相当于是一个生产者消费者模型,一边接收着发送端发来的数据作为生产者,一边应用程序又会调用read 等方法读取数据作为消费者,中间通过一个接收缓冲区(相当于是阻塞队列)来协调:

如果发送端发送的太快,就会导致接收端的缓冲区被快速填满,此时如果发送端继续发送,就会发生丢包。接着又会引发丢包重传等一系列补救措施,反而得不偿失了。

因此TCP⽀持根据接收端的处理能⼒, 来决定发送端的发送速度. 这个机制就叫做流量控制(Flow Control)

那么具体该如何协调呢?

其实仔细想想,会发现这个模型与我们小学时都做过的一道应用题,游泳池一边放水,一边接水十分相似。放水就相当于是消费者消费数据,接收就相当于是生产者生产数据。我们可以通过“定量”的方式,来实现制约:

可以通过“水位”来表示接收缓冲区中有多少数据。通过空闲空间的变化来控制合理的窗口大小:

  • 如果空闲空间越大,就可以认为是应用程序处理速度比较快,就可以让发送方发的更快一些,即设定一个更大的窗口大小
  • 如果空闲空间越小,就可以认为是应用程序处理速度比较慢,就可以让发送方发的更慢一些,即设定一个更小的窗口大小

在TCP中,接收方收到数据时,就会把接收缓冲区剩余空间大小通过 ACK 报文返回给发送端。接着发送端就可以根据这个数据来设置合适的窗口大小了。

 注意:

这里记录窗口大小的数据位只有16位,,但并不意为着发送端窗口大小最大只有64KB

在tcp报头中的选项中,可以设置一个特殊的选项 “窗口扩展因子M”实际窗⼝⼤⼩是 窗⼝字段的值左移 M 位。注意每左移一位,就相当于原数据*2,这么一调整,窗口大小就十分可观了。

如果接收端缓冲区满了, 就会将窗⼝置为0。这时发送⽅不再发送数据,但是需要定期发送⼀个窗⼝探测数据段,使接收端把窗⼝⼤⼩告诉发送端:

3、拥塞控制

这个机制的主要目的与上文介绍的 流量控制 是类似的。

  • 流量控制是站在接收方的视角来限制发送方的速度
  • 拥塞控制是站在传输链路层视角来限制发送方的速度的

试想以下假如接收端处理的速度非常快,此时发送端的发送速度难道也能无限大下去吗?

显然不能。因为在传输过程中的结点设备可能会承受不住。如果发送端只是根据接收端的处理速度来设定窗口大小,其发送量很可能会超过一些中间节点的承受能力,导致该节点负载过高而可能会发生丢包问题

不过这好像也不太好把控。毕竟接收方的需求是比较好确定的,但传输链路层就非常复杂了。因为每次传输经过的结点数非常多,并且每次传输走的线路可能都是不同的,其拥塞状况可能也是截然不同的。

于是,拥塞调整并不是“一锤子买卖”,而是引入了慢启动机制,先发少量的数据探路,再根据每次传输反馈进行动态调整发送速度:

1、先按照一个比较小的速度发送数据

2、数据非常畅通,没有丢包,说明此时网络上传输数据整体上是比较流畅的,可以加快传输速度

3、增大到一定速度后,出现了丢包,说明网络上可能存在拥堵了,就减慢传输数据的速度

4、减速之后,发现又不丢包了,就继续加速

5、加速之后又发现丢包了,就继续减速

就这样循环往复,使传输速度动态变化,以适应瞬息万变的网络环境

此时引入一个概念“拥塞窗口”,来描述拥塞控制下的窗口大小变化。我们接着看在实际传输中,拥塞窗口的变化过程吧:

1、刚开始传输的时候,拥塞窗口非常小,用一个很小的速度来发送数据

一般定义初始拥塞窗口大小为1。相当于在试探当前网络环境拥塞程度

2、每次收到一个ACK应答,拥塞窗口大小加一

此时的增长速度是指数级的,是非常快的

在实际发送数据包时,发送端会将拥塞窗口接收端反馈的窗口大小进行比较,取较小的作为实际发送窗口

3、当增长速度达到一定阈值时,会停止指数增长,变成线性增长

一定程度上减缓增长速度,避免过快的增长速度对传输链路造成压力,不至于过快让传输结点负载过高,出现丢包问题

这里的初始阈值一般为 TCP 开始慢启动是窗口的最大值

4、线性增长,也会使发送速度越来越快。当速度快到一定量时,就难免会出现丢包,触发重传

一旦出现丢包,慢启动阈值就会变成原来的一半,并减小拥塞窗口大小。有两种减小方式:

  • 经典方案:回归慢启动开始时非常小的初始值,接着重复之前的增长流程
  • 现在方案:回归到新的阈值上,接着继续线性增长。重复此步骤,不会再进行一开始的指数增长了
拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对⽅,但是⼜要避免给⽹络造成太⼤压⼒的折中⽅案。

总结

滑动窗口机制的出现就是为了缓解TCP协议可靠传输带来的性能损失,用于在发送端和接收端之间管理数据流动的速度,通过 流量控制拥塞控制 使窗口大小控制在一个合理的范围内,以避免接收端因处理能力不足而导致数据丢失或发送端因发送过快而导致网络拥塞,又通过 快速重传 产生了一种滑动的效果,尽可能的提高了传输的效率。

那么本篇文章就到此为止了,如果觉得这篇文章对你有帮助的话,可以点一下关注和点赞来支持作者哦。如果有什么讲的不对的地方欢迎在评论区指出,希望能够和你们一起进步✊

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

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

相关文章

2025届计算机保研经验贴(末九→浙江大学软件学院)

燕园再美美不过宁波港,没到过浙软的人不会明了 软微已死,浙软当立! 文章目录 一、个人情况二、保研历程1、去年今日2、前期准备3、夏令营天大智算软件所西交软本校浙江大学软件学院 4、预推免 三、后记链式反应9.28下午冥场面9.29博弈 浙软当…

ClickHouse 24.9 版本发布说明

本文字数:7295;估计阅读时间:19 分钟 作者:ClickHouse Team 本文在公众号【ClickHouseInc】首发 又到新版本发布的时间了! 发布概要 本次ClickHouse 24.9 版本包含了23个新功能🎁、14项性能优化&#x1f6f…

[已解决] HttpMessageNotReadableException: JSON parse error: Unexpected character

[已解决] HttpMessageNotReadableException: JSON parse error: Unexpected character 文章目录 写在前面问题描述报错原因分析: 解决思路解决办法1. 检查并修复客户端的 JSON 数据格式2. 确认请求头的 Content-Type 设置正确3. 捕获并处理 HttpMessageNotReadableE…

三层b+树估算存储多少行数据

文章目录 B树结构图示估算方法(这里要以聚簇索引来看) B树结构图示 估算方法(这里要以聚簇索引来看) 非叶子节点数* 每个叶子结点记录总数 假设mysql 数据页,16kb,刚好对应B树的一个节点 每个叶子结点记录数, 叶子结点存储的是对应的原始数据…

项目常用版本控制管理工具

不仅仅是代码管理工具 gitHubgitcodeSVN gitHub https://github.com/ github gitcode https://gitcode.com/ gitcode SVN 图片: 带尺寸的图片: 居中的图片: 居中并且带尺寸的图片:

git--git reset

HEAD 单独一个HEAD eg:git diff HEAD 表示当前结点。 HEAD~ HEAD~只处理当前分支。 注意:master分支的上一个结点是tmp分支的所在的结点fc11b74, 79f109e才是master的第二个父节点。 HEAD~ 当前结点的父节点。 HEAD~1 当前结点的父节点。 HEAD~n 当前结点索…

Python 工具库每日推荐 【easyocr】

文章目录 引言Python OCR 工具库的重要性今日推荐:EasyOCR 工具库主要功能:使用场景:安装与配置快速上手示例代码代码解释实际应用案例案例:多语言名片信息提取案例分析高级特性自定义模型训练处理倾斜文本扩展阅读与资源优缺点分析优点:缺点:总结【 已更新完 TypeScript…

Qt实现侧边栏功能

本文介绍Qt实现侧边栏功能。 采用Qt进行界面应用程序开发时,经常遇到侧边栏功能实现,采用侧边栏可以将一些暂时不用到的功能隐藏,使用的时候点击一下相应的按钮即可弹出(动画方式)功能菜单。减少主界面控件数量&#…

JS | JS中类的 prototype 属性和__proto__属性

大多数浏览器的 ES5 实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。 构造函数的子类有prototype属性。‌ …

搭建知识库:助力大健康零售电商的快速发展

一、大健康零售电商行业的快速发展及其对知识库的需求 随着互联网技术的飞速发展和人们对健康意识的显著提升,大健康零售电商行业迎来了前所未有的发展机遇。这一行业不仅涵盖了传统零售业的商品销售,还融入了健康管理、健康咨询、健康数据分析等多元化…

『网络游戏』数据库表格转储【25】

避免勿删数据库表格,可以将表格存储 放到桌面即可 现在将表格删除后点击 浏览桌面表格保存即可 修改客户端脚本:NetSvc.cs 目的是在数据库更新异常时弹出提示以便修改 本章结束

使用 Helsinki-NLP 中英文翻译本地部署 - python 实现

通过 Helsinki-NLP 本地部署中英文翻译功能。该开源模型性价比相对高,资源占用少,对于翻译要求不高的应用场景可以使用,比如单词,简单句式的中英文翻译。 该示例使用的模型下载地址:【免费】Helsinki-NLP中英文翻译本…

Pura 70系列和Pocket 2已支持升级尝鲜鸿蒙NEXT,报名教程在这里

相信不少关注鸿蒙 NEXT 的人都知道,10月8日起,华为开启了鸿蒙 NEXT 系统的公测,但有不少人不知道的是,除了公测的 Mate 60 和 Mate X5 两个系列的机型,还有两个系列的手机其实也可以提前升级体验鸿蒙 NEXT 系统。 Pur…

随时随地一键开播的云微客实景直播神器,你想要吗?

AI实景直播系统正在以自动化、智能化的特性,逐渐成为直播行业的新宠。在众人频繁使用手机的时代背景下,直播已经成为了大多数人娱乐的方式之一,然而传统的直播方式不仅操作繁琐而且人员成本也高;现在云微客实景直播不仅可以告别人…

在工业现场,数据采集相关的对象一般有哪些类型?

在工业现场,数据采集相关的对象一般有以下类型:一、设备运行参数类1.温度 —描述:反映设备的发热情况、工作环境温度等,对于一些对温度敏感的设备(如电子设备、精密机械等)至关重要。 —举例:在…

企业注册资金如何实缴?步骤与方式详解

在企业的发展过程中,注册资金实缴是一个重要的环节。它不仅体现了企业的实力和信誉,也为企业的经营活动提供了坚实的资金保障。那么,在 2024 年,企业注册资金实缴的步骤和方式有哪些呢? 一、企业注册资金实缴步骤 1、确…

[C语言]结构体

1.什么是结构体 结构是多种类型的数据的集合。。且每个结构成员都有名字,因此当使用特定的成员时需要指明结构体成员的名字。 2.结构体的声明 以学生的数据为例: struct student //student结构名{char name; //name结构体成员名int height;…

传知代码-自动车牌识别检测系统(论文复现)

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 YOLO V8实现多种车牌检测识别! 一、概述 使用yolov8进行车牌检测(训练测试演示部署) 二、支持类型 我们的车牌识别检测系统支持多种类型的车牌 具体支持类型如下&#xff1a…

云渲染农场可以用哪些CG软件!

随着数字内容创作的需求日益增长,3D艺术家们不断寻求更高效、更强大的渲染解决方案。云渲染农场以其灵活性和可扩展性,成为了许多创作者的首选。这些平台支持多种流行的CG软件,如Maya、3ds Max、Cinema 4D、Blender等,以及各种插件…

go 语言学习路线图

1. Go语言简介 Go语言的历史背景和设计理念Go的优势:简洁、高效、并发支持强Go的应用场景:微服务、云计算、系统编程 2. 开发环境设置 安装Go语言开发环境 在Windows、macOS、Linux系统上的安装方法 配置环境变量:GOROOT 和 GOPATH验证安装…