应用层协议——TCP(上)

在这里插入图片描述

文章目录

  • 1. TCP协议
    • 1.1 TCP协议段格式
    • 1.2 确认应答(ACK)机制
    • 1.3 16位窗口大小
    • 1.4 6位标志位
      • 1.4.1 TCP三次握手
    • 1.5 确认应答(ACK)机制
    • 1.6 超时重传机制
    • 1.7 连接管理机制
      • 1.7.1 理解TIME_WAIT状态
      • 1.7.2 理解 CLOSE_WAIT 状态

1. TCP协议

TCP全称为传输控制协议,意思是要对数据的传输进行一个详细的控制

1.1 TCP协议段格式

在这里插入图片描述
TCP的报头是20个字节,一共5行,每行4个字节。这是标准长度,如果报头中含有选项,那么报头的长度就是变长的。

那么我们该如何去解包呢
因为TCP的报头是变长的,所以我们首先要看的是4位首部长度。它的意思是除数据外其它的总长度。因为是4个bit位,所以最大是1111,并且它的单位是4字节,所以最大长度是4*15=60字节,也就是说TCP报头最大60字节。而我们得知标准长度是20个字节,那么4位首部长度是5,也就是0101,它的取值范围就是0101~1111。

当我们要解包时,先提取20字节,在这20字节找到4位首部长度,假如是30,那么就30-20=10得出选项的大小,再把选项提取出来。

1.2 确认应答(ACK)机制

首先,我们要谈一个问题:可靠性。

那么什么是不可靠的呢
丢包、乱序、校验失败等等

怎么确定一个报文是丢了还是没丢
在这里插入图片描述
当我们给对方发送消息的时候,怎么确定对方有没有收到呢?如果对方给我们应答吃了,吃的是炸鸡。就说明对方收到了我发送的消息。
在这里插入图片描述
那么对方又怎么确定我有没有收到呢?我们再给对方回应。
在这里插入图片描述
可能大家此时就会发现一个结论:在长距离交互的时候,永远有一条最新的数据是没有应答的

如果发送的消息有对应的应答,就一定没有丢,否则是不确定的

还有一个问题:当我们传多个报文时,怎么确定哪个回应对应哪个报头呢
这里我们就需要使用32位序号和32位确认序号
当我们发送数据的时候,在32位序号中添加数字,在响应数据时,在32位确认序号中添加。

32位确认序号代表的是:对方前面已经发送完整报文的数据量
举个例子:比如客户端给服务器发送了1,2,3,5,6这几个报文,那么我们的确认序号就应该是4,说明4前面的序号报文都接受成功了,应该再从4开始重新发送。

那么为什么需要两种序号呢
因为TCP协议是全双工的,我在给你发消息的同时,我也可以收消息。如果客户端想给你发消息的同时再接受消息呢?反之服务端也是如此

所以,客户端根据它的序号和对方的确认序号保证客户端发送数据的可靠性,服务端根据它的序号和客户端的确认序号保证服务器发送的数据可靠性

总而言之,序号是让对方确认的,确认序号是对方让我确认的。

1.3 16位窗口大小

首先,我们要知道:TCP协议是具有发送缓冲区和接受缓冲区的。
在这里插入图片描述
我们在调用write,read等系统接口时,其实就是把数据拷贝到缓冲区,或者从缓冲区拷贝数据到应用层。

从进程地址空间看,就是从内核空间拷贝到用户空间的栈或者堆上。

当互相通信的时候,TCP的过程如下图所示:
在这里插入图片描述
每个发送缓冲区和接受缓冲区都是一对,所以TCP通信的时候是全双工的。

我们在应用层用系统调用接口,把数据拷贝到内核缓冲区里,所以,数据什么时候发,发多少,出错了怎么办,要不要添加提高效率的策略,都是由OS内的TCP自主决定的。所以叫做传输控制协议。

既然是缓冲区,就说明缓冲区是有大小的。那么如果发送数据太快,缓冲区来不及接受,或者发送的太慢,效率太低。这些问题该怎么办呢
为了解决这个问题,我们需要让客户端知道服务器的接受能力,它的接受能力就是接受缓冲区剩余空间的大小。

那么客户端怎么知道服务器接受能力的大小
发送都会有应答,应答里包含TCP报头,报头里有16位窗口大小来表示接受能力的属性字段。我们就可以根据对方的窗口大小来设置发送速度,这种策略叫做流量控制。

如果我们是第一次发送消息,我们怎么确定对方的接受能力呢
因为是16位,最大是64K,所以我们不能超过这个大小。

1.4 6位标志位

