TCP 协议机制超详解

我的主页:2的n次方_  

在这里插入图片描述

1. 协议结构

2. 确认应答

在之前提到过 TCP 的核心机制是确认应答,可以确认对方是否收到数据,在数据传输的过程中,如果有多条请求,并且返回对应的响应,但是此时可能会出现这样的问题:最先发送的请求可能并不会最先收到响应,也就是收到响应的顺序会不一样。

针对这样的问题的解决方案就是给每一个字节都进行编号(TCP 的传输是面向字节流的),并且编号是连续且递增的,按照字节编号这样的机制就称为“TCP 的序号”,在应答报文中,针对之前收到的数据进行对应的编号,称为“TCP 的确认序号”

上面的 32 位序列号和确认序列号就是这种,由于序号是递增的,知道了第一个字节的序号,后续每一个字节的序号都能知道

假如 TCP 发送了的数据标记为了 1~1000,那么确认应答的序号应该是收到的数据最后一个字节序号的下一个序号,也就是1001,表示小于 1001 序号的数据都收到了

并且之后的六位标志位中的第二位(ack)就会设为 1(默认是0)

3. 丢包

丢包的原因:

  1. 数据传输过程中发生了 bit 翻转,收到这个数据的接收方/中间的路由器等,计算校验和发现不匹配,就会把当前数据包丢掉,不再交给应用层
  2. 数据传输到某个节点(路由器/交换机)时,当前节点负载过高,例如某个路由器单位时间内只能发送n 个包,但是遇到了高峰期,单位时间内需要发送的包超过了 n ,后续传输过来的数据就可能被路由器丢掉了

4. 超时重传

TCP 对抗丢包的方法:其实丢包是不可能避免的,TCP 感应到丢包之后就会再重新发一次数据,第二次再发生丢包的概率就会减小很多,TCP 感应丢包是通过应答报文来区分的,收到应答报文之后就说明没有丢包,没有收到应答报文就说明数据丢包了,但是也不能排除当时没收到后续收到了的情况,所以就需要设置一个时间限制,在时间限制内来判断是否丢包,不过还有一个特殊情况:

第一种就是正常的数据没有发送到丢包了,第二种是数据没有丢,但是 ack 丢了,不过无论是哪种情况都会认为是丢包并且进行数据重传,这时就会出现一个问题,第一种情况是没问题的,数据丢了重新传,但是第二种情况数据没有丢,再次发送就意味着主机2收到了两份同样的数据,如果是转账的请求,让你转两次账肯定也不合理

针对上述问题 TCP 也进行了处理,接收方会有一个接收缓冲区,收到的数据会先进入缓冲区中,后续再收到数据就会根据序号在缓冲区中找对应的位置,如果发现当前序号 1~1000 已经存在了,就会把新收到的数据丢弃了,以此来确保读取到的数据是唯一的

重传的时间设定:

这里的时间不是固定的,而是动态变化的,例如发送方第一次重传,超时时间为 t1,如果重传之后仍然没有 ack ,还是继续重传,第二次重传超时时间为 t2,,t2 是大于 t1 的,每多重传一次,超时时间的间隔就会变大

经过一次重传之后,就能让数据到达对方的概率显著提示,反之,如果重传几次都没有顺利到达,说明网络的丢包率已经达到了一个很大的程度

重传也不会无休止的进行,当重传到达一定次数的时候,TCP 就不会尝试重传了,就认为这个链接已经G了,此时先进行“重置/复位 连接”,发送一个特殊的数据包“复位报文”,如果网络恢复了,复位报文就会重置连接,使通信继续进行,如果网络还是有问题,复位报文没有得到回应,此时 TCP 就会单方面放弃连接

确认应答和超时重传这两个核心机制共同构建了 TCP 的“可靠传输机制”

5. TCP 的三次握手

三次确保了客户端和服务器之间建立连接,发送不携带业务数据(没有载荷,只有报头)的数据包

