TCP相关特性

协议段格式

• 源/⽬的端⼝号:表⽰数据是从哪个进程来,到哪个进程去;
• 32位序号/32位确认号:后⾯详细讲;
• 4位TCP报头⻓度:表⽰该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最⼤⻓度是15*4=60
• 6位标志位:
    ◦ URG:紧急指针是否有效
    ◦ ACK:确认号是否有效
    ◦ PSH:提⽰接收端应⽤程序⽴刻从TCP缓冲区把数据读⾛
    ◦ RST:对⽅要求重新建⽴连接;我们把携带RST标识的称为复位报⽂段
    ◦ SYN:请求建⽴连接;我们把携带SYN标识的称为同步报⽂段
    ◦ FIN:通知对⽅,本端要关闭了,我们称携带FIN标识的为结束报⽂段
• 16位窗⼝⼤⼩:后⾯再说
• 16位校验和:发送端填充,CRC校验.接收端校验不通过,则认为数据有问题.此处的检验和不光包含TCP⾸部,也包含TCP数据部分.
• 16位紧急指针:标识哪部分数据是紧急数据;

接下来我们来谈谈TCP常见的几个特性

1.确认应答-------用来确保可靠性最核心的机制

TCP将每个字节的数据都进⾏了编号.即为序列号.
 

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

2.超时重传 --------确认应答的补充

没收到ack 一种情况就是丢包

如果一切顺利,通过应答报文就可以告诉发送方,当前数据是不是成功收到

这个情况下,就要超时重传了

发送方发了个数据,要等;  等的时间里收到了ack(数据报在网络上传输,需要时间)

等了好久,ack还没等到,此时发送方就认为数据的传输出现丢包

当认为丢包之后,就会把刚才的数据包在传输一次(重传)

等待的过程有一个时间阈值(上线),就是(超时)

没收到ack 另一种情况就是ack丢了

因此接收方会收到很多重复数据.那么TCP协议需要能够识别出那些包是重复的包,并且把重复的丢弃掉

接收方如何判定这个数据是否是"重复数据"   核心判定依据,数据的序号 ---序列号

a.数据还在接收缓冲区里没有被read走

此时,就拿着新收到的数据的序号,和缓冲区中的所有数据的序号对一下,看看有没有一样的

有一样的就是重复了,就可以把新收到的数据丢弃了

b.数据在接收缓冲区中,已经被应用程序read走了,此时新来的数据序号直接无法在接收缓冲区查到

超时是会重传,重传也不是无限的重传.......重传过程也是有一定的策略~~

1.重传次数是有上限的,重传到一定程度,还没有ack ,就尝试重置连接,如果重置也失败,就直接放弃连接

2.重传的超时时间阈值也不是固定不变的,随着重传次数的增加,而增大(重传频率越来越低)

        经历重传之后还是丢包,大概率是网络问题,再怎么重传也白费力气,重传还是要重传,但是可以少传几次

数据丢了,还是ack丢了,发送方角度看起来,就是区分不了,都是ack没收到

3.连接管理

1)建立连接(三次握手)

SYN称为同步报文, 意思就是向另外一放申请连接, ACK为应答报文, 意思为同意建立连接)

三次握手本质是就是检测客户端和服务器各自的发送能力和接收能力是否正常.

建⽴连接的意义:

1. 投⽯问路,确认当前通信路径是否畅通.
2. 协商参数,通信双⽅共同确认⼀些通信中的必备参数数值.

        其中一个信息挺关键的,TCP通信的序号,初始值

        TCP一次通信过程中,序号不是从0或者1开始计算的 

        而是选择一个比较大的数字,以这个数字开头来继续计算

        即使同一个客户端和服务器,每次连接没开始的序号都不同 

2)断开连接(四次挥手)

断开连接的本质:把对方信息从数据结构中给删除掉

       

四次挥手能否像三次握手一样,把中间两次交互合并?

有时候能合并,有时候不能合并,不像三次握手一样,100%合并

在实际通信过程中,ack 和第二个fin时间间隔比较长,此时就无法进行合并,就分两次进行传输

四次挥手和三次握手有什么区别?

相似

都是通信双方各自给对方发起一个syn/fin,各自给对方返回ack

数据传输的顺序,syn/ack/syn/ack       fin/ack/fin/ack

不同

三次握手中间两次一定能合并,四次挥手则不一定

三次握手,必须是客户端主动,    四次挥手 ,客户端/服务器都可以主动

确认应答,超时重传,连接管理----->可靠传输,可靠传输也付出了代价 传输效率

