传输层之 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://xiahunao.cn/news/3030085.html

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

相关文章

Android 屏幕适配全攻略(上)-掌握屏幕单位,应对千变万化的设备

本文从 Android 开发中常见的长度单位 px、dp、sp 入手&#xff0c;详细介绍了它们的特点及转换关系。 接着深入探讨了屏幕尺寸、分辨率、像素密度等重要的屏幕指标&#xff0c;帮助读者全面理解它们之间的联系。最后&#xff0c;通过实例代码演示了如何在代码中进行单位转换&…

第一章 buffer cache管理 - 2 原理机制

本章节主要介绍缓冲区管理器机制&#xff0c;从原理上介绍共享缓冲区如何管理内存页。 1、缓冲区管理器的结构 PostgreSQL缓冲区管理器由缓冲区hash表、缓冲区buffer描述符和缓冲池组成。下面依次介绍这几个结构。 1.1 缓冲区标签 typedef struct buftag {RelFileNode rnod…

Python运维之协程

目录 一、定义协程 二、并发 三、异步请求 协程是一种轻量级的线程&#xff0c;它通过保存和恢复寄存器上下文和栈来实现调度切换&#xff0c;从而保留函数执行的状态。 这种机制使得协程在处理I/O密集型任务时效率较高&#xff0c;因为它们可以在I/O操作期间让出CPU&#…

5g视频彩信和普通彩信有什么区别

5G视频彩信和普通彩信有什么区别 随着科技的不断进步&#xff0c;手机通信技术也在迅速发展。5G技术的出现&#xff0c;为彩信传输提供了更高的速度和更广的带宽。在这种背景下&#xff0c;5G视频彩信和普通彩信成为了人们关注的焦点。本文将探讨这两种彩信的区别。 5G视频彩信…

Java数组的应用---选择排序(Select Sort)

一、需求&#xff1a;选择排序(Select Sort)&#xff0c;进行升序显示 在一组排列中把最大的数取出来放在一个新的列表里&#xff0c;再删去&#xff0c;在取最大的数出来&#xff0c;依次类推直到取到最后一个数字 二、定义一个无序的一维数组&#xff0c;并输出数组 程序运…

BBS客户端服务器的编写

根据网络编程中的内容&#xff0c;我们本篇文章将讲解一个bbs通信的项目&#xff0c;首先让我们了解一下什么是bbs. 一、bbs介绍 BBS&#xff0c;即Bulletin Board System的缩写&#xff0c;中文译为“电子公告板系统”或“网络论坛”。它是一个在网络上进行信息交流和讨论的…

重装前端整体流程

用户管理 --汇总 -- 明细-CSDN博客 一、node 这个看环境变量 2023最新版Node.js下载安装及环境配置教程&#xff08;非常详细&#xff09;从零基础入门到精通&#xff0c;看完这一篇就够了_nodejs安装及环境配置-CSDN博客 配置到国内镜像的时候&#xff0c;去看&#xff0c;淘…

代码随想录算法训练营第六十二天|503.下一个更大元素II、42.接雨水

代码随想录算法训练营第六十二天|503.下一个更大元素II、42.接雨水 503.下一个更大元素II 给定一个循环数组 nums &#xff08; nums[nums.length - 1] 的下一个元素是 nums[0] &#xff09;&#xff0c;返回 nums 中每个元素的 下一个更大元素 。 数字 x 的 下一个更大的元…

小程序(三)

十三、自定义组件 &#xff08;二&#xff09;数据方法声明位置 在js文件中 A、数据声明位置&#xff1a;data中 B、方法声明位置methods中&#xff0c;这点和普通页面不同&#xff01; Component({/*** 组件的属性列表*/properties: {},/*** 组件的初始数据*/data: {isCh…

【系统架构师】-案例篇(七)信息安全

某软件公司拟开发一套信息安全支撑平台&#xff0c;为客户的局域网业务环境提供信息安全保护。该支撑平台的主要需求如下&#xff1a; 1.为局域网业务环境提供用户身份鉴别与资源访问授权功能&#xff1b; 2.为局域网环境中交换的网络数据提供加密保护&#xff1b; 3.为服务…

26、Qt使用QFontDatabase类加载ttf文件更改图标颜色

