Tcp协议的十大特性详解+示例

news2024/11/23 10:09:50

前言

    之前我们简单了解了一下Tcp是什么及它的套接字如何使用:基于UDP和TCP套接字实现简单的回显客户端服务器程序_Crystal_bit的博客-CSDN博客

 因为要给大家介绍Tcp的十大特性,所以这里给出Tcp报头结构:


目录

1. 确认应答

2. 超时重传

3. 连接管理

3.1 建立连接

3.2 断开连接

4. 滑动窗口

5. 流量控制

6. 拥塞控制

7. 延时应答

8. 捎带应答

9. 面向字节流

10. 异常情况


1. 确认应答

    Tcp协议有个特点就是可靠传输,而这里我们介绍的确认应答,就是实现可靠传输的重要机制.为了让大家更好理解确认应答这个特性,请看如下示例:

这里小丽的回复就相当于应答报文,也称ACK(acknowledge)报文.但是网络上信息传递真有这么简单吗?大家请看下个示例:

背景:论小明的求生欲.

正常情况下:

    

非正常情况:

这种情况下,小丽收到信息和小明发送的信息顺序发生了一定的改变,但是内容就大不一样了,他怕是要完蛋.为了帮小明解决这个问题,我们就可以对他发送的消息进行编号,这里我们把这个编号称为“序号”,同时分配一个“确认序号”。如上述示例问题作出解决:

 这样小丽就可以根据确认序号知道,小明回复的消息是针对她发送的哪条消息进行回复。但在TCP协议中,不是针对一条多条消息进行编号,而是针对每个字节进行编号,如图:

 确认序号是取发送方发过来的所有数据,最后一个字节的下一个字节。如果它< 1001则表示数据已经收到,并向发送方索要当前1001之后的数据,如图:

这样发送方就可以通过确认序号知道哪些数据已被接收方接收,那为什么会出现这种后发先至的问题呢?因为在网络传输过程中,很难保证数据的到达顺序,tcp有一个接收缓冲区,每个socket都有自己的一份缓冲区,tcp按照序号给收到的消息进行整队(优先级队列),那么就能保证应用数据读到的数据和发送数据顺序一致。

注:可靠传输不代表数据能100%传过去,只是说尽可能地传,如果传不过去,也会让发送方知道自己发送的数据没有发送成功.就比如我们微信发消息有的时候就会出现感叹号,我们就知道我们发送的消息,对方没有收到,但是大部分情况下,我们的消息都是发送成功了的.


2. 超时重传

      超时重传用于解决一个经典问题,那就是丢包问题。在网络传输环境中,发送方到接收方会涉及到多个结点的传输。如图:

当然实际情况可能远远不止这些结点。 

如果中间任何一个结点出现问题,都可能会导致丢包。为什么中间结点会出现问题呢?那是因为每个设备(结点),都在承担很多转发任务,而它们转发能力有上限。在某一时刻,某个中间设备的流量达到峰值,就可能会引起部分数据丢包。包如果丢了,那么接收方就不会返回ack应答报文。那么发送方等待一端时间后,如果还是没有收到ack,就会视为刚才发送的数据包已经丢失,最后就会重新发一次,这就叫超时重传。如果发生多次丢包,每丢包一次,超时等待的时间会变长,连续多次重传,都没有得到ack,那么tcp就会重置连接,如果重置连接失败,tcp就会关闭连接,放弃网络通信(只能保证尽可能地完成传输,不能保证100%完成传输)。

注:丢包有两种情况,一种是发送方发送的数据包丢了,ack未返回,另一种就是发送方发送的数据包收到了,但是返回的ack丢了。这两种情况都会超时重传。如果是第二种,发送方重传的数据包在接收方的缓冲区会根据收到数据的序号过滤,避免收到重复数据。


3. 连接管理