客户端发送同步请求,也就是标志位中的第 5 位,然后服务端也回应发送同步信息和确认应答,客户端再确认应答,虽然说看上去是四次交互,但是中间服务器的 syn + ack 合并了,一起发送到客户端,也就是三次握手

TCP 进行三次握手的原因:

  1. 验证通信路径是否畅通。
  2. 验证通信双方的发送能力和接收能力是否正常,客户端第一次发送 syn 可以确定客户端的发送能力和服务器的接收能力正常,然后服务器发送 syn + ack 告诉客户端的发送能力和服务器的接收能力正常,然后客户端就知道了自己的接收和发送能力都正常,再发送 ack ,服务器也确认了自己的发送能力正常
  3. 让通信双方在进行通信之前,对通信过程中需要用到的一些关键参数进行协商(例如确定起始序号,TCP 通信时,起始数据的序号就是通过三次握手协商确定的,每次建立连接 TCP 的其实序号都不同,并且差别很大,这样做是为了避免上一次的数据如果“迷路了”,在下一次 TCP 连接时出现误判,如果发现不是属于此次起始范围的数据就丢弃)

6. TCP 的四次挥手

  1. 客户端完成数据发送任务后,发送一个带有 FIN(终止)标志位的数据包,用来关闭客户端到服务器的数据传送。此时客户端进入 FIN_WAIT_1 状态,表示客户端不再向服务器发送数据,但仍可以接收服务器发送的数据。
  2. 服务器收到客户端的 FIN 包后,发回一个 ACK 数据包给客户端,确认序号为收到的序号加 1。此时服务器进入 CLOSE_WAIT 状态,表明服务器还有数据可能需要发送给客户端,客户端收到这个 ACK 后进入 FIN_WAIT_2 状态,继续等待服务器的 FIN 报文。
  3. 当服务器端确定数据已发送完成,则向客户端发送 FIN 报文,告诉客户端自己也要断开连接了,然后服务器进入 LAST_ACK 状态,等待客户端的确认。
  4. 客户端收到服务器的 FIN 报文后,回复一个 ACK 报文给服务器,确认号为收到的序号加 1,随后客户端进入 TIME_WAIT 状态。服务器收到这个 ACK 报文后,连接正式关闭,进入 CLOSED 状态。客户端在经过 2 倍的 MSL(报文最大生存时间)后,也进入 CLOSED 状态。

状态说明:

LISTEN:服务器进入的状态,服务器把端口绑定好之后相当于进入了该状态,等待客户端发生请求

ESTABLISHED:客户端和服务器都会进入的状态,表示 TCP 已经建立好连接了

CLOSE_WAIT:被动断开连接的一方(先收到 FIN)会进入这个状态,等待代码执行 close 方法

TIME_WAIT:主动断开连接的一方会进入这个状态,按照时间来等待,达到一定时间后等待结束(原因:防止最后一个 ACK 丢包),时间就是 2 倍的 MSL(报文最大生存时间)

7. 滑动窗口

在之前介绍的可靠传输是发一次应答一次,效率并不高,所以 TCP 就在保证可靠传输的前提下,也能有一个不错的效率,引入了滑动窗口,发送方可以在未收到确认应答的情况下连续发送多个数据包,不必每发一个包就停下来等待确认,大大减少了数据传输的等待时间,提高了传输效率。

当发送方发送的数据被接收方正确接收并确认后,发送窗口会向前滑动,即发送窗口的左边界会向右移动,同时右边界也可能根据接收方通告的接收窗口大小而向右移动。也就是当收到 2001 ack ,说明 1001~2000 的数据已经得到应答了,然后立即发送 5001~6001 的数据,此时等待的 ack 范围就是 2001~6000 窗口大小还是 4000

q:滑动窗口的前提是可靠性,如果说在滑动窗口传输中出现了丢包该怎么办?

这种情况其实是不需要做任何处理的,批量发送数据,ACK 只是丢其中的一部分,而确认序号表示的是收到的数据最后一个字节的下一个序号,也就是确认序号之前的数据都收到了,虽然 1001 的 ACK 丢了,但是 2001 到达了,还可以证明 2001 之前的数据都到了,后一个 ACK 也就涵盖了前一个 ACK 的意义