在这里插入图片描述
在前面,初步讲解了3次握手和4次挥手:初步了解三次握手

在服务端,收到的报文有的是为了建立链接的,有的是正常传输数据,有的是断开链接。这说明报文是有区别的。

SYN: 请求建立连接,SYN需要被设置成1,我们把携带SYN标识的称为同步报文段
客户端需要给服务端发送一个报头,即使没有有效载荷,我们也要发一个SYN为1的TCP报头过去。

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

SYN和FIN不应该同时设置。

ACK: 确认标记位,表示该报文是对历史报文的确认,一般在大部分正式通信的信号下,ACK都是1
比如:客户端给服务器发送一个报文,服务端给客户端响应返回,这个响应返回中ACK就会设置成1,说明上一条报文发送成功,然后可以提取确认序列号。

PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走
当客户端给服务端发送数据时,服务器是用read来从接受缓冲区里读取的。如果读取条件不满足,read会被阻塞。这是进程主动地去轮询检测的。

当数据准备好的时候,我们不想使用系统默认的读取情况,而是想主动通知服务端立即去读取,我们可以使用PSH标记位。

URG: 紧急指针标记位
我们知道:报文在发送的时候,是可能乱序到达的。
那么如何做到让我们的报文进行按序到达呢
主机每次发送数据时,TCP就给每个数据包分配一个序列号并且在一个特定的时间内等待接收主机对分配的这个序列号进行确认,如果发送主机在一个特定时间内没有收到接收主机的确认,则发送主机会重传此数据包。接收主机利用序列号对接收的数据进行确认,以便检测对方发送的数据是否有丢失或者乱序等,接收主机一旦收到已经顺序化的数据,它就将这些数据按正确的顺序重组成数据流并传递到高层进行处理

数据在TCP中是有序到达的话,但是如果有一些数据优先级更高,但序号较后,我们想紧急处理此数据。我们是按照优先级还是序号呢
我们可以设置TCP中的URG来让优先级更高的数据先处理。

不是所有的数据都是紧急数据,我们该如何找到紧急数据呢
这里就需要根据16位紧急指针的偏移量了。但是只能读取一个字节。

1.4.1 TCP三次握手

在这里插入图片描述
这里的线画的是斜线,原因是考虑到时间的关系。
并且我们要知道:大量的连接,OS就要管理这些连接,就需要先描述,后组织。

下面我们要讲解一下为什么是3次握手:
三次握手,前两次都有应答,第三次没有应答。所以说3次握手不一定成功。

为什么不是一次
如果一次握手成功,那么它非常容易受到攻击。如果一台主机不断的给我们服务器发送SYN链接,服务器需要创建相关数据结构来管理,那么它可能创建假的SYN来把服务器的资源占满,让正常的SYN不能链接。

为什么不是二次
如果客户端将服务器的回复丢弃,那么效果就和一次是一样的了。

为什么是三次
三次握手最后一次是由服务端来确定的,所以最后握手如果没有成功,影响的是客户端不是服务端。最后一次握手,服务器不一定能收到,但是客户端是发送出去了,它需要维护相关数据结构。以奇数次握手来将花费的成本嫁接到客户端。并且客户端发送一次,接受一次,服务器发送一次,接受一次,以最小成本验证全双工。

如果客户端发出最后一次ACK,但是报文丢了,服务端没有收到,那么客户端认为建立成功,服务端认为建立没有成功,下面会是什么情况呢
那么客户端就会给服务端发送数据,如何服务端就会给客户端发出报头,报头里的RST就会设置成1。
RST: 对方要求重新建立连接,我们把携带RST标识的称为复位报文段

1.5 确认应答(ACK)机制

在这里插入图片描述
TCP将每个字节的数据都进行了编号,即为序列号。
在这里插入图片描述
每一个ACK都带有对应的确认序列号,意思是告诉发送者,我已经收到了哪些数据,下一次你从哪里开始发。

1.6 超时重传机制

在这里插入图片描述
主机A发送数据给B之后,可能因为网络拥堵等原因,数据无法到达主机B,如果主机A在一个特定时间间隔内没有收到B发来的确认应答,就会进行重发。
在这里插入图片描述
但是,主机A未收到B发来的确认应答,也可能是因为ACK丢失了。因此主机B会收到很多重复数据,那么TCP协议需要能够识别出那些包是重复的包,并且把重复的丢弃掉。这时候我们可以利用前面提到的序列号,就可以很容易做到去重的效果。

那么丢包要在特定的时间内重传,如果超时的时间如何确定?
最理想的情况下,找到一个最小的时间,保证"确认应答一定能在这个时间内返回"。但是这个时间的长短,随着网络环境的不同,是有差异的。如果超时时间设的太长,会影响整体的重传效率。如果超时时间设的太短, 有可能会频繁发送重复的包。

TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间
Linux中(BSD Unix和Windows也是如此),超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。如果重发一次之后,仍然得不到应答,等待 2 *500ms 后再进行重传。如果仍然得不到应答,等待 4 *500ms 进行重传, 依次类推,以指数形式递增。累计到一定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接。

1.7 连接管理机制

在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接:
在这里插入图片描述
在某些特殊情况,如果客户端和服务端想同时断开连接,那么在第一次ACK时,可以把FIN加上,这样就是3次挥手。

如果我们服务器不accept,3次握手也能成功,accept只是把已经成功建立的连接拿上来

服务端状态转化:
[CLOSED -> LISTEN] 服务器端调用listen后进入LISTEN状态,等待客户端连接。

[LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段),就将该连接放入内核等待队列中,并向客户端发送SYN确认报文。

[SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文,就进入ESTABLISHED状态,可以进行读写数据了。

[ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用close),服务器会收到结束报文段,服务器返回确认报文段并进入CLOSE_WAIT。

[CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据),当服务器真正调用close关闭连接时,会向客户端发送FIN,此时服务器进入LAST_ACK状态,等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)。

[LAST_ACK -> CLOSED] 服务器收到了对FIN的ACK,彻底关闭连接。

客户端状态转化:
[CLOSED -> SYN_SENT] 客户端调用connect,发送同步报文段。

[SYN_SENT -> ESTABLISHED] connect调用成功,则进入ESTABLISHED状态,开始读写数据。

[ESTABLISHED -> FIN_WAIT_1] 客户端主动调用close时,向服务器发送结束报文段,同时进入FIN_WAIT_1。

[FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认,则进入FIN_WAIT_2,开始等待服务器的结束报文段。

[FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段,进入TIME_WAIT,并发出LAST_ACK。

[TIME_WAIT -> CLOSED] 客户端要等待一个2MSL(Max Segment Life, 报文最大生存时间)的时间,才会进入CLOSED状态。

1.7.1 理解TIME_WAIT状态

这里是以客户端为例,如果是服务端也是一样的。谁先断开连接,谁先进入TIME_WAIT状态,当客户端发送最后一次ACK之后,客户端理论上可以关闭连接了,但是需要等一段时间,才会进入CLOSED状态。

为什么TIME_WAIT状态需要等一段时间才会结束
原因是:在第四次挥手时,发送的ACK可能会丢失,那么对方可能会进行FIN重传机制。如果在一段时间内,没有收到对方的FIN,就认为对方收到了ACK。并且,如果当正常数据发送的时候,同时发送FIN,那么等一段时间是为了让历史数据尽可能的被双方收到。

那么TIME_WAIT状态需要等待的时间是多长呢
TCP协议规定:主动关闭连接的一方要处于TIME_ WAIT状态,等待两个MSL的时间后才能回到CLOSED状态。
MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同, 在Centos7上默认配置的值是60s。可以通过 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看msl的值。

为什么是TIME_WAIT的时间是2MSL
MSL是TCP报文的最大生存时间,因此TIME_WAIT持续存在2MSL的话就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启,可能会收到来自上一个进程的迟到的数据,但是这种数据很可能是错误的)。
同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失,那么服务器会再重发一个FIN。这时虽然客户端的进程不在了,但是TCP连接还在,仍然可以重发LAST_ACK)。

如果是服务端先断开连接,进入TIME_WAIT状态后,我们就不能启动服务端了(./server 8080),它的错误码是2(绑定失败)
这是因为虽然server的应用程序终止了,但TCP协议层的连接并没有完全断开,因此不能再次监听同样的server端口。

在server的TCP连接没有完全断开之前不允许重新监听,某些情况下可能是不合理的:
服务器需要处理非常大量的客户端的连接(每个连接的生存时间可能很短,但是每秒都有很大数量的客户端来请求)。这个时候如果由服务器端主动关闭连接(比如某些客户端不活跃,就需要被服务器端主动清理掉),就会产
生大量TIME_WAIT连接。
由于我们的请求量很大,就可能导致TIME_WAIT的连接数很多,每个连接都会占用一个通信五元组(源ip、源端口、目的ip、目的端口、协议)。其中服务器的ip和端口和协议是固定的,如果新来的客户端连接的ip和端口号和TIME_WAIT占用的链接重复了,就会出现问题。

解决TIME_WAIT状态引起的bind失败的方法:使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1,表示允许创建端口号相同但IP地址不同的多个socket描述符
在这里插入图片描述
函数功能:设置套接字描述符的属性

