应用层协议——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.rhkb.cn/news/91368.html

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

相关文章

基于C#的无边框窗体阴影绘制方案 - 开源研究系列文章

今天介绍无边框窗体阴影绘制的内容。 上次有介绍使用双窗体的方法来显示阴影&#xff0c;这次介绍使用API函数来进行绘制。这里使用的是Windows API函数&#xff0c;操作系统的窗体也是用的这个来进行的绘制。 1、 项目目录&#xff1b; 下面是项目目录&#xff1b; 2、 函数介…

Kubernetes网络模型

Kubernetes 用来在集群上运行分布式系统。分布式系统的本质使得网络组件在 Kubernetes 中是至关重要也不可或缺的。理解 Kubernetes 的网络模型可以帮助你更好的在 Kubernetes 上运行、监控、诊断你的应用程序。 网络是一个很宽泛的领域&#xff0c;其中有许多成熟的技术。对于…

剑指offer-2.1数组

数组 数组可以说是最简单的一种数据结构&#xff0c;它占据一块连续的内存并按照顺序存储数据。创建数组时&#xff0c;我们需要首先指定数组的容量大小&#xff0c;然后根据大小分配内存。即使我们只在数组中存储一个数字&#xff0c;也需要为所有的数据预先分配内存。因此数…

C++ 之动态链接库DLL使用注意事项及C#调用详解

C 之动态链接库DLL使用注意事项及C#调用详解 有时候算法开发完成之后需要封装成动态链接库DLL来进行集成&#xff0c;一方面增加了算法or代码的复用或者广泛使用性&#xff0c;另一方面也起了保密的效果平时封装成DLL之后放到一台新的电脑上会出现问题&#xff0c;所以本文总结…

初识结构体

文章目录 目录1. 结构体类型的声明1.1 结构的基础知识1.2 结构的声明1.3 结构成员的类型1.4 结构体变量的定义和初始化 2. 结构体成员的访问3. 结构体传参 目录 结构体类型的声明结构体初始化结构体成员访问结构体传参 1. 结构体类型的声明 1.1 结构的基础知识 结构是一些值的…

发布属于自己的 npm 包

1 创建文件夹&#xff0c;并创建 index.js 在文件中声明函数&#xff0c;使用module.exports 导出 2 npm 初始化工具包&#xff0c;package.json 填写包的信息&#xff08;包的名字是唯一的&#xff09; npm init 可在这里写包的名字&#xff0c;或者一路按回车&#xff0c;后…

Postgresql 基础使用语法

1.数据类型 1.数字类型 类型 长度 说明 范围 与其他db比较 Smallint 2字节 小范围整数类型 32768到32767 integer 4字节 整数类型 2147483648到2147483647 bigint 8字节 大范围整数类型 -9233203685477808到9223203685477807 decimal 可变 用户指定 精度小…

【Linux初阶】system V消息队列 + system V信号量

文章目录 一、system V消息队列&#xff08;了解&#xff09;二、system V信号量&#xff08;了解&#xff09;1.信号量是什么2.临界资源和临界区3.互斥4.为什么要信号量 三、IPC资源的组织方式结语 一、system V消息队列&#xff08;了解&#xff09; 消息队列提供了一个从一…

MongoDB 简介

什么是MongoDB ? MongoDB 是由C语言编写的&#xff0c;是一个基于分布式文件存储的开源数据库系统。 在高负载的情况下&#xff0c;添加更多的节点&#xff0c;可以保证服务器性能。 MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。 MongoDB 将数据存储为一个…

4、Rocketmq之存储原理

CommitLog ~ MappedFileQueue ~ MappedFile集合 正常情况下&#xff0c;RocketMQ支持消息体字节数最多为1个G。注意该消息体并不单单是消息体body。如果生产的消息其字节数超过1个G则该消息是无法被落盘处理的。因为没有一个MapperFile文件可以承载该消息所有的字节数。 1.All…

linux学习(自写shell)[11]

打印出提示信息获取用户键盘输入 cmd_line[NUM];用来保存完整的命令行 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/wait.h>#define NUM 1024 char cmd_line[NUM]; //shell int main() {wh…

Kali Linux是什么?它的主要用途是什么?

1. Kali Linux是什么&#xff1f; Kali Linux是一款基于Debian Linux的发行版&#xff0c;专注于网络安全和渗透测试。它由全球顶尖的安全专家和黑客社区维护开发&#xff0c;提供了丰富的工具和资源&#xff0c;用于测试网络、系统和应用程序的安全性。Kali Linux以其强大的功…

C#字符串占位符替换

using System;namespace myprog {class test{static void Main(string[] args){string str1 string.Format("{0}今年{1}岁&#xff0c;身高{2}cm&#xff0c;月收入{3}元&#xff1b;", "小李", 23, 177, 5000);Console.WriteLine(str1);Console.ReadKey(…

聊一下互联网开源变现

(点击即可收听) 互联网开源变现其实是指通过开源软件或者开放源代码的方式&#xff0c;实现收益或盈利。这种方式越来越被广泛应用于互联网行业 在互联网开源变现的模式中&#xff0c;最常见的方式是通过捐款、广告、付费支持或者授权等方式获利。 例如&#xff0c;有些开源软件…

轻量级自动化测试框架WebZ

一、什么是WebZ WebZ是我用Python写的“关键字驱动”的自动化测试框架&#xff0c;基于WebDriver。 设计该框架的初衷是&#xff1a;用自动化测试让测试人员从一些简单却重复的测试中解放出来。之所以用“关键字驱动”模式是因为我觉得这样能让测试人员&#xff08;测试执行人员…

MySQL — 索引

文章目录 索引索引结构 — B树与B树B树B树 聚簇索引与非聚簇索引聚簇索引非聚簇索引优缺点 覆盖索引与回表联合索引索引覆盖最左前缀匹配 索引 索引是对数据库表中一列或多列的值进行排序的一种结构。MySQL索引的建立对于MySQL的高效运行是很重要的&#xff0c;索引可以大大提…

剑指 Offer 40. 最小的k个数

题目描述 输入整数数组 arr &#xff0c;找出其中最小的 k 个数。例如&#xff0c;输入4、5、1、6、2、7、3、8这8个数字&#xff0c;则最小的4个数字是1、2、3、4。 示例 思路 方法1 采用未改进的快速排序 class Solution {public int[] getLeastNumbers(int[] arr, int k…

k8s ------存储卷(PV、PVC)

目录 一&#xff1a;为什么需要存储卷&#xff1f; 二&#xff1a;emptyDir存储卷 ​三&#xff1a;hostPath存储卷 四&#xff1a;nfs共享存储卷 五&#xff1a;PVC 和 PV 1、PVC 和 PV介绍 2、PV和PVC之间的相互作用遵循的生命周期 3、PV 的4 种状态 4、一个PV从创…

【Spring MVC】Spring MVC基于注解的程序开发

目录 一、什么是Spring MVC 二、Spring MVC项目的创建和使用 1、实现客户端和服务器端之间的连接 1.1、RequsestMapping注解 1.2、RequestMapper的简单使用 1.3、使用GetMapping和POSTMapping注解来实现HTTP连接 三、获取参数 1、实现获取单个参数 2、实现获取对象 3…