在上图中,B 收到的数据是1~1000,2001~3000···,其中 1001~2000 的数据丢了,此时 B 收到2001~3000 时返回的不是 3001,而是 1001,也就是 B 希望接下来收到的是 1001~2000 的,但是一直没有收到,后续 A 发送的 3001~4000,4001~5000···收到的 ACK 都是 1001 当主机 A 连续多次收到相同的 1001 后就意识到丢包了,就会重新传输 1001~2000 的数据包,传输之后由于 2001~7000 的数据在之前已经发过了,1001~2000 相当于是补全了之前的空缺,接下来索要 7001 开头的数据即可

上述过程快速地识别出是哪个数据包丢失了,并且针对性的重传,其他到达的数据无需重传,这个过程称为“快速重传”,快速重传可以认为是滑动窗口搭配下的超时重传。

如果单位时间内发送的数据量比较小,就会按照之前的确认应答,超时重传发送,数据量多了之后就会按照滑动窗口,快速重传

8. 流量控制

滑动窗口的窗口大小对于传输数据的性能是直接相关的,但是窗口肯定也不能无限大,在之前提到过,内核中的内存空间,每一个 socket 对象都是有一个接收缓冲区的,无限大的话,接收方可能无法处理如此大量的数据,造成缓冲区溢出,从而丢失数据,而且,网络的中间设备(如路由器等)可能无法承受如此巨大的数据量,导致网络阻塞,进而影响传输的可靠性

这时就需要通过“定量”的方式来看接收缓冲区剩余空间的大小,如果空闲空间越大,就认为应用程序处理速度比较快,就可以让发送方发的快一点,设置一个更大的窗口,如果空闲空间越小,就认为应用程序处理速度比较慢,就可以让发送方发的慢一点,设置一个更小的窗口

TCP 中接收方收到数据的时候,就可以把接收缓冲区剩余空间大小通过 ACK 数据报的方式反馈给发送方,发送方就可以依据这个数据设置发送窗口的大小了

但是 ACK 数据报是不携带业务信息的,这时就用到了上面的 16 位窗口大小的属性

16 位窗口大小就体现了刚才提到的接收方缓冲区的剩余空间,这个属性只有在 ACK 报文中(ACK 为 1)才有效

此处的 16 位表示的范围是 64KB ,但也并不意味着发送方窗口的大小最大就是 64KB,在选项中还可以设置一个特殊的选项“窗口扩展因子”

发送方的窗口大小 = 窗口大小 << 窗口扩展因子

那发送方不发送数据这个状态要持续多久呢,通过 ACK 来确认接收缓冲区的剩余空间的话,不发数据那么就没有 ACK,就会一直等吗

过了重发超时的时间如果还没有收到窗口更新的通知之后,发送端就会发送一个窗口探测的包(不携带业务数据,载荷是空的)来触发 ACK,以此来查询接收区缓冲区还剩多少

接收方也会在接收缓冲区不为 0(消费了一定数据)的时候主动触发一个“窗口更新的通知”这样的数据报

9. 拥塞控制

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

例如从 A 传输数据到 B 的过程中,拥塞控制中把中间传输的节点看作一个整体,不关心内部的细节,如果当前节点的负荷已经很高了,此时 A 再以很快的数据发送数据,就会丢包

流量控制可以精准的使用接收方的缓冲区剩余空间来进行衡量,而拥塞控制考虑中间节点的情况,就需要从多方面来看了,中间节点的数量很多,每次传输的路线也不一样,中间那个节点遇到瓶颈了也不能确定,并且中间节点也不止 A 一个数据,还有很多其他设备的数据,这就很难从一个方面来考虑解决办法了

所以就可以采用一个“测试”的方法

  1. 首先通过“慢启动”来控制,在连接建立初期,发送方的发送窗口大小非常小
  2. 发现不丢包之后,以指数增长的方式逐渐增加发送窗口的大小。
  3. 增长到一定程度,达到某个阈值之后,此时即使没有丢包,也会停止指数增删,变成线性增长
  4. 线性增长也会持续使发送速度越来越快,达到某个阈值之后就会丢包

