传输层之 TCP 协议

TCP协议段格式

        源/目的端口号:表示数据是从哪个进程来,到哪个进程去。

        序号:发送数据的序号。

        确认序号:应答报文的序号,用来回复发送方的。

        4 位首部长度:一个 TCP 报头,长度是可变的,不像 UDP 固定是 8 个字节。描述了 TCP 报头具体多长。因为选项之前的部分都是确定的 20 字节,所以首部长度 -20 字节就是选项部分的长度。如果首部长度值是 5,表示整个 TCP 报头是20字节(相当于没有选项)。因为 4bit => 0 - 15,所以 TCP 报头最长为 15 * 4 = 60 字节(选项相当于是 40 字节)。

        保留:占个位置,现在没用但是以后可能会用。如果 TCP 后续引入了一些新功能,就可以使用这些保留位字段。就像现在用 128G 内存的手机够用,但是建议 256G 或者更大,为了后续能有空间。

        URG:紧急指针是否有效

        ACK:确认号是否有效,如果标志位为 1,表示是应答报文,如果是 0,就表示不是

        PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走

        RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文

        SYN:请求建立连接;我们把携带SYN标识的称为同步报文段

        FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段

        检验和:发送端填充,CRC校验。接收端校验不通过,则认为数据有问题。此处的检验和不光包含TCP首部,也包含TCP数据部分。

TCP 内部核心工作机制

确认应答(安全机制)

        这是实现可靠传输最核心的机制。TCP 进行可靠性传输,最主要的就是靠这个确认应答机制。A 给 B 发了消息,B 收到之后就会返回一个 应答报文(ACK),此时 A 收到应答之后就知道数据已经顺利到达 B 了。

        但是,网络可能会出现“先发后至”的情况,即我给对方发了 吃饭做我女朋友 这两条信息,本来对方是 可以不行 的,但由于“先发后至”,我就会收到 不行可以,这时候就会对我造成信息的误解。 

为了解决上述问题,就可以给传输的数据和应答报文进行编号即可:

        每一个 ACK 都带有对应的确认序列号,意思是告诉发送者,我已经收到了哪些数据;下一次你从哪里开始发。

        应答报文报头中确认序号填写的是 1001,就是刚才 1000 字节的数据基础上 +1。表示的含义是:小于 1001 的数据已经确认收到了、接下来应该从 1001 这个序号开始继续发送数据了。如果 1001 这个 ACK 丢了,但是 2001 这个没丢,就会表示 2001 之前的数据都收到了。

        小结:TCP 具有可靠传输能力,最主要就是通过确认应答机制来保证的,通过应答报文,就可以让发送方知道数据是否传输成功;进一步地引入了序号和确认序号,针对多组数据进行详细区分。

超时重传

        在讨论确认应答的时候,都是建立在顺利传输的基础上,但如果传输过程丢包了呢?发的数据丢了、返回的 ACK 丢了或者还在路上,此时发送方没有收到 ACK,就会统一认为已经丢包了。此时 TCP 就引入了重传机制。

        主机 A 发送数据给 B 之后,可能因为网络拥堵等原因,数据无法到达主机 B。TCP 引入了一个 时间阈值,如果超过这个阈值也没有收到 ACK,此时就会认为已经是丢包了,就会重新传输。

        但如果是 应答报文丢了 或者 应答报文还在路上,此时 TCP 又重发了,那么就有可能出现接收到两个相同的数据。 TCP 针对这种重复的数据传输,可以进行特殊的处理进行去重:TCP 存在一个“接收缓冲区”这样的存储空间(每个 TCP 的 Scoket 对象,都有一个发送缓冲区和一个接收缓冲区),即接收方的操作系统内核里的一段内存。B 收到 A 的数据,其实就是 B 的网卡读到了数据,然后把这个数据放到 B 对应的接收缓冲区中。可以想象成是一个优先级阻塞队列,根据数据的序号很容易识别是否有数据重复,如果重复就把后来的那份数据丢弃。TCP 使用这个接收缓冲区对收到的数据按序号进行重新排序,保证应用程序 read 到的数据和发送的顺序一致。

        如果重传的数据又丢了的话,就会再一次重传,每次重传所需的时间间隔都会增加,当重传次数达到一定时,TCP认为网络或者对端主机出现异常,就会直接断开连接(避免浪费系统资源)。由于去重和重新排序机制的存在,发送方只要发现 ACK 没有按时到达,就会重新发送数据。

        小结:可靠传输是 TCP 最核心的部分。可靠传输是通过 确认应答 + 超时重传 来进行体现的。其中确认应答描述的是传输顺利的情况;超时重传描述的是传输出现问题的情况。这两者相互配合,共同支撑 TCP 整体的可靠性传输。