4.滑动窗口  ----->TCP中非常有特点的机制

确认应答机制下,每次发送方收到一个ack才会发下一个数据,导致大量的时间都消耗在等待ack上了

滑动窗口提出就是为了解决上述问题的,滑动窗口就可以在保证可靠传输的基础上,提高效率(这里的提高效率其实是降低损失,而不是增加速度)

• 窗⼝⼤⼩指的是⽆需等待确认应答⽽可以继续发送数据的最⼤值.上图的窗⼝⼤⼩就是4000个字节(四个段).
• 发送前四个段的时候,不需要等待任何ACK,直接发送;
• 收到第⼀个ACK后,滑动窗⼝向后移动,继续发送第五个段的数据;依次类推;
• 操作系统内核为了维护这个滑动窗⼝,需要开辟发送缓冲区来记录当前还有哪些数据没有应答;只
有确认应答过的数据,才能从缓冲区删掉;
• 窗⼝越⼤,则⽹络的吞吐率就越⾼;

那么如果出现了丢包,如何进⾏重传?这⾥分两种情况讨论.
情况⼀:数据包已经抵达,ACK被丢了


这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进⾏确认;

情况⼆:数据包就直接丢了.


• 当某⼀段报⽂段丢失之后,发送端会⼀直收到1001这样的ACK,就像是在提醒发送端"我想要的是1001"⼀样;
• 如果发送端主机连续三次收到了同样⼀个"1001"这样的应答,就会将对应的数据1001-2000重新发送;
• 这个时候接收端收到了1001之后,再次返回的ACK就是7001了(因为2001-7000)接收端其实之前就已经收到了,被放到了接收端操作系统内核的接收缓冲区中;

确认应答 超时重传  滑动窗口  快速重传   并不冲突 ,而且是同时存在

滑动窗口中当然也有确认应答,只不过把等待的策略稍作调整,转成批量的

批量的前提,短时间发了很多数据,如果发的数据很少,此时滑动窗口滑不起来,退化成确认应答

如果当前传输过程是按照滑动窗口(短时间传输大量数据) 就按照快速重传保证可靠性.此时判定丢包的标准就是看连续多少个ack索要数据

如果当前传输过程不是按照滑动窗口(没有传很多数据),此时仍按照之前的超时重传保证可靠性,此时判断丢包就是达到超时时间还没有ack到达

5.流量控制(流控)

通过滑动窗口可以提高传输效率

窗口大小越大,更多的数据复用同一块时间等待,效率就越高(批量传多少数据不需要等待ack,此时数据的量就称为"窗口大小")

接收端处理数据的速度是有限的.如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继⽽引起丢包重传等等⼀系列连锁反应.
因此TCP⽀持根据接收端的处理能⼒,来决定发送端的发送速度.这个机制就叫做流量控制(Flow
Control);

• 接收端将⾃⼰可以接收的缓冲区⼤⼩放⼊TCP⾸部中的"窗⼝⼤⼩"字段,通过ACK端通知发送端;
• 窗⼝⼤⼩字段越⼤,说明⽹络的吞吐量越⾼;
• 接收端⼀旦发现⾃⼰的缓冲区快满了,就会将窗⼝⼤⼩设置成⼀个更⼩的值通知给发送端;
• 发送端接受到这个窗⼝之后,就会减慢⾃⼰的发送速度;
• 如果接收端缓冲区满了,就会将窗⼝置为0;这时发送⽅不再发送数据,但是需要定期发送⼀个窗⼝探测数据段,使接收端把窗⼝⼤⼩告诉发送端.

接收端如何把窗⼝⼤⼩告诉发送端呢?回忆我们的TCP⾸部中,有⼀个16位窗⼝字段,就是存放了窗⼝⼤⼩信息;
那么问题来了,16位数字最⼤表⽰65535,那么TCP窗⼝最⼤就是65535字节么?
实际上,TCP⾸部40字节选项中还包含了⼀个窗⼝扩⼤因⼦M,实际窗⼝⼤⼩是窗⼝字段的值左移M位;

6.拥塞控制   限制发送方发送数据的速率

                     流量控制是站在接收方的角度来制约发送方速率的

总的原则是,流量控制和拥塞控制,谁产生的窗口大小更小,谁就说了算

这个拥塞控制具体是怎么这个窗口大小给试出来de?

1.慢启动,刚开始传输得数据,速率是比较小的,采用的窗口大小也就比较小

   此时,网络的拥堵情况未知,如果一上来就搞很大,可能就让本来不富裕的网络带宽,就雪上加霜了