sockfd:要设置的套接字描述符。
level:是被设置的选项的级别,如果想要在套接字级别上设置选项,就必须把level设置为 SOL_SOCKET。
optname:指定准备设置的选项,optname可以有哪些取值,这取决于level。
optval:指向某个变量的指针,该变量是要设置新值的缓冲区。可以是一个结构体,也可以是普通变量。
optlen:optval缓冲区的长度。

代码如下:

		// 1. 创建socketlistenSock_ = socket(PF_INET, SOCK_STREAM, 0);if (listenSock_ < 0){exit(1);}int opt = 1;setsockopt(listenSock_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));// 2. bind绑定// 2.1 填充服务器信息

1.7.2 理解 CLOSE_WAIT 状态

如果客户端断开连接,服务端没有close,那么服务端就会进入CLOSE_WAIT 状态。
小结: 对于服务器上出现大量的 CLOSE_WAIT 状态,原因就是服务器没有正确的关闭 socket,导致四次挥手没有正确完成。这是一个BUG,只需要加上对应的 close 即可解决问题。

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

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

相关文章

〔AI 绘画〕Stable Diffusion 之 VAE 篇

✨ 目录 &#x1f388; 什么是VAE&#x1f388; 开启VAE&#x1f388; 下载常见的VAE&#x1f388; 对比不同VAE生成的效果 &#x1f388; 什么是VAE VAE&#xff1a;是 Variational Auto-Encoder 的简称&#xff0c;也就是变分自动编码器可以把它理解成给图片加滤镜&#xff…

JavaWeb-Filter过滤器

目录 Filter过滤器 1. Filter的生命周期 2.Filter的配置 3.拦截路径 4.拦截具体的使用 5.拦截方式配置&#xff08;资源被访问方式&#xff09; 6.FilterChain拦截链 Filter过滤器 filter是过滤器&#xff0c;相比于Servlet的发送请求&#xff0c;filter是用于拦截请求。…

2023-08-14 linux 串口终端输入长命令不换行,覆盖前面内容,stty命令设置串口终端行列数

一、linux 串口终端输入长命令不换行&#xff0c;覆盖前面内容&#xff0c;现象如下图&#xff1a; 二、解决方法&#xff1a;用stty 命令设置行列数 stty columns 200 stty rows 10三、参考文章 https://www.cnblogs.com/goloving/p/15170537.html 常用Linux串口设备操作命…

【Servlet】(Servlet API HttpServlet 处理请求 HttpServletRequest 打印请求信息 前端给后端传参)

文章目录 Servlet APIHttpServlet处理请求 HttpServletRequest打印请求信息前端给后端传参 Servlet API Servlet中常用的API HttpServlet 实际开发的时候主要重写 doXXX 方法, 很少会重写 init / destory / service destory 服务器终止的时候会调用. //下面的注解把当前类和…

gin的占位符:和通配符*

1、用法 在 Gin 路由中&#xff0c;可以使用一个通配符&#xff08;*&#xff09;或一个占位符&#xff08;:&#xff09;来捕获 URL 的一部分。 r.GET("/royal/:id", func(c *gin.Context) {id : c.Param("id")//fmt.Println("into :id")c.Str…

HBase API

我们之后的实际开发中不可能在服务器那边直接使用shell命令一直敲的&#xff0c;一般都是通过API进行操作的。 环境准备 新建Maven项目&#xff0c;导入Maven依赖 <dependencies><dependency><groupId>org.apache.hbase</groupId><artifactId>…

命令执行漏洞

1、命令执行漏洞 1.1、简介 Django是用Python开发的一个免费开源的Web结构&#xff0c;几乎包括了Web使用方方面面&#xff0c;能够用于快速建立高性能、文雅的网站&#xff0c;Diango提供了许多网站后台开发常常用到的模块&#xff0c;使开发者可以专注于业务部分。 1.2、漏…

【删除vlan的方法】

提示错误 [SW1]undo vlan 10 Error: The VLAN has a L3 interface. Please delete it first. 解决办法 undo interface Vlanif10 #删除vlan 10下的接口 [SW1-GigabitEthernet0/0/1]dis this #删除下列的IP

接口自动化必备技能——jmeter提取token方式以及设置成全局变量(跨线程组传token值)方式

前言 今天Darren洋教大家如何使用jmeter中的插件来进行token值的提取与调用&#xff0c;今天Darren洋介绍两种jmeter提取token值的方式&#xff0c;一种是在当前线程组中直接提取token值&#xff0c;一种是跨线程组的方式进行token值的提取并调用给不同线程组里的HTTP接口使用。…