一旦出现丢包,接下来就需要减少发送的速度,减小窗口的大小,此时有两种处理方式:

经典方案:回归慢开始的初始值,然后指数级增长,再线性增长

当前方案:回归到新的阈值上,线性增长,并且之后也不会指数级增长

流量控制和阻塞控制,都是在于对“可靠传输”进行补充,这两个机制同时作用,最终实际的发送窗口的大小取决于二者的最小值。

10. 延时应答

当接收方收到数据后,不是立即发送确认应答(ACK),而是等待一段时间再发送。这样做的目的是让接收方有更多的时间来处理数据,从而有可能在发送 ACK 时,将接收窗口的大小设置得更大一些,以达到更高的效率

11. 捎带应答

正常情况下,ACK 和响应是不同的时机,无法合并,但是 ACK 涉及到上面讲的“延时应答”,这样就会使 ACK 返回的时间往后拖,这样一延时,就可能赶上接下来发送响应数据的操作了,于是就可以在发送响应的时候,把刚才的 ACK 的信息捎带上。

延时应答和捎带应答都提升了 TCP 的性能。

12. 面向字节流

在之前已经提到过,TCP 传输数据时面相字节流的,所以就会涉及到“粘包问题”,粘的是 TCP 携带的载荷(应用层数据包)

由于 TCP 是面相字节流的,所以此处的读操作怎么读都可以,不过读出来的效果就可能和原来的数据包不一样了,无法区分各个数据包的边界,针对“粘包问题”,有以下两种解决方案:

  1. 指定分隔符(适合于文本类的数据)。在之前写的回显服务器小案例的时候采用的就是发送请求响应时使用 println 进行写数据,读取请求响应时,专门使用 scanner.next 按照 \n 来解析,需要确认数据内容的正文中不能包含分隔符,如果传输的数据是纯文本数据的话,此时使用 \n 等就不太合适,可以使用 ASCII 中靠前的“控制字符”。
  2. 指定数据的长度(适合于二进制数据)。例如每个应用层数据包,开头的几个字节用来表示数据包长度

UDP 由于是面向数据报的传输,每一次传输都是一个完整的数据报,所以也不涉及到上述问题

13. 异常处理

  1. 进程崩溃。在 Java 中的体现就是抛出异常,但是没有被 catch 到,最终异常到了 JVM 这里,JVM 进程就崩溃了,当进程崩溃之后,进程中的 PCB 就会被回收,PCB 中的文件描述符表里对应的所有文件,也会被系统自动关闭,其中针对 socket 文件,也会触发正常的关闭流程(四次挥手)
  2. 主机关机。正常流程点击关机按钮,此时操作系统就会先关闭所有的进程,关闭的过程中,同样会触发四次挥手,这时就会出现两种情况:a. 四次挥手非常快,已经完成之后,关机动作才完成 b. 四次挥手没来得及挥完,关机就完成了:

面对这种情况 B 这里的 FIN 没有收到 ACK ,就会触发超时重传,重传一定次数之后,就主动放弃连接

     3. 主机掉电。也有两种情况:

           a.接收方掉电

A 给 B 发送的数据不会再有 ACK 了,A 就会触发超时重传,重传多次之后 A 尝试重置连接(RST),重置连接也没有 ACK,A 就会单方面释放连接

        b. 发送方掉电

A 发着发着就不发了,B 就会给 A 发送一个探测报文(不携带业务逻辑,为了触发 ACK),连续多个探测报文都没有 ACK,就可以认为 A 挂了,这样的探测报文是周期性的,同时这个报文是用来探测对方“生死”的,就称为“心跳包”。TCP 内置了心跳包,由于 TCP 内置的心跳包周期比较长,应用程序这一层也会自行实现一些心跳包,达到更快速的“保活机制”。

        4. 网线断开。这和主机掉电是类似的