3.1 建立连接

        Tcp中客户端和服务器通过三次握手建立连接关系。握手也就是指通信双方进行网络交互,三次握手也就是三次网络交互,如图:

 类似于男女双方相互告白的过程,女方害羞,男方会先问女方你愿意做我的girlfriend吗?(syn)女方说她愿意(ack),但是她不确定男方是不是愿意,所以也会向男方发出疑问(syn),男方也返回一个我愿意(ack),这样男女双方关系就确定好了。一次连接中,通信双方都是唯一的。那我们怎么确定某个报文是syn报文还是ack报文呢?通过前言中给定的TCP报头结构的6个特殊比特位,它们一般默认是0,如果设为1,则表示特定的含义,第二位是ACK,第五位是SYN,只要某个报文中这两位为1,则分别表示该报文为应答报文或同步报文,当然如果它们都为1,则当前这个报文为syn+ack 报文。

大家这时候可能就会有疑问:为什么第2次交互和第3次交互要合并到一起?其实我们可以把它们拆开发送,但是没有必要,分两次发还不如一次就发完,可以提高封装分用的效率。

注:本质上三次握手这个过程就是在投石问路,验证客户端和服务器双方各自发送和接收能力是否正常。

3.2 断开连接

        和三次握手类似,断开连接我们通过四次挥手,通信双方各自给对方发送一个fin(结束报文),再各自给对方返回ack。如图:

这时候大家就会有一个问题,这里的第2步和第3步为什么大部分情况下没有合并呢?那是因为三次握手,ack和syn是同一个时机触发的(在内核中完成),而四次挥手,ack和fin则是不同时机触发的。ack是内核完成的,会在收到fin的时候第一时间返回,fin则是通过应用程序代码控制的,只有在调用了socket的close方法后才会触发fin(收到fin时立即调用close方法是可以合并的)。如果tcp客户端没有显式调用close,那等进程结束,会自动调用close方法,也会触发fin。

注:断开连接,客户端和服务器都有可能先发起,而建立连接一定是客户端主动先发起。断开连接的整个过程,内核会一直将tcp连接进行维护,直到四次挥手完成。


4. 滑动窗口

       滑动窗口是为了提高传输效率的特性。主要是靠缩短发送方等待时间,让发送方批量去发送数据,一次发多条数据,一次等待多个ack应答报文。我们将批量传输数据的过程称为滑动窗口。如图:

 批量不是无限发送,当发送到了一定程度就等待ack返回,而不等待直接发送的数据量是有限的,每回来一个ack就立即发送下一条,相当于总的要批量等待的数据是一致的。我们可以把要批量等待数据的数量称为“窗口大小”,如图:

 从上图中可以看出窗口大小没变,但是它在不断往后挪动,如果发送方很快就收到ack,那么这个窗口就在快速往后滑动。

批量发送的丢包情况:

情况一:数据包已经抵达,但是ack丢失。如图:

 这种情况下,是没问题的,确认序号的意思就是该序号之前的数据已经收到,在图例中,虽然大部分的ack都丢失了,但是当6001确认序号返回的时候,发送方就会知道6001之前的数据已经收到,那么1001,2001,4001,5001....这些确认序号丢失也没有关系,这时候大家就会说那如果最后一个确认序号丢失了怎么办?当然就靠我们的超时重传了啊~滑动窗口是建立在可靠性的基础上,对传输效率的提高。

情况二:数据包丢失,接收方未收到数据。如图:

 当1001-2000段的数据丢失后,接收方会一直索要1001-2000段的数据,不会因为收到2001-3000的数据就直接返回3001。发送方连续收到多个1001确认序号就可以确定1001-2000段的数据丢失,就会重传1001-2000段的数据,此外,由于1-1000,2001-7000的数据已经接收,所以重传后返回的确认序号7001,表示7001之前的数据已经全部接收到。类似于在主机B的接收缓冲区里拼图,每收到一端数据放到对应位置,差哪块就要持续索要哪块。上述重传称为快速重传,它和超时重传是不一样的,它只有丢了数据才会重传,不丢的数据不会重传,整体速度比较快。

一般传输少量数据,就无需使用滑动窗口,直接确认应答+超时重传。

注:在可靠性的基础上,使用一份时间等待多个ack,缩短总等待时间从而提升整体效率。如果主机B的缓冲区满了,Tcp也有机制来控制这种情况尽量避免丢包,类似于阻塞等待的效果。