连接管理

        连接:TCP 建立连接,就是在 A 记录 B 的 IP和端口,在 B 记录 A 的 IP和端口,当这两部分信息都被维护好了之后,连接就有了。此时,也把保存这部分信息的空间(数据结构)也称为连接。断开连接就是 A 和 B 把自己储存的连接信息(数据结构)删了。

        管理:就是描述了连接如何创建(三次握手),如何断开(四次挥手)。

三次握手

        通信双方各自要记录对方的信息,彼此之间要相互认同。

        SYN:同步报文段,客户端主动给服务器端发起的建立连接请求。在 TCP 报文段第五位。(synchronize,同步)

三次握手重要的两个状态:

1. LISTEN:服务器的状态。表示当前服务器已经准备就绪,随时可以有客户端来建立连接。

2. ESTABLISHED:客户端和服务器端都有。表示建立完成,接下来可以正常通信了。

此时知道,彼此之间都是对方唯一的羊了之后就相当于确认恋爱关系了,即连接建立完毕。但现在可看到是进行了四次交互,是因为中间两次是可合并成一次的(节约资源)。

所谓的三次握手,本质上是进行了“四次”交互。通信双方各自要向对方发起一个“建立连接”的请求,同时双方都得回应对方一个 ACK。

        中间两次交互能合并的原因:封装分用两次,一定比封装分用一次成本要高。这几次交互过程都是纯内核完成的,即服务器系统内核收到 SYN 后,会立即发送 ACK 也会立即发送 SYN。就像在同一家网店下单购买两件商品,发两个快递肯定比直接发一个快递成本要高。

        如果是两次握手的话,不能完成建立连接得到过程。如果少了最后一次握手,那么对于 美羊羊 来说,不知道 喜羊羊 是不是她的唯一。

        三次握手还有一个重要的作用:可以验证通信双方各自的 发送能力和接收能力 是否正常。 因此,三次握手也一定程度上保证了 TCP 传输的可靠性(只是辅助作用)。

三次握手的意义:

1. 让通信双方各自建立对对方的“认同”(保存对方的信息)

2. 验证通信双方各自的发送能力和接受能力是否 OK

3. 在握手的过程中,双方协商一些重要参数(TCP 通信过程中,有些数据通信双方需要相互同步,此时就需要有这样的交互过程)

四次挥手

        和三次握手类似。通信双方各自向对方发起一个断开连接的请求,再各自给对方一个回应。

四次挥手重要的两个状态:

1. CLOSE_WAIT:等待关闭,等待调用 close 方法关闭 Soclet。出现在被动发起断开连接的一方。建立连接一定是客户端主动发起请求的。断开连接可能是客户端注定发起,也可能是服务器注定发起。

2. TIME_WAIT:出现在主动发起断开连接的一方。如上图,在左边客户端的视角,当发完 ACK 的时候就会认为四次挥手已经完成了,就会断开连接。但忽视了一个问题:如果 ACK 丢包了,那么在右边服务器的视角就不知道是 ACK 丢了还是自己的 FIN 没发过去。此时右边服务器就会触发超时重传机制,再发一个 FIN。所以 TIME_WAIT 存在的意义就是解决这一情况,会保持 2MSL 这么长时间,MSL 表示在互联网上,两个节点之间数据传输消耗的最大时间,超过这个时间如果没收到重传的 FIN,就默认 ACK 已经到达,此时就断开连接(如果恰好 ACK 丢了,又恰好重传的 FIN 也丢了,那就没办法了)。只有 TIME_WAIT 是等 2MSL,其他的过程都是根据超时重传的阈值。