2.如果上述传输的数据,没有出现丢包,,说明网络还是畅通的,就增加窗口大小,此时,增大方式是按指数来增长的

 由于使用慢启动,开始的时候,窗口大小非常小,也有可能网络上就是很畅通,通过指数增长可以让上述的窗口大小快速变大,这样就可以保证传输的效率

3.指数增长,不会一直持续保持的,可能会增长太快,一下就导致网络拥堵

    这里引入一个阈值,当拥塞窗口达到阈值之后,此时,指数增长就成了线性增长

    线性增长能使当下的窗口持久保持在一个比较高的速率,并且也不容易一下就造成丢包

4.线性增长也是一直在增长,积累一段时间之后,传输的速率可能过快,此时还是会引起丢包

   一旦出现丢包.就把拥塞窗口重置成一个较小的值,回到最初的 慢启动 过程(又要重新指数增长)

   并且这里也会根据刚才丢包时窗口大小,重新设置指数增长到线性增长的阈值

7. 延时应答

也是基于滑动窗口,是要尽可能的再提高一点效率

结合滑动窗口以及流量控制,能够通过延时应答ack的方式.把反馈的窗口大小,搞大一些

接收收到数据之后,不会立即返回ack,而是稍等一下,等一会再返回ack.等了这一会,相当于给接收方的应用程序这里,腾出来更多的时间,来消费这里的数据

核心就在于允许的范围内,让窗口尽可能的大

那么所有的包都可以延迟应答么?肯定也不是;

• 数量限制:每隔N个包就应答⼀次;
• 时间限制:超过最⼤延迟时间就应答⼀次;
具体的数量和超时时间,依操作系统不同也有差异;⼀般N取2,超时时间取200ms;


8.捎带应答   基于延时应答,引入的机制,能够提高传输效率

修改窗口大小,确实是提升效率的有效途径

捎带应答,就是走另一条路,尽可能的把能合并的数据包进行合并,从而起到提高效率的效果

9.⾯向字节流

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

• 调⽤write时,数据会先写⼊发送缓冲区中;
• 如果发送的字节数太⻓,会被拆分成多个TCP的数据包发出;
• 如果发送的字节数太短,就会先在缓冲区⾥等待,等到缓冲区⻓度差不多了,或者其他合适的时机发送出去;
• 接收数据的时候,数据也是从⽹卡驱动程序到达内核的接收缓冲区;
• 然后应⽤程序可以调⽤read从接收缓冲区拿数据;
• 另⼀⽅⾯,TCP的⼀个连接,既有发送缓冲区,也有接收缓冲区,那么对于这⼀个连接,既可以读数据,也可以写数据.这个概念叫做全双⼯

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

• 写100个字节数据时,可以调⽤⼀次write写100个字节,也可以调⽤100次write,每次写⼀个字节;
• 读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以⼀次read100个字节,也可以⼀次read⼀个字节,重复100次;

粘包问题

• ⾸先要明确,粘包问题中的"包",是指的应⽤层的数据包
• 在TCP的协议头中,没有如同UDP⼀样的"报⽂⻓度"这样的字段,但是有⼀个序号这样的字段.
• 站在传输层的⻆度,TCP是⼀个⼀个报⽂过来的.按照序号排好序放在缓冲区中.
• 站在应⽤层的⻆度,看到的只是⼀串连续的字节数据
• 那么应⽤程序看到了这么⼀连串的字节数据,就不知道从哪个部分开始到哪个部分,是⼀个完整的应⽤层数据包.

那么如何避免粘包问题呢?归根结底就是⼀句话,明确两个包之间的边界.

• 对于定⻓的包,保证每次都按固定⼤⼩读取即可;例如上⾯的Request结构,是固定⼤⼩的,那么就从缓冲区从头开始按sizeof(Request)依次读取即可;
• 对于变⻓的包,可以在包头的位置,约定⼀个包总⻓度的字段,从⽽就知道了包的结束位置;
• 对于变⻓的包,还可以在包和包之间使⽤明确的分隔符(应⽤层协议,是程序猿⾃⼰来定的,只要保证分隔符不和正⽂冲突即可);

思考:对于UDP协议来说,是否也存在"粘包问题"呢?

• 对于UDP,如果还没有上层交付数据,UDP的报⽂⻓度仍然在.同时,UDP是⼀个⼀个把数据交付给应⽤层.就有很明确的数据边界.
• 站在应⽤层的站在应⽤层的⻆度,使⽤UDP的时候,要么收到完整的UDP报⽂,要么不收.不会出现"半个"的情况.