一、图标下载 iconfont-阿里巴巴矢量图标库 点击上面的链接&#xff0c;在打开的网页中搜索自己要使用的图标&#xff0c;如&#xff1a;最大化 找到一个自己想用图标&#xff0c;选择“添加入库” 点击“购物车”图标 能看到刚才添加的图标&#xff0c;点击“下载代码”(需要…

js教程(13)

一、作用域 作用域规定了变量能够被访问的范围&#xff0c;而离开变量作用域的变量则不能被访问&#xff08;有时也叫变量的生命周期&#xff09;。作用域又分为局部作用域和全局作用域。 1.局部作用域 在函数或代码块内部声明的变量只能在其内部被访问&#xff0c;在外部无法…

牛客周赛 Round 41 C-F

C 小红的循环移位 思路&#xff1a; 一个数是不是四的倍数&#xff0c;只用看最后两位是否能够整除4即可。 #include <bits/stdc.h>using namespace std; const int N 1e6 5; typedef long long ll; typedef pair<ll, ll> pll; typedef array<ll, 3> p3;…

暗区突围进不去/游戏无法启动/掉帧卡顿/报错的解决方法

暗区突围是一款高拟真硬核射击手游&#xff0c;打造了全新的沉浸式暗区战局体验&#xff0c;发行商是腾讯公司。这个游戏名词虽然看起来有些陌生&#xff0c;但其本身的玩法内核毫无疑问的是&#xff0c;这款游戏在画面质量和枪械操作方面&#xff0c;都是手游市场上同类游戏中…

【vulhub靶场】Apache 中间件漏洞复现

【vulhub靶场】Apache 中间件漏洞复现 一、Apache HTTPD 换行解析漏洞&#xff08;CVE-2017-15715&#xff09;1. 漏洞详情2. 影响版本3. 漏洞复现 二、Apache多后缀解析漏洞&#xff08;apache_parsing_vulnerability&#xff09;1. 漏洞详情2. 漏洞复现 三、Apache HTTP Serv…

【LLM 论文】Step-Back Prompting:先解决更高层次的问题来提高 LLM 推理能力

论文&#xff1a;Take a Step Back: Evoking Reasoning via Abstraction in Large Language Models ⭐⭐⭐⭐ Google DeepMind, ICLR 2024, arXiv:2310.06117 论文速读 该论文受到的启发是&#xff1a;人类再解决一个包含很多细节的具体问题时&#xff0c;先站在更高的层次上解…

第8章.STM32开发方式(库函数)介绍

目录 0. 《STM32单片机自学教程》专栏 8.1 单片机的开发方式 8.1.1 直接操作寄存器 8.1.2 使用库函数 8.2 STM32的库函数 8.2.1 标准外设库(STD库) 8.2.2 HAL库 8.2.3 LL库 0. 《STM32单片机自学教程》专栏 本文作为专栏《STM32单片机自学教程》专栏其中的一…

数据库调优-SQL语句优化

2. SQL语句优化 sql 复制代码 # 请问这两条SQL语句有什么区别呢&#xff1f;你来猜一猜那条SQL语句执行查询效果更好&#xff01; select id from sys_goods where goods_name华为 HUAWEI 麦芒7 魅海蓝 6G64G 全网通; ​ select id from sys_goods where goods_id14967325985…

搜索的未来:OpenAI 的 GPT 如何彻底改变行业

搜索的未来&#xff1a;OpenAI 的 GPT 如何彻底改变行业 概述 搜索引擎格局正处于一场革命的风口浪尖&#xff0c;而 OpenAI 的 GPT 处于这场变革的最前沿。最近出现了一种被称为“im-good-gpt-2-chatbot”的神秘聊天机器人&#xff0c;以及基于 ChatGPT 的搜索引擎的传言&am…

MySQL索引(聚簇索引、非聚簇索引)

了解MySQL索引详细&#xff0c;本文只做整理归纳&#xff1a;https://blog.csdn.net/wangfeijiu/article/details/113409719 概念 索引是对数据库表中一列或多列的值进行排序的一种结构&#xff0c;使用索引可快速访问数据库表中的特定信息。 索引分类 主键索引&#xff1a…