此时连接就断开了,就从情侣转变为路人。

这里是四次的原因是:FIN 是由应用程序触发的;ACK 是由内核控制的,在收到 FIN 之后会立即返回 ACK,因此应用程序再发送 FIN 的时候就会存在一个时间差,就不能合并成一个了。除非在代码实现上,ACK 和 FIN 之间的时间间隔比较短,此时就有可能合并成一个。(就像我今天买了个快递,过了一周又买了个)

        小结:TCP 作为一个有连接的协议,就需要建立连接和断开连接。其中建立连接的过程是三次握手,断开连接的过程是四次挥手。

滑动窗口

        对于每一个发送的数据段,都要给一个ACK确认应答,然后收到ACK后再发送下一个数据段。这样做有一个比较大的缺点,就是性能较差。尤其是数据往返的时间较长的时候。

        因为可靠性和传输效率本身是矛盾的,所以在保证可靠性的基础上来尽可能提高传输效率,尽量降低效率的折损(其实是将多个段的等待时间重叠在一起了)。 

滑动窗口的本质就是批量发送一组数据。我们把无需等待确认应答而可以继续发送数据的最大值叫做窗口大小,上图的窗口大小是 4000。只要有一个 ACK 到了,就可以继续发送下一条数据了,不必等四个 ACK 都到。(就像去饭馆吃饭,有座位就能去,不用等所有人都吃完)

 此时如果出现了丢包,如何进行重传?这里分两种情况讨论。

一、ACK 丢了

        这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认,因为有确认序号的设定。所以就引申出两个概念:ACK 并不是所有都发的,有时候会少发一部分(并不影响可靠性,同时节省系统资源);如果所有的 ACK 都恰好丢了,那么就会认为网络已经出现严重故障了。

二、数据丢了

        由于 1001-2000 字节的数据丢包了,接下来的 2001-3000 到达主机 B 之后,B 返回给 A 的ACK 确认序号仍然是 1001,意思是在索要从 1001 开头的数据。当 1001-2000 这个数据重传被收到之后,因为后面的 2001-7000 原本都以正常到达,被放到了接收端操作系统内核的接收缓冲区中,所以 ACK 返回序号就是 7001。对于这个丢包重传的方式,叫做“快速重传”,这个可以视为是 超时重传机制 在滑动窗口下的变形。

        因此可以得出结论:如果当前数据传输密集,按照滑动窗口方式传输,此时按照快速重传机制;如果当前数据传输稀疏,不再按照滑动窗口方式传输,此时还是按照超时重传机制传输。

流量控制

        这是一种干预窗口大小的机制。滑动窗口,窗口越大,传输效率就越高,但是窗口不能无限大:完全不等 ACK,就没有可靠性而言;窗口太大会消耗大量系统资源;发送速度太快接收方处理不过来等于白发。因此接收方的处理能力是一个重要的约束依据,发送方的速度不能超出接收方的能力。

        我们使用接收方接收缓冲区的剩余大小来衡量接收方的处理能力(剩的越多说明处理能力越快)。所以每次 A 给 B 发送数据,B 都要算一下剩余空间,然后把这个值通过 ACK 报文返回给 A,A 就会根据这个值来决定接下来发送的窗口大小是多少。

        虽然 TCP 报文结构的窗口大小是 16 位,但并不意味最大是 64K,因为在选项部分,引入了窗口扩展因子:比如窗口大小已经是 64K,扩展因子里面写了个 2,意思就是让 64K << 2,即左移两位就变成了 256K。

        由于接收方缓冲区剩余空间是一直动态变化的,所以每次返回 ACK 所带的窗口大小都在变化,因此发送方也会进行动态调整。当窗口大小为 0 的时候,发送方就会暂停发送,在这期间会定期给 B 发送 窗口探测报文,只是起到出发 ACK 从而查询窗口大小的功能,不携带具体数据。