5. 流量控制

      在滑动窗口(批量发送)的条件下,窗口越大,批量发送的数据越多,整体的速度就越快。但是别忘了,Tcp是可靠传输,批量发送太快,接收方缓冲区一下子就满了,那就会导致数据丢包。所以这就需要Tcp另一种特性——流量控制,让接收方去限制发送方的速度(有时候可能会让发送方阻塞一下)。在TCP报头结构中,当第二位ack为1时,窗口大小字段就会生效,这里的值就是建议发送方发送的窗口大小(接收缓冲区剩余的空间大小):

当接收缓冲区没有满的时候,发送方会根据接收缓冲区剩余空间大小进行数据发送,直到缓冲区满了,就停止发送并且会隔一段时间就触发一个窗口探测报文,如果探测一会发现接收缓冲区剩余空间不为0,腾出空间了就会继续发送数据。应用程序向接收方缓冲区读取数据时,接收缓冲区内容就会被消耗,从而空间就可以腾出来了。

注:这里返回的窗口大小与实际可能会有出入,实际上发送方的窗口大小 = 流量控制 + 拥塞控制.


6. 拥塞控制

      上述说到的流量控制主要衡量接收方的处理能力,而拥塞控制特性主要衡量中间节点,传输的能力(传输路径的处理能力)。发送方到接收方有多条路径,且每条路径有多少个结点,每个结点当前的情况如何,都是未知的。一条路径上的某个结点处理情况出现问题都会影响整个传输速率。拥塞控制就是不断通过实验的方式,找到一个合适的发送速率,达到发送和消耗两者的动态平衡

如图:

从图中可以看出,以一个非常小的窗口(比较小的速度)慢开始,每次进行指数增长,让窗口大小短时间内就达到比较大的值,快速接近当前网络传输路径的能力瓶颈,达到阈值,就变成线性增长,逐渐接近传输上限。如果出现丢包,那么当前窗口大小已经超过传输上限。此时又将窗口大小恢复到一个比较小的初始值。重复上诉过程,直到找到一个合适的发送速率。

注:实际发送方的窗口大小 = min(拥塞窗口,流量控制窗口)。不同的系统实现,阈值很可能存在差别。


7. 延时应答

      上述中我们可以知道决定传输效率的关键元素就是窗口大小。而延时应答就是通过延时让接收方应用程序尽可能多消耗数据,那么返回的窗口大小就会大一些同时接收方也能处理得过来,发送方的发送速率也能更快一些。举个例子:吃自助的过程中,我一般都是不建议一直不间断地吃的,吃一会儿等肚子消化一会儿,等胃空间空出来了,才能吃得更多。来自吃货的自豪~

注:不是所有的包都可以延迟应答。分两种情况:一种是数量限制,另一种是时间限制。前者是每隔N个包就延迟应答一次,后者是超过最大延迟时间就延迟应答一次。


8. 捎带应答

      捎带应答基于延时应答,客户端服务器之间的通信模型有四种:

             ①  一问一答。大多数情况下都是这种。

             ② 多问一答。上传大文件。

             ③ 一问多答。下载大文件。

             ④ 多问多答。游戏串流。

       在四次挥手过程中,ack应答报文由内核负责,收到请求后会被立即返回,而fin结束报文是通过应用程序调用close方法触发。这俩时机是不同的,但是在延时应答的条件下,ack应答报文就可能稍等一会儿再发送,那么就很可能和fin结束报文合并成一个数据报一起发送,这就是捎带应答。这也是为什么四次挥手也有可能是三次的原因。


9. 面向字节流

       TCP特性之一面向字节流进行数据传输,那么这里就涉及到一个经典问题——粘包问题。当主机A向主机B发送了多个应用层数据报,那么这些数据报就会在主机B的接收缓冲区紧紧挨在一起,那么B的应用程序读数据就无法将这多个数据报完整地区分出来。对于上述这种问题,我们可以定义分隔符(比如每条信息都以;或。结尾) 或者 约定整个数据报长度(由产品经理约定的需求决定)。