10.异常情况

1)其中有一方出现了 ,进程崩溃

进程无论是正常结束还是异常崩溃,都会触发到回收文件资源,关闭文件这样的效果(系统自动完成)就会触发 四次挥手

TCP连接的生命周期,可以比进程更长一些,虽然进程已经退出了,但是TCP连接还在,仍然可以继续四次挥手

虽然说是异常崩溃,实际上和正常的四次挥手结束,没啥区别,进程不在了,是通过系统中仍然持有的连接信息,完成后续的挥手过程的

3)其中一方出现断电(直接拔电源,也是关机,更突然性的关机)

如果直接断电,机器瞬间关机,此时,肯定来不及发送fin

a.断电是接收方,发送方就会突然发现,没有ack了,就要重传,重传几次之后还是,还是不行

TCP就会尝试"复位"连接(相当于清除原来的TCP中的各种临时数据,重新开始)

需要用到一个TCP中的"复位报文段"

b.断电是发送方?接收方本来就是在阻塞等待发送方的消息,迟迟没来消息,咋办

这种情况下,接收方需要区分出,发送方是挂了,还是好着暂时没法

TCP中也是如此,接收方一段时间之后,没有收到对方的消息,就会触发"心跳包"来询问对方的情况

如果对端没心跳了,此时本端也就会尝试复位并且单方面释放连接了

心跳包:也是不携带应用层数据的特殊数据包

1.周期性

2.没有心跳,视为是对端挂了

4)网线断开

⼩结


为什么TCP这么复杂?因为要保证可靠性,同时⼜尽可能的提⾼性能.
可靠性:
• 校验和
• 序列号(按序到达)
• 确认应答
• 超时重发
• 连接管理
• 流量控制
• 拥塞控制
提⾼性能:
• 滑动窗口
• 快速重传
• 延迟应答
• 捎带应答
其他:
• 定时器(超时重传定时器,保活定时器,TIME_WAIT定时器等)

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

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

相关文章

ARMv8架构特殊寄存器介绍-0

一、zero 寄存器 零寄存器用作源寄存器时读取零,用作目标寄存器时丢弃结果。您可以在大多数指令中使用零寄存器,但不是所有指令。二、sp寄存器 在ARMv8架构中,要使用的堆栈指针的选择在某种程度上与Exception级别。默认情况下,异…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:ScrollBar)