拥塞控制

        流量控制和拥塞控制共同决定发送方的窗口大小是多少。只不过流量控制考虑的是接收方的处理能力;而拥塞控制描述的是传输过程中,中间节点(路由器 / 交换机)的处理能力。但是由于中间节点不好衡量,因为存在太多变数(路径、数据多少),因此就通过“实践”的方式来测试出一个合适的值。(就像 A 不停挑逗 B,步步逼近,看看 B 什么时候发火)

        当增长速率达到阈值(ssthresh),就从指数增长变成了线性增长。增长的前提是不丢包,如果传输的过程中丢包了,说明此时发送的速率已经接近网络的极限了,就会把窗口大小重新开始调整。因此可以知道:拥塞窗口不是固定的数值,而是一直动态变化的,随着时间的推移逐渐达到一个动态平衡的过程。这样既能把问题解决也能随着网络的变化而变化。

        拥塞窗口和流量控制的窗口,共同决定了发送方实际的发送窗口。

延时应答

        也是与滑动窗口有关的。因为滑动窗口的窗口大一点,传输速度就快一点,因此要在接收方能够处理得了的前提下,尽可能把窗口变大一些。延时:就是收到数据之后,不会立即返回 ACK,而是稍微等一会,在等的时间里,接收方的应用程序会把缓冲区 的数据消费一波,此时返回的剩余空间就变大了。(就是上面说到的不用每一条都发)

捎带应答

        是在延迟应答的基础上,引入的捎带应答。很多情况下,客户端服务器在应用层都是 "一发一收" 的,即业务上的请求和响应。

面向字节流

创建一个TCP的socket,同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区:

        调用write时,数据会先写入发送缓冲区中;

        如果发送的字节数太长,会被拆分成多个TCP的数据包发出;

        如果发送的字节数太短,就会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适 的时机发送出去;

        接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区; 然后应用程序可以调用read从接收缓冲区拿数据;

        另一方面,TCP的一个连接,既有发送缓冲区,也有接收缓冲区,那么对于这一个连接,既可以读数据,也可以写数据。这个概念叫做 全双工。

由于缓冲区的存在,TCP程序的读和写不需要一一匹配,例如:

        写100个字节数据时,可以调用一次write写100个字节,也可以调用100次write,每次写一    个字节;

        读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以一次read   100个字节,也可以一次read一个字节,重复100次。

粘包问题

        接收缓冲区是把收到的多个数据都得放到一起,就像是都粘在一起了,此时就会出现一个问题:应用程序在进行 read 的时候,怎么知道都到哪里就算是一个完整的应用层数据包呢?

        为了解决粘包问题,解决方案就是约定好应用层协议,明确应用层数据包之间的边界即可。比如:约定好分隔符;约定好每个包的长度。

TCP 异常情况

        在传输过程中出现了不可抗力。

一、程序崩溃或主机关机

        主机关机是要先杀进程然后才正式关机。进程没了,对应的 PCB 就没了,对应的文件描述符表就释放了,相当于 Socket.close(),此时内核会继续完成四次挥手,即使可能没挥完也任然是一个正常断开的流程。

二、主机掉电或网线断开

        接收方掉电:发送方仍然在继续发数据,但是一直等不到 ACK,就会触发超时重传,但重传几次后依旧没收到,就会尝试重置 TCP 连接,显然也会失败,因此就会单方面放弃连接。

        发送方断电:接收方发现过了很久都没数据来,不知道是还没发还是发送方已经挂了,然后接收方就会周期性给发送方发送一个信息,确认对方是否还正常工作。这个也被称作是“保活机制”,发送的消息也被称为“心跳包”,这是用来确认通信双方是否处于正常的工作状态中。

TCP 和 UDP 的对比

        TCP 优势:可靠传输。绝大部分场景中,都需要可靠传输的时候。

        UDP 优势:效率更高。有些场景对于性能要求更苛刻的时候(例如同一个机房内服务器之间的网络通信)。同时,UDP 天然支持广播。

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

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