对于 A 来说:A 收不到 ACK 就会触发超时重传,然后重置连接,最后单方面释放

对于 B 来说:B 就会发送心跳包,也收不到 ACK,最后单方面释放

在这里插入图片描述

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

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

相关文章

【通俗易懂】知识图谱增强 RAG 思路 和 实现方案

【通俗易懂】知识图谱增强 RAG 思路 和 实现方案 为什么用 知识图谱增强 RAG&#xff1f;对比传统方法3 种实现方式 方案一&#xff1a;利用 KG 关系网络&#xff0c;构建问题子图促精准解答地图固定深度整体优化方案 方案二&#xff1a;利用 KG 语义关联&#xff0c;提升文档片…

【重学 MySQL】三十八、group by的使用

【重学 MySQL】三十八、group by的使用 基本语法示例示例 1: 计算每个部门的员工数示例 2: 计算每个部门的平均工资示例 3: 结合 WHERE 子句 WITH ROLLUP基本用法示例注意事项 注意事项 GROUP BY 是 SQL 中一个非常重要的子句&#xff0c;它通常与聚合函数&#xff08;如 COUNT…

C++ -缺省参数-详解

博客主页&#xff1a;【夜泉_ly】 本文专栏&#xff1a;【C】 欢迎点赞&#x1f44d;收藏⭐关注❤️ C -缺省参数-详解 1.是什么2.分类2.1全缺省参数2.2半缺省参数&#xff1a; 3.实际应用4.关于缺省参数的声明与定义5.总结 1.是什么 先来看看下面这段代码&#xff1a; #incl…

HTML5简介的水果蔬菜在线商城网站源码系列模板3

文章目录 1.设计来源1.1 主界面1.2 商品列表1.3 商品信息1.4 购物车1.5 其他页面效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.ne…

JavaScript 类型转换:数字转换和 Symbol 类型转换

数字转换 将数据类型转换为数字称为数字转换&#xff0c;可以使用Number()、parseInt()、parseFloat()等方法将数据类型显式转换为数字。当一个值不能被强制转换为一个数字时&#xff0c;就会返回 NaN。 1. 字符串 > 数字&#xff1a; 当把字符串转换为数字时&#xff0c…

2024年双十一有哪些值得入手的好物?2024年双十一必买物品推荐

2024年的双十一购物季将是一个精彩的时刻&#xff0c;各大电商平台将推出一系列精品数码产品&#xff0c;包括最新款的智能手机、笔记本电脑、平板电脑、智能手表等等。这些产品不仅拥有时尚的外观和高端的配置&#xff0c;而且还能够满足消费者多种多样的需求&#xff0c;比如…

windows11上超详细JDK17安装教程

1.下载安装包,访问官网地址​&#xff1a; https://www.oracle.com/java/technologies/downloads/#java172、选择jdk-17_windows-x64_bin.exe Installer。 3、接着等待下载&#xff0c;下载完成后双击进行安装 4、点击下一步 5、这里可以选择安装位置 6、等待安装 7、安…

24年最新Stable Diffusion之最全详解图解

前言 1. Stable Diffusion介绍 1.1 研究背景1.2 学术名词 2.Stable Diffusion原理解析 2.1 技术架构2.2 原理介绍 扩散过程 3.1 Diffusion前向过程3.2 Diffusion逆向&#xff08;推断&#xff09;过程 本次教程将使用AI绘画工具 Stable Diffusion 进行讲解&#xff0c;如还未…

超详细超实用!!!AI编程之cursor配置中文(一)

云风网 云风笔记 云风知识库 一、配置中文扩展 打开扩展&#xff0c;搜索chinese,安装chinese(simplified) 简体中文语言包,重启即可 二、配置中文方法二 使用快捷键组合【CtrlShiftp】&#xff0c;在搜索框中输入configure display language,选择中文也可配置中文 三、配置…

近几年来说最有效率的编程语言和市场最认可的编程语言分别是什么?