10. 异常情况

         ① 进程关闭/崩溃:进程没了,socket是文件,随之被关闭。虽然进程没了,但是连接还在,仍然可以四次挥手。

         ② 主机关机(正常流程关闭):会杀死所有用户进程,触发四次挥手,如果没挥完,ack应答报文没返回给发送方,那么发送方就会重传,如果几次都没有收到ack,就尝试重置连接,重置连接失败则释放连接。

         ③ 主机掉电(非正常关闭):对端如果是发送方,那么就跟②步骤一样,但是如果对端是接收方,我们还没发数据就over了,这时候就涉及到另一个机制——心跳包保活机制(周期性+心跳没了就挂了)。对方给我们发的心跳包(ping),我们关机了则无法返回pong,对端就会知道我们这边已经挂了。就类似于人的心跳,心在跳动,人就还活着,不跳了人也就没了。但一般心跳的确定不会很严格,会设置连续多少次ping了却没有pong才视为连接异常。

         ④ 网线断开:和③一致


除上述十大核心机制外,TCP还有别的很多机制,大家有兴趣可以去TCP REC标准文档中了解。

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

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

相关文章

【Android复习笔记】Parcelable 为什么速度优于 Serializable ?

Q:Parcelable 为什么速度优于 Serializable ? 首先,抛开应用场景谈技术方案都是在耍流氓,所以如果你遇到有面试官问这样的题目本身就是在给面试者挖坑。 序列化 将实例的状态转换为可以存储或传输的形式的过程。 Serializable 实现方式: Serializable 是属于 Java 自带的…

Solid Converter PDF v10 安装及使用教程

目录 一、软件介绍二、下载教程三、安装教程四、使用教程1.PDF转Word、Html等2.合并PDF文件 一、软件介绍 Solid Converter PDF是一套专门将PDF文件转换成Word的软件。 能够将PDF转换为Word、Excel、HTML、PowerPoint、纯文本文件从PDF文档中提取数据并以CSV等格式保存能够转…

数仓工程师理解复杂业务的思考方法论

模型设计框架&#xff08;业务过程驱动&#xff09;还是在经典的三层数据模型架构下去进行&#xff0c;概念模型、逻辑模型、物理模型 首先概念模型其实是业务过程&#xff08;流程图&#xff09;&#xff0c;其中需要考虑到几个方面&#xff1a; 1.数据 业务覆盖 业务感知、…

循坏队列CircularQueue

前言 一、CircularQueue 二、特点 三、设计思路 1&#xff09;判空与判满 2&#xff09;链表还是数组实现&#xff1f; 四、实现 1).IsEmpty() 2).IsFull() 3)CircularQueueCreate创建 4&#xff09;CircularQueueEnQueue插入 5&#xff09;CircularQueueDeQueue删除 6&#xf…

React Hook之useCallback 性能优化