相关文章

2024年汉字小达人活动还有4个多月开赛:来做18道历年选择题备考吧

不出特殊情况的话&#xff0c;距离2024年第11届汉字小达人比赛还有4个多月的时间&#xff0c;如何利用这段时间有条不紊地备考呢&#xff1f;我的建议是两手准备&#xff1a;①把小学1-5年级的语文课本上的知识点熟悉&#xff0c;重点是字、词、成语、古诗。②把历年真题刷刷熟…

脆皮之“指针和数组的关系”

文章目录 1. 数组名的理解2. 使用指针访问数组3. 一维数组传参的本质4. 冒泡排序5. 二级指针6. 指针数组7. 指针数组模拟二维数组 hello&#xff0c;大家好呀&#xff0c;窝是脆皮炸鸡。这一期是关于数组和指针的&#xff0c;我觉得并不是很难&#xff0c;但是我觉着下一期可能…

自定义el-select下拉菜单的内容以及数据回显的内容

最终的效果 下拉选项的自定义内容好实现&#xff0c;因为他有默认插槽&#xff0c;所以直接在el-option标签里面写自定义内容就可以实现 <el-selectref"seriesBorderTypeRef"class"series-border-type"change"changeSeriesBorderType"v-model…

ESLint: Unexpected ‘debugger‘ statement.(no-debugger)(debugger报红)

ESLint: Unexpected debugger statement.(no-debugger) 解决办法&#xff1a; 找到.eslintrc.js文件中rules的no-debugger更改为0即可

gpustat 不能使用问题

突然间就不能用了&#xff0c;可能是环境出了问题&#xff0c;如果GPU没问题的话&#xff0c;那么换个环境重新安装试一下&#xff08;pip install gpustat&#xff09;&#xff0c;目前是换个环境就可以了&#xff08;做个笔记&#xff09;

信息系统项目管理师0101:项目建议与立项申请(7项目立项管理—7.1项目建议与立项申请)

点击查看专栏目录 文章目录 第七章 项目立项管理7.1项目建议与立项申请1.立项申请概念2.项目建议书内容记忆要点总结第七章 项目立项管理 项目立项管理是对拟规划和实施的项目技术上的先进性、适用性,经济上的合理性、效益性,实施上的可能性、风险性以及社会价值的有效性、可…

镜舟科技亮相2024中国移动算力网络大会、Qcon、DTC等多项活动

在刚刚过去的 4 月份&#xff0c;镜舟科技受邀参与一系列技术交流活动&#xff0c;与移动云、金科创新社、infoQ、墨天轮、开科唯识等媒体及合作伙伴展开积极交流&#xff0c;并分享其在数据技术、金融等垂直行业领域的创新实践&#xff0c;从产业侧、业务侧、技术侧洞察需求、…

idea-自我快捷键-2

1. 书签 创建书签&#xff1a; 创建书签&#xff1a;F11创建特色标记书签&#xff1a;Ctrl F11快速添加助记符书签&#xff1a;ctrl shift 数字键 查看书签&#xff1a; shift F11快速定位到助记符书签&#xff1a;Ctrl 数字键 删除书签&#xff1a; delete 2. 自动…

【最新点云数据增强综述】深度学习点云数据增强技术的进展

深度学习(DL)已成为点云分析任务(如检测、分割和分类)的主流和有效方法之一。为了减少深度学习模型训练过程中的过拟合,提高模型性能,尤其是在训练数据的数量和/或多样性有限的情况下,增强往往至关重要。虽然各种点云数据增强方法已被广泛应用于不同的点云处理任务中,但…

docker搭建mysql集群实现主从复制

前言 随着业务的增长&#xff0c;一台数据服务器已经满足不了需求了&#xff0c;负载过重。这个时候就需要减压了&#xff0c;实现负载均衡和读写分离&#xff0c;一主一丛或一主多从。 主服务器只负责写&#xff0c;而从服务器只负责读&#xff0c;从而提高了效率减轻压力。 …