如何读取文件夹内的诸多文件,并选择性的保留部分文件

目录 问题描述: 问题解决: 问题描述: 当前有一个二级文件夹,第一层是文件夹名称是“Papers(LNAI14302-14304)",第二级文件夹目录名称如下图蓝色部分所示。第三层为存放的文件,如下下图所示,每一个文件中,均存放三个文件,分别为copyright.pdf, submission.pdf, s…

重磅发布!曙光存储“3+N”,绿色存力新选择

8月9-10日&#xff0c;2023年数据中心市场年会在京举办。会上&#xff0c;中科曙光存储产品事业部总监石静发表《绿色存力 打通绿色数据中心最后一站》主题演讲。“在今天&#xff0c;数据中心正在成为‘高能耗’产业&#xff0c;绿色节能从可选项走向必选项。曙光存储跨越绿色…

开发测试框架一 - 创建springboot工程及基础操作

一、创建及运行方式 1. 从官网导入&#xff1a; 注意&#xff1a;由于我的java版本是1.8&#xff1b;所以选中了spring2.7.14&#xff1b;如果你的java版本是9及以上&#xff0c;选中spring3相关的同时Java 版本也要对应起来 2. 创建第一个get请求 创建Controller package及…

mysql滑动窗口案例

获取学科最高分 SELECT DISTINCT name,subject,MAX(score) OVER (PARTITION by subject) as 此学科最高分数 from scores;获取学科的报名人数 select DISTINCT subject,count(name) over (partition by subject) as 报名此学科的人数 from scores; 求学科总分 SELECT DISTI…

Spring Boot + Vue3前后端分离实战wiki知识库系统十二--用户管理单点登录开发一...

目标&#xff1a; 在上一次https://www.cnblogs.com/webor2006/p/17533745.html我们已经完成了文档管理的功能模块开发&#xff0c;接下来则开启新模块的学习---用户登录&#xff0c;这块还是有不少知识点值得学习的&#xff0c;先来看一下整体的效果&#xff0c;关于效果官网有…

YOLOv8目标检测算法

YOLOv8目标检测算法相较于前几代YOLO系列算法具有如下的几点优势&#xff1a; 更友好的安装/运行方式速度更快、准确率更高新的backbone&#xff0c;将YOLOv5中的C3更换为C2FYOLO系列第一次尝试使用anchor-free新的损失函数 YOLOv8简介 YOLOv8 是 Ultralytics 公司继 YOLOv5…

FiboSearch Pro – Ajax Search for WooCommerce 商城AJAX实时搜索插件

FiboSearch Pro是最受欢迎的WooCommerce 产品搜索插件。它为您的用户提供精心设计的高级 AJAX 搜索栏&#xff0c;并提供实时搜索建议。默认情况下&#xff0c;WooCommerce 提供非常简单的搜索解决方案&#xff0c;没有实时产品搜索&#xff0c;甚至没有 SKU 搜索。FiboSearch&…

uniapp开发微信小程序底部地区选择弹框

个人项目地址&#xff1a; SubTopH前端开发个人站 &#xff08;自己开发的前端功能和UI组件&#xff0c;一些有趣的小功能&#xff0c;感兴趣的伙伴可以访问&#xff0c;欢迎提出更好的想法&#xff0c;私信沟通&#xff0c;网站属于静态页面&#xff09; SubTopH前端开发个人站…

React Native 图片组件基础知识

在 React Native 中使用图片其实跟 HTML 中使用图片一样简单&#xff0c;在 React Native 中我们使用Image组件来呈现图片的内容&#xff0c;其中主要的属性有&#xff1a;source。这个属性主要是设置图片的内容&#xff0c;它可以是网络图像地址、静态资源、临时本地图像以及本…

Android侧滑栏(一)可缩放可一起移动的侧滑栏

在实际的各类App开发中&#xff0c;经常会需要做一个左侧的侧滑栏&#xff0c;类似于QQ这种。 今天这篇文章总结下自己在开发中遇到的这类可以跟随移动且可以缩放的侧滑栏。 一、实现原理 使用 HorizontalScrollView 实现一个水平方向的可滑动的View&#xff0c;左布局为侧滑…

Camx--概述

该部分代码主要位于 vendor/qcom/proprietary/ 目录下&#xff1a; 其中 camx 代表了通用功能性接口的代码实现集合&#xff08;CamX&#xff09;&#xff0c;chi-cdk代表了可定制化需求的代码实现集合&#xff08;CHI&#xff09;&#xff0c;从图中可以看出Camx部分对上作为H…