上文 对比之前的组件优化说明React.memo的作用我们说了 React.memo的妙用 但是 它却并非万能 我们来看这个情况 我们子组件代码编写如下 import React from "react";const ChildComponent ({ dom1funt }) > {console.log("ChildComponent 被重新渲染"…

规则引擎--规则集:规则集合的组织和执行

目录 回顾easy-rules的rules执行如何想规则集合的构造 规则集合定义普通规则集和执行定义树形规则集 当弄清楚了一个规则的设计和执行逻辑后&#xff0c;接下来需要考虑的就是许多的规则如何组织了&#xff0c;即规则集的抽象设计。 来看一些例子 回顾easy-rules的rules执行 …

NFCEE Discovery and Mode Set

10.1 NFCEE ID NFCC 动态为 NFCEE 分配 ID&#xff08;称为“NFCEE ID”&#xff09;。 DH 通过执行 NFCEE Discovery 来了解 ID 值。 在配置状态为 0x01 的 NFCC 重置之前&#xff0c;NFCEE ID 一直有效。 值为 0x00 的 ID 在本规范中称为 DH-NFCEE ID&#xff0c;并且应代表…

五、Docker本地镜像发布到阿里云/发布到私有库

目录 前言一、本地镜像发布到阿里云1.1 流程图1.2 注册阿里云创建容器服务个人实例1.3 创建命名空间1.4 创建镜像仓库1.5 将镜像推送到阿里云本地仓库 二、从阿里云仓库拉去自己推送的镜像三、本地镜像发布到阿里云总结四、本地镜像发布到私有库4.1 流程图4.2 下载镜像Docker R…

Shell编程从入门到实践——实践篇

欢迎关注 「Android茶话会」 回 「学习之路」 取Android技术路线经典电子书回 「pdf」 取阿里&字节经典面试题、Android、算法、Java等系列武功秘籍。回 「天涯」 取天涯论坛200精彩博文,包括小说、玄学等 背景 之前在搞一些CI/CD,使用到了shell脚本&#xff0c;shell的开…

nvdiffrec在Windows上的配置及使用

nvdiffrec是NVIDIA研究院开源的项目&#xff0c;源代码地址&#xff1a;https://github.com/NVlabs/nvdiffrec &#xff0c;论文为《Extracting Triangular 3D Models, Materials, and Lighting From Images》&#xff0c;从图像中提取三角形三维(三角网格)模型、空间变化的材质…

uni-app微信小程序获取手机号授权登录(复制即用,js完成敏感数据对称解密,无需走服务端处理)

目录 一、示例 二、具体实现说明 一、示例 获取到的手机号 二、具体实现说明 属性说明 属性名说明生效时机getphonenumber获取用户手机号回调open-type"getPhoneNumber" 按钮写法 <template><view class"login"><view class"content…

为什么要写这个带点玄幻气息的英语单词记忆博客

&#x1f31f;博主&#xff1a;命运之光 ☀️专栏&#xff1a;英之剑法&#x1f5e1; ❤️‍&#x1f525;专栏&#xff1a;英之试炼&#x1f525; ☀️博主的其他文章&#xff1a;点击进入博主的主页 &#x1f433; 开篇想说的话&#xff1a;开学就大三了&#xff0c;命运之光…

DMA详解及应用(嵌入式学习)

DMA 0. 前言1. DMA作用2. DMA特性3. DMA寄存器4. DMA的增量或者循环模式5. 练习 0. 前言 DMA&#xff08;Direct Memory Access&#xff0c;直接内存访问&#xff09;是一种计算机系统中用于高效地实现数据传输的技术。它允许数据在外设和内存之间直接传输&#xff0c;而无需C…

GEE:为每个对象(斑块/超像素)添加属性

作者:CSDN @ _养乐多_ 本文将介绍为每个对象(斑块/超像素)添加属性的代码。并举例将最近距离作为属性添加到每个对象(斑块/超像素)特征中。 结果如下图所示, 文章目录 一、代码二、代码链接一、代码 这段代码的目的是对动态世界土地覆盖图像进行分析,并提取出其中的目…

贪婪算法简介-数据结构和算法教程

贪婪算法是一种算法范例&#xff0c;它遵循在每个阶段进行局部最优选择的问题求解启发式&#xff0c;希望找到全局最优值。换句话说&#xff0c;贪婪算法在每一步都选择最好的可能选项&#xff0c;而不考虑该选择对未来步骤的影响。 当一个问题可以被划分成更小的子问题&#…

1.GPIO的工作原理

1.stm32引脚说明&#xff1a; 对于stm32f103zet6&#xff1a; 一共有7组io口&#xff1b;每组io口有16个io&#xff1b;一共有16*7112个io&#xff1b;分组情况为&#xff1a;GPIOA&#xff0c;GPIOB~GPIOG&#xff1b; 2.GPIO的基本结构&#xff1a; 3.GPIO的工作模式&…

C++入门:类和对象(后)

目录 前言&#xff1a; 一&#xff1a;static成员 (1)概念 (2)特性 (3)例子 二&#xff1a;explicit关键字 三&#xff1a;内部类 (1)概念 (2)特性 (3)实例 四&#xff1a;匿名对象 (1)概念 (2)特性 (3)实例 五&#xff1a;拷贝对象时的一些编译器优化 (1)引入 …

Spring整合MyBatis底层原理

Spring整合MyBatis底层原理 项目结构图 项目代码 build.gradle需要进入的依赖 // testImplementation(platform("org.junit:junit-bom:5.9.1")) // testImplementation("org.junit.jupiter:junit-jupiter")implementation("org.aspectj:aspect…

电池SOC和动力电池OCV功率联合估计研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

爱心方程(理科生的浪漫?)

目录 前言 C/C 源代码 扩展 Java Python HTML 前言 这个在大一的时候就想找了&#xff0c;然后后面是找到了一个&#xff0c;但是忘记出处了。我决定把可以找到的所有爱心给整理一下&#xff0c;为了实现“理科生的浪漫”&#xff01;&#xff01;&#xff01; C/C 首先…