在过去的几年中&#xff0c;编程语言的效率和市场认可度在不断演变。不同的语言适用于不同的领域和场景&#xff0c;因而编程语言的“效率”和“市场认可”需要根据具体应用来分析。本文将从两个角度入手&#xff0c;分别探讨近几年中被认为最有效率和最受市场认可的编程语言。…

开源语音实时交互新突破:LLaMA-Omni实现大语言模型无缝语音交互

像 GPT-4o 这样的模型通过语音实现了与大型语言模型&#xff08;LLMs&#xff09;的实时交互&#xff0c;与基于文本的传统交互相比&#xff0c;显著提升了用户体验。然而&#xff0c;目前在如何构建基于开源 LLMs 的语音交互模型方面仍缺乏探索。为了解决这个问题&#xff0c;…

如何精准计算:大型语言模型(LLM)部署到底需要多少GPU内存?

在几乎所有关于大型语言模型&#xff08;LLM&#xff09;的访谈中&#xff0c;总有一个问题反复出现&#xff1a;“部署 LLM 需要多少 GPU 内存&#xff1f;” 这个问题并非偶然&#xff0c;它是衡量您对这些强大模型在实际生产环境中部署和扩展能力理解程度的关键指标。 当您…

Transformer推理结构简析(Decoder + MHA)

一、Transformer 基本结构 Transformer由encoder和decoder组成&#xff0c;其中&#xff1a; encoder主要负责理解&#xff08;understanding&#xff09; The encoder’s role is to generate a rich representation (embedding) of the input sequence, which the decoder c…

国内短剧cps系统和短剧(播放)系统的区别,附各源码部署教程

国内短剧项目主要分为两大形式&#xff1a;一种是做短剧播放平台&#xff0c;让用户付费观看&#xff1b;另一种是做短剧的分销&#xff0c;就是将他人的平台短剧推广&#xff0c;可做平台可入驻&#xff0c;拿分成。 首先来说一下短剧播放平台&#xff08;短剧系统&#xff0…

828华为云征文|华为云服务器Flexus X 搭建BTC虚拟币质押投资理财系统(仅测试学习)

一、华为云服务器Flexus X 选购和介绍 强大性能&#xff0c;引领云服务新潮流 柔性算力&#xff0c;满足多样化需求 Flexus X实例的部署与管理过程也非常便捷。用户只需在华为云官网注册账号&#xff0c;选择适合的Flexus X实例规格&#xff0c;完成购买后即可开始部署。华为…

telnet ftp ssh 如何在交换设备上创建

telnet 测试 说明telnet 成功 这测试ftp 成功

深入理解MySQL InnoDB中的B+索引机制

目录 一、InnoDB中的B 树索引介绍 二、聚簇索引 &#xff08;一&#xff09;使用记录主键值的大小进行排序 页内记录排序 页之间的排序 目录项页的排序 &#xff08;二&#xff09;叶子节点存储完整的用户记录 数据即索引 自动创建 &#xff08;三&#xff09;聚簇索引…

【每日刷题】Day129

【每日刷题】Day129 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 105. 从前序与中序遍历序列构造二叉树 - 力扣&#xff08;LeetCode&#xff09; 2. LCR 154. 复杂…

足球预测模型理论:足球数据分析——XGBoost算法实战

简介&#xff1a;本文将探讨如何使用XGBoost算法进行足球数据分析&#xff0c;特别是足球运动员身价估计。我们将通过实例和生动的语言&#xff0c;解释XGBoost算法的原理和实际应用&#xff0c;帮助读者理解复杂的技术概念&#xff0c;并提供可操作的建议和解决问题的方法。 足…

Eclipse离线安装Tomcat插件

Eclipse离线安装Tomcat插件 最近的自己在对低版本的代码的进行维护补丁,不得不采用Eclipse 来进行跑项目,真的是折磨 其中遇到一个问题就是打开Eclipse的2021版,安装Tomcat的插件,发现好家伙,就是死活在线安装失败 (喵的,真的是让我抓耳挠腮!!哈哈哈) 无奈,只好采用离线安装,特…