滚动条组件ScrollBar,用于配合可滚动组件使用,如List、Grid、Scroll。 说明: 该组件从API Version 8开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 子组件 可以包含单个子组件。 接口 ScrollBar(val…

6、Design Script之列表

Range 在DesignScript中,Range是从起点到终点的一系列数字,使用指定的步距(间距类型),并有以下的初始化方法: start..end..step; start..end..#amount; start..end..~approximate; Range可以是数字的,也可以是字母的。 字母范围因大小写而异。 开始,结束. .#数量范围(…

springboot276基于JS的个人云盘管理系统的设计与实现

个人云盘管理系统设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装个人云盘管理系统软件来发挥其…

P6技巧:导出XER设置老版本/新版本

前言 在一个大型的项目中,虽然业主方已要求承包商必须使用P6格式来提交计划,但实际情况是承包商会给到你多种不同版本的XER文件,使得得在Oracle Primavera P6 之间导入或导出。 如果收到的 XER 文件不适合你使用的 Primavera P6 版本&#x…

java学习之路-程序逻辑控制

目录 1.分支结构 1.1 if语句 栗子 判断奇数还是偶数 判断一个年份是否为闰年 1.2switch语句 栗子 2. 循环结构 2.1while 循环 栗子 2.2break和continue break continue 2.3for循环 基本语法 栗子 2.4 do while 循环 3.输入输出 3.1输出 3.2从键盘输入 栗子…

南大通用数据库-Gbase-8a-学习-43-SQL长时间处于Writing to net状态排查

目录 一、问题截图 二、排查思路 1、Gbase8a SQL有几种状态 2、问题导致原因猜想 3、观察服务端(集群端)网络情况 4、观察客户端网络情况 5、排查客户端程序处理数据慢 5.1、send (1)声明 (2)作用…

数据库不应放在容器中?- B站Kubernetes有状态服务实践(Elasticsearch/Clickhouse)

本期作者 前言 云原生时代下, Kubernetes已成为容器技术的事实标准, 使得基础设施领域应用下自动化运维管理与编排成为可能。对于无状态服务而言, 业界早已落地数套成熟且较完美的解决方案。可对于有状态的服务, 方案的复杂度就以…

精简版 Obsidian 图床配置 PicGo+ gitee

精简版 Obsidian 图床配置 PicGo gitee 图床的作用 图床(Image Hosting Service)是一种在线服务,用于存储和托管用户上传的图片文件。用户可以将图片上传到图床服务器,并获得一个可访问的图片链接,然后可以在网页、博…

模板进阶:非类型模板参数,特化

一、非类型模板参数 非类型模板参数&#xff0c;就是用一个常量作为 类/函数 的模板参数&#xff0c;在 类/函数 中可以被当成常量使用。 template<class T, size_t N>// N 为一个非类型模板参数 class Stack { public:Stack(){_a new T[N];} protected:T* _a; };int m…

GAMMA数据处理(六)

最近进行Sentinel-1数据处理,需要用到拼接三个iw的命令,找了一下 但是这样大的数据提取进来,处理速度很慢,后面可能要进行研究区的裁剪,除了开始用的方法,直接在生成的burst_tab文件里面修改参数:删去自己不需要的iw,也就是下面说的这种方法 在指导手册上面看到了其他的…

前端接口防止重复请求实现方案

虽然大部分的接口处理我们都是加了loading的&#xff0c;但又不能确保真的是每个接口都加了的&#xff0c;可是如果要一个接口一个接口的排查&#xff0c;那这维护了四五年的系统&#xff0c;成百上千的接口肯定要耗费非常多的精力&#xff0c;根本就是不现实的&#xff0c;所以…

使用OpenCV实现人脸特征点检测与实时表情识别

引言&#xff1a; 本文介绍了如何利用OpenCV库实现人脸特征点检测&#xff0c;并进一步实现实时表情识别的案例。首先&#xff0c;通过OpenCV的Dlib库进行人脸特征点的定位&#xff0c;然后基于特征点的变化来识别不同的表情。这种方法不仅准确度高&#xff0c;而且实时性好&am…

Serverless:无服务器架构的魅力与实践

导语&#xff1a;随着云计算的不断发展&#xff0c;无服务器架构&#xff08;Serverless&#xff09;逐渐成为开发人员关注的焦点。本文将为您深入解析 Serverless 的概念、优势、应用场景以及实践经验&#xff0c;带您领略 Serverless 的魅力&#xff01; 一、Serverless 是什…

打卡学习kubernetes——了解kubernetes组成及架构

目录 1 什么是kubernetes 2 kubernetes组件 3 kubernetes架构 1 什么是kubernetes kubernetes是一个旨在自动部署、扩展和运行应用容器的开源平台。目标是构建一个生态系统&#xff0c;提供组件和工具以减轻在公共和私有云中运行应用程序的负担。 kubernetes是&#xff1a…

deepin23beta中SQLite3数据库安装与使用

SQLite 是一个嵌入式 SQL 数据库引擎&#xff0c;它实现了一个自包含、无服务器、零配置、事务性 SQL 数据库引擎。 SQLite 的代码属于公共领域&#xff0c;因此可以免费用于任何商业或私人目的。 SQLite 是世界上部署最广泛的数据库&#xff0c;其应用程序数量之多&#xff0c…

Linux使用Docker部署Registry结合内网穿透实现公网远程拉取推送镜像

文章目录 1. 部署Docker Registry2. 本地测试推送镜像3. Linux 安装cpolar4. 配置Docker Registry公网访问地址5. 公网远程推送Docker Registry6. 固定Docker Registry公网地址 Docker Registry 本地镜像仓库,简单几步结合cpolar内网穿透工具实现远程pull or push (拉取和推送)…

VUE 运行NPM 报错:npm ERR! code CERT_HAS_EXPIRED 解决方案

现象 由于各种原因需要调试一下VUE代码&#xff0c;用Git拉下来运行不了&#xff08;之前是可以正常运行的&#xff09;&#xff0c;报错为&#xff1a;npm ERR! code CERT_HAS_EXPIRED........... 原因 NPM 证书签名过期了 解决方法 第一步&#xff1a;CMD 命令 查看NPM代理源…

【C++ RB树】

文章目录 红黑树红黑树的概念红黑树的性质红黑树节点的定义红黑树的插入代码实现总结 红黑树 AVL树是一颗绝对平衡的二叉搜索树&#xff0c;要求每个节点的左右高度差的绝对值不超过1&#xff0c;这样保证查询时的高效时间复杂度O( l o g 2 N ) log_2 N) log2​N)&#xff0c;…

MySQL锁整理

MySQL锁信息来源 MySQL锁太多&#xff0c;内容太杂。写篇文章记录一下