融资融券概念和操纵流程,案例解析

融资融券是一种金融工具&#xff0c;它允许投资者在证券市场上进行杠杆交易。简单来说&#xff0c;融资就是借钱买股票&#xff0c;融券就是借股票卖出。这种交易方式可以帮助投资者在短期内获得更高的收益&#xff0c;但同时也伴随着较高的风险。 案例背景&#xff1a; 假设…

基于VOLOPV2的自动驾驶环境感知系统

基于VOLOPV2的自动驾驶环境感知系统是一个复杂的系统&#xff0c;它主要负责实时检测并识别周围环境中的各种物体和信息&#xff0c;为自动驾驶车辆提供必要的感知数据。以下是对该系统的一个简要介绍&#xff1a; 环境感知是自动驾驶系统中的一个关键部分&#xff0c;它依赖于…

静态分析-RIPS-源码解析记录-03

既然有源码可以debug&#xff0c;那么直接跑测试用例&#xff0c;来跟踪处理逻辑感觉比直接看代码理逻辑更快一些&#xff0c;尤其是涉及到了扫描阶段&#xff0c;不然不容易弄清某刻某个变量的取值。 对于所有漏洞而言&#xff0c;都是由sink点到source点检测是否有过滤函数&…

数据库管理-第187期 23ai:怎么用SQL创建图(20240510)

数据库管理187期 2024-05-10 数据库管理-第187期 23ai:怎么用SQL创建图&#xff08;20240510&#xff09;1 安装PGX1.1 数据库配置对应用户1.2 使用RPM包安装Graph Server1.3 安装Oracle Graph Client1.4 访问PGX页面 2 SQL Property Graph2.1 创建SQL属性图2.2 关于点和边图元…

副业兼职没那么难,视频号带货,1天稳定500,适合新手操作

向大家推荐一个项目&#xff1a;视频号书单号带货玩法。我已经实践了一段时间&#xff0c;并成功售出了1200多单&#xff0c;赚取了2万多元。这个项目表现相当出色&#xff0c;强烈推荐给大家&#xff01; 周周近财&#xff1a;让网络小白少花冤枉钱&#xff0c;赚取第一桶金 …

[机器学习-03] Scikit-Learn机器学习工具包学习指南:主要功能与用法解析

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

芋道系统springcloud模块启动报错,枚举类不能为空

问题描述&#xff1a; Error starting ApplicationContext. To display the conditions report re-run your application with debug enabled. 2024-05-10 15:50:15.756 | ERROR 9120 | main [TID: N/A] o.s.b.d.LoggingFailureAnalysisReporter | ************************…

Vue创建todolist

电子书 第三章&#xff1a; https://www.dedao.cn/ebook/reader?idV5R16yPmaYOMqGRAv82jkX4KDe175w7xRQ0rbx6pNgznl9VZPLJQyEBodb89mqoO 没有使用VUE CLI创建项目。 创建步骤&#xff1a; 1&#xff0c; 用Vite 创建项目 2&#xff0c; npm run dev 运行程序 参照之前的文…

数据结构与算法学习笔记八-二叉树的顺序存储表示法和实现(C语言)

目录 前言 1.数组和结构体相关的一些知识 1.数组 2.结构体数组 3.递归遍历数组 2.二叉树的顺序存储表示法和实现 1.定义 2.初始化 3.先序遍历二叉树 4.中序遍历二叉树 5.后序遍历二叉树 6.完整代码 前言 二叉树的非递归的表示和实现。 1.数组和结构体相关的一些知…

搭建Harbor仓库

文章目录 Harbor仓库搭建Harbor仓库安装 docker 服务修改配置文件 Harbor仓库 搭建Harbor仓库 下载 Harbor 仓库 安装 docker 服务 # step 1: 安装必要的一些系统工具 yum install -y yum-utils device-mapper-persistent-data lvm2 # Step 2: 添加软件源信息 yum-config-m…