传输层协议 —— TCP协议(上篇)

目录

1.认识TCP

2.TCP协议段格式

3.可靠性保证的机制

确认应答机制

超时重传机制

连接管理机制

三次握手

四次挥手


1.认识TCP

在网络通信模型中,传输层有两个经典的协议,分别是UDP协议和TCP协议。其中TCP协议全称为传输控制协议(Transmission Control Protocol),从名称就可以看出,TCP协议需要对数据的传输进行严格的控制。

UDP协议具有无连接、不可靠、面向数据报的特点,而TCP协议恰恰相反,具有 有连接、可靠、面向字节流的特点。而其中,可靠性是TCP最著名的特点;也正因为TCP协议需要保证通信的可靠性,所以TCP协议才会有一系列保证可靠性的机制和策略,这也是我们需要重点学习的内容。

2.TCP协议段格式

所谓协议,其实就是通信双方都认识的结构化的数据。TCP协议是传输层的协议,传输层的协议是在操作系统内部实现的,所以操作系统内部一定有TCP协议相关的代码。

Linux内核中TCP协议部分代码:

把代码形象化便得到了下面这张图:

  

各个字段的粗略认识:

1、16位源端口和16位目的端口:表明数据从哪个进程来,要发送给哪个进程。

2、32位序号和32位确认序号:序号可以用来对接收到的报文进行按序到达去重,确认序号表明该序号之前的报文都收到了。(后面会详谈)

3、4位首部长度:表明TCP报头的长度。TCP报头由固定长度的20字节和不固定的选项构成,四位首部长度表明了这两部分共同的长度。其中,首部长度是有基本单位的,基本单位是4字节。4个比特位的最大取值是15,所以四位首部长度的最大范围是60字节。

4、6位标记位:

URG: 表明紧急指针是否有效

ACK: 表明确认号是否有效

PSH: 提示接收端应用程序立刻从 TCP 缓冲区把数据读走

RST: 对方要求重新建立连接; 我们把携带 RST 标识的称为复位报文段

SYN: 请求建立连接; 我们把携带 SYN 标识的称为同步报文段

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

5、16 位窗口大小: 表明自己的接收能力,通信双方可以动态的调整发送报文的大小。

6、16 位校验和: 发送端填充, CRC 校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含 TCP 首部, 也包含 TCP 数据部分.

7、16 位紧急指针: 标识哪部分数据是紧急数据,需要优先处理。

8、40 字节头部选项: 暂时忽略;

3.可靠性保证的机制

确认应答机制

铺垫:什么是序列号?

我们可以这样理解。操作系统会为TCP分配两个进行通信的缓冲区,我们把缓冲区当成char类型的大数组,那么缓冲区中的数据不就天然的具有编号了吗?这个编号我们把它叫做序列号。序列号是对每个字节的编号,这也体现出了TCP面向字节流的特点。

TCP需要保证可靠性,首先需要保证发送方发送的数据,接收方要能收到。那发送方如何得知接收方是否收到了自己发送的数据呢?这个时候,TCP协议便引入了确认应答机制。

确认应答机制就是接收方对接收到的报文进行应答,这样一来,发送方就知道对方有没有收到我发送的数据了。并且这个应答是需要在指定的时间内收到的,如果主动发送数据的一方没有在规定时间内收到应答,那么它就会认为对方没有收到我发送的数据。

但是问题又来了,发送方发送一次消息,接收方应答一下,这样似乎没有什么问题,但是,这种通信模式的效率是非常低的,所以,通信双方进行通信的时候,数据的发送往往是并行的。

那么问题又来了,接收方收到多个报文的时候,如何保证能够正确的进行应答呢? 其实啊,应答的时候,是通过序列号来完成的。序列号是能够确认顺序的,接收方会对收到的报文根据序列号进行排序,一旦排好序之后,接收方就会先判断最小序列号之前的报文是否全部收到,如果该序列号之前的报文全部收到,才会进行应答,并且应答的时候是需要填充32位确认序号的,确认序号的值是收到的报文的序列号+1,表明该序列号之前的数据我全部收到了,下次你应该从哪里发。这其实这就是按序到达策略。

如果收到了对方的确认序号,下一次发送数据的序号就是 收到的确认序号+要发送报文的长度。 

我们已经知道了确认应答机制是通过序列号来完成的,那你有没有这样的疑问,为什么报头中有序列号了,还需要有一个确认序号呢?直接用序列号的字段来表明确认序号不就可以了吗?

这是因为,在实际通信的过程中,接收方往往也需要向发送方响应消息。 也就是说数据的接收方既要响应,又要发送消息;那么这个过程可不可以一步到位呢?这是可以的,序列号表明自己发送的报文,确认序号可以作为接收消息的响应。那么,这样一来,中间的两次发送可以合并为一次发送,这样,不就又提高了通信的效率了吗?这其实就是捎带应答

超时重传机制

前面我们已经知道了TCP可靠性保证的一个机制 —— 确认应答机制,接收方需要向发送方进行响应,表明自己收到了对方的消息。但是,如果发送方一直没有收到对方的响应,会怎么办呢?这就需要引入超时重传机制了。

发送方发送数据之后,会等待一段时间,在该时间内,如果收到了对方的响应,就表明对方收到了我发送的数据;如果在该时间段内,没有收到对方的应答,发送方就会认为对方没有收到自己发送的数据,这个时候就需要再发送一遍,这就是超时重传机制。

超时重传的时间怎么定呢?

数据是需要通过网络进行发送的,如果网络状态比较好的话,数据发送的速度就会比较快,如果网络比较差的话,数据发送的速度就会比较慢。也就是说,网络的状态是动态变化的,那么超时时间的设置也必须是动态变化的,如果网络状态比较好的话,超时时间就可以设置的短一点,如果网络状态比较差的话,超时时间就设置的长一点。

• Linux 中, 超时时间以 500ms 为一个单位进行控制, 每次判定超时重发的超时时间都是500ms 的整数倍.
• 如果重发一次之后, 仍然得不到应答, 等待 2 * 500ms 后再进行重传.
• 如果仍然得不到应答, 等待 4 * 500ms 进行重传. 依次类推, 以指数形式递增.
• 累计到一定的重传次数, TCP 认为网络或者对端主机出现异常, 强制关闭连接.

超时重传机制存在的问题

由于超时重传机制的存在,在规定时间内没有收到应答就会进行重传。那有没有可能,发送方发送的数据在网络中阻塞了一段时间,但是在一段时间后被对方收到,但是这个时候已经重传了?还有没有可能,对方已经收到了报文,但是响应丢了呢?不管是那种情况,都会导致,发送方重复发送对方已经收到的报文,那么接收方就会收到重复的报文,这个时候怎么办呢?

不要忘了TCP协议报头中有序列号,序列号不仅仅可以用来做为应答,还可以用来去重。当接收方接收到消息的时候,它会根据序列号判断这个报文我曾经是否收到过,如果收到过的话,就直接将该报文丢弃了。所以我们不用担心重复报文的问题。

接收方如何判断这个报文我曾经是否收到过呢?

这个问题更具体的解决策略就是,接收方根据自己最新一次的确认序号就能知道多少号报文之前的报文我都收到了,如果对方发过来的报文的序号小于最新一次的确认序号,那么该报文就能丢弃了,也就实现了去重。

连接管理机制

TCP协议是面向连接的协议,通信之前,通信双方必须进行三次握手建立连接,通信之后,通信双方必须进行四次挥手断开连接。

三次握手

使用TCP协议进行通信的时候,通信双方必须建立连接才能进行正常的通信,当通信结束时,通信双方也必须断开连接以确保不会造成服务器端的资源浪费。所以在基于TCP通信的过程中,会有各种各样的报文,有的报文是用来请求建立连接的,有的报文是用来进行正常通信的,有的报文是用来请求断开连接的。为了区分这些不同的报文,于是,TCP协议报头中引入了标记位

TCP协议中与连接管理有关的标记位:

SYN:SYN标记位也称为同步标记位。如果客户端发送的报文中的SYN标记位被置为1,服务器端就知道对方想与我建立连接了。

FIN:FIN标记位也称为结束标记位。如果客户端发送的报文中的FIN标记位被置为1,服务器端就知道对方想与我断开连接了。

ACK:ACK标记位我们可以称其为应答标记位。用于表明该应答中的确认序号是否有效,也就是表明该报文是否是用于应答的报文。

RST:重置标记位。要求对方重新建立连接。

三次握手过程中套接字的状态变化: 

1.双方未建立连接的时候,双方的套接字都处于CLOSED状态。

2.服务器端需要先调用listen接口将自己的套接字状态设为LISTEN状态,等待客户端连接。

3.此时,客户端需要主动调用connect接口向服务器发起连接,此时客户端套接字状态变为SYN_SENT状态。

4.当服务器端监听到连接请求(SYN报文), 就将该连接放入内核等待队列中, 如果它也想与对方建立连接,就会在发送的报文中将SYN和ACK标记位置1,当该报文发送出去的时候,客户端的套接字状态进入SYN_RCVD状态。

5.当客户端收到服务器端的应答的时候,他就认为连接建立好了,客户端的套接字状态进入ESTABLISHED状态,并向服务器端发送一个ACK报文,表明我也愿意与你通信。

6.当服务器端收到这个ACK报文的时候,服务器端也认为连接建立好了,服务器端的套接字状态就进入ESTABLISHED状态。

此时,通信双方都认为连接建立好了;在这个过程中,客户端通过connect函数发起连接,服务器端的accept函数并不参与三次握手。

一个问题:

在这个过程中,我们发现,客户端认为连接建立好的时间是早于服务器端的。如果客户端发送信息的需求非常迫切,一旦认为连接建立好了就要发送消息,但是此时服务器端的连接还没有建立好呢?

这个时候,当服务器端收到客户端的正常的通信报文的时候,就会向客户端响应一个RST标记位被置为1的报文,要求对方重新建立连接。也就是重新进行三次握手。

那你有没有思考过一个问题,为什么建立连接之前要进行三次握手呢?

1.双方要进行通信,首先要确保通信的信道是健康的。三次握手的工程中,客户端发送的数据,被服务器端接收到之后,服务器端要对客户端进行响应,如果客户端也收到了服务器端的应答,说明客户端是能够进行收发的;同理,客户端也要对服务器端进行响应,如果服务器端收到了客户端的应答,说明服务器端也是能够进行收发的。

2.通信双方都能进行数据的收发还不够,还需要检查对方是否愿意和自己通信。三次握手的过程中,都有一次给对方的响应,说明对方是愿意和自己进行通信的。

此时,通信双方都能够进行数据的收发,并且,对方也愿意与自己进行通信,那么此时就可以建立连接进行通信了。

三次握手的本质:

在三次握手的过程中,服务器是提供服务的一方,当有客户来请求建立连接的时候,服务器肯定是愿意的,并且也要询问对方是否愿意和自己建立连接,再者,两次报文中并不涉及数据,只是涉及报头中标记位的变化,所以,两次报文可以进行捎带应答,合并成一个报文,这才有了三次握手。也就是说,三次握手的本质也是四次握手,只不过中间的两次被捎带应答,合二为一了。

谈完三次握手建立连接,我们现在谈谈四次挥手断开连接。

四次挥手

四次挥手过程中套接字状态的变化:

我们假如客户端主动请求断开连接。

1.客户端主动调用 close 时, 向服务器发送结束报文段, 同时进入 FIN_WAIT_1;

2.当客户端主动关闭连接(调用 close), 服务器会收到结束报文段, 服务器返回确认报文段并进入 CLOSE_WAIT;

3. 客户端收到服务器对结束报文段的确认, 则进入 FIN_WAIT_2, 开始等待服务器的结束报文段;

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

5.客户端收到服务器发来的结束报文段, 进入TIME_WAIT, 并发出 ACK;客户端要等待一个 2MSL(Max Segment Life, 报文最大生存时间)的时间, 才会进入CLOSED 状态.

6.服务器收到了对 FIN 的 ACK, 彻底关闭连接

理解CLOSE_WAIT状态和TIME_WAIT状态:

当客户端主动请求断开连接的时候,说明客户端要发送的数据发送完了,但是服务器端要发送的数据不一定发送完了,所以服务器端不能立即调用close函数断开连接,而是进入CLOSE_WAIT状态,直到将要发送的数据发送完之后,才向客户端发送请求断开连接的报文,也就是说进入CLOSE_WAIT状态的一方不会立即关闭文件描述符。所以,如果我们发现我们的服务器上有大量的CLOSE_WAIT状态,很有可能是服务器端没有关闭文件描述符。

如果客户端是主动断开连接的一方,当客户端收到来自服务器端的断开连接的请求报文的时候,就会进入TIME_WAIT状态,处于TIME_WAIT状态的一方不会立即断开连接,而是需要进行一段时间的等待。这是因为网络中可能还有历史报文,如果连接关闭之后,立马又来了相同的连接,那么历史报文就会对新的连接发送的报文造成影响,等待一段时间可以让历史报文消散。当然,还有一个原因。如果服务器端没有收到客户端发送的最后一个ACK报文,服务器端可以要求客户端进行超时重传,此时连接还在,是可以进行超时重传的,也就保证最后一个报文可靠到达。

进入TIME_WAIT状态的一方需要等待的时长是两个MSL(maximum segment lifetime)时间,MSL时间并不是指数据从发送到接收所花费的时间,而是数据在网络中的最大存活时间。

等待两个MSL时间,是因为客户端发送最后一个ACK需要消耗一个MSL时间,如果服务器端要求客户端进行重传,客户端接收消息也需要消耗一个ACK时间。

和三次握手一样,我们来思考一下,为什么断开连接要进行四次挥手呢?

和三次握手一样,断开连接也需要表明通信双方的意愿,这个过程需要双方进行至少一次的互问互答来完成,当双方都发起断开连接的请求之后,并且也都收到了对方肯定的回答,那么这个时候就可以断开连接了。

四次挥手的过程和三次握手的过程挺像的,那中间的两个报文能否合并成一个报文呢?

通信双方断开连接的时候,必须保证待发送的数据都已经发送完了。假如客户端发起断开连接的请求,客户端是知道自己没有数据再要发送了,也就不会再向服务器发送消息了(这里的消息主要是数据,不包括协议报头),所以才会要求断开连接。但是服务器端不一定将待发送的数据都发送完了,服务器必须保证待发送的数据发送完之后才能断开连接。也就是说客户端发起断开连接请求的时候,如果服务器没有需要发送的数据了,那么此时是可以将中间的两个报文合二为一的,但是这种情况的概率非常小。所以断开连接的时候通常是四次挥手。

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

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

相关文章

torch.embedding 报错 IndexError: index out of range in self

文章目录 1. 报错2. 原因3. 解决方法 1. 报错 torch.embedding 报错: IndexError: index out of range in self2. 原因 首先看下正常情况: import torch import torch.nn.functional as Finputs torch.tensor([[1, 2, 4, 5], [4, 3, 2, 9]]) embedd…

游戏如何检测加速外挂

在游戏面临的众多外挂风险中,除了常见的内存修改挂、注入挂等作弊手段,黑灰产还常用「加速」手段实现作弊。 游戏安全风险分布占比图 「加速」顾名思义是指改变游戏内的速度。游戏在运行中需要以帧为单位播放画面,而计算每帧动画播放所需时间…

代码随想录算法训练营第3天|链表理论基础、203. 移除链表元素、 707.设计链表、 206.反转链表

目录 链表理论基础203. 移除链表元素1、题目描述2、思路3、code4、复杂度分析 707. 设计链表1、题目描述2、思路3、code 206. 反转链表1、题目描述2、思路3、code4、复杂度分析 链表理论基础 ❤️链表增删的时间复杂度都是 O ( 1 ) O(1) O(1),适合动态增删&#xf…

C语言进阶【4】---数据在内存中的存储【1】(你不想知道数据是怎样存储的吗?)

本章概述 整数在内存中的存储大小端字节序和字节序判断练习1练习2练习3练习4练习5练习6 彩蛋时刻!!! 整数在内存中的存储 回忆知识:在讲操作符的那章节中,对于整数而言咱们讲过原码,反码和补码。整数分为有…

【初阶数据结构】一文讲清楚 “堆” 和 “堆排序” -- 树和二叉树(二)(内含TOP-K问题)

文章目录 前言1. 堆1.1 堆的概念1.2 堆的分类 2. 堆的实现2.1 堆的结构体设置2.2 堆的初始化2.3 堆的销毁2.4 添加数据到堆2.4.1 "向上调整"算法 2.5 从堆中删除数据2.5.1 “向下调整”算法 2.6 堆的其它各种方法接口函数 3. 堆排序3.1 堆排序的代码实现 4. TOP-K问题…

CWFED:自然灾害检测数据集(猫脸码客 第192期)

Cyclone Wildfire Flood Earthquake Database 在自然灾害频发的今天,准确、及时地获取并分析相关数据对于灾害预防、预警及响应至关重要。为此,Cyclone Wildfire Flood Earthquake Database(以下简称CWFE Database)应运而生&…

PostgreSQL 的log_hostname 参数测试

PostgreSQL 的log_hostname 参数测试 log_hostname 是 PostgreSQL 配置文件 (postgresql.conf) 中的一个参数,用于控制是否在日志条目中记录客户端主机名。默认情况下,PostgreSQL 只记录客户端的IP地址,而 log_hostname 参数允许数据库管理员…

使用FLBOOK快速制作3D电子版翻页产品册

​随着数字化时代的到来,传统纸质产品册已逐渐无法满足人们快节奏、便捷的生活方式。而FLBOOK,一款强大的3D电子版翻页产品册制作工具,凭借其简洁的操作界面、丰富的功能和出色的展示效果,已成为越来越多企业的首选。 1.要制作电子…

1:java的介绍与基础1:变量,数据类型与数学运算符

1.1Java的开始 从今天开始,我将更新一下关于学习Java的笔记,文章,希望大家支持。这个Java吧,感觉本质上逻辑始于python很类似,但是吧它的表达更加繁琐难懂,所以我还是喜欢python,比较简介明了。…

获取java jdk包的方式记录

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、OpenLogic方式二、华为源下载 前言 记录一下获取java jdk的方式方法。 一、OpenLogic方式 网址:https://www.openlogic.com/openjdk-download…

OCR两篇革命之作

DocOwl2 参考 阿里8B模型拿下多页文档理解新SOTA,324个视觉token表示一页,缩减80% mPLUG-DocOwl 2聚焦多页文档理解,兼顾效果和效率,在大幅缩减单页视觉token的前提下实现了多页文档理解的SOTA效果。 仅用324个token表示文档图…

相亲交易系统源码详解与开发指南

随着互联网技术的发展,越来越多的传统行业开始寻求线上转型,其中就包括婚恋服务。传统的相亲方式已经不能满足现代人快节奏的生活需求,因此,开发一款基于Web的相亲交易系统显得尤为重要开发者h17711347205。本文将详细介绍如何使用…

API接口在不同编程语言中是如何实现的?

API接口是现代软件开发中的关键技术,它允许不同的软件系统相互通信和交换数据。在不同的编程语言中,API接口的实现方式可能会有所不同,但它们的核心概念是一致的:提供一组预定义的方法和协议,使得开发者可以访问特定的…

SpringCloud~

帮你轻松入门SpringCloud~ 1 微服务概述 1.1什么是微服务 如idea中使用maven建立的一个个moudle,它具体是使用SpringBoot开发的一个小模块,专业的事交给专业的模块来做,每个模块完成一个具体的任务或功能。 1.2 什么是微服务架构 它将单一应用…

SAP B1 流程实操 - 营销单据销售部分(上)

背景 在 SAP B1 中,最重要的模块就是【销售】,企业可能不涉及生产和库存(贸易公司),甚至不涉及采购(服务业),但是一定会有基本的 销售。本文中我们讲解 销售 模块的基本核心&#x…

持续低迷的大环境下,写给技术人几句掏心窝的话

文章目录 一、写在前面二、职业发展:兴趣是关键点三、关于职业规划四、做事认真,提升效率五、不要怕事,多经历总是好的六、走技术还是走管理七、关于跳槽八、认识个人与团队的关系,并且学会自我管理九、做好知识归档、写好文档十、…

ORA-28032 Your password has expired and the database is set to read only

做个记录。 non-cdb 处于只读状态,CDB创建到noncdb的dblink后产生的报错,dblink可以成功创建,但无法连接到non-cdb。 解决:一开始以为是cdb的密码不正确,mos上找到问题,non-cdb的密码过期了,并且…

卷积神经网络(Convolutional Neural Network,CNN)

CNN网络主要有三部分构成:卷积层、池化层和全连接层构成,其中卷积层负责提取图像中的局部特征;池化层用来大幅降低参数量级(降维);全连接层类似神经网络的部分,用来输出想要的结果。 卷积思想 卷积Convolution&#x…

对人像图添加指定光源,再进行二次扩图

在一些业务场景中,需要对人像图片添加特定光源,来增加氛围感,例如赛博朋克科技、海边夕阳余晖、以及红蓝相间的高冷;但实现这个功能的难点是:如何将光源与原图片融合,在图片上产生正常光的镜面反射&#xf…

【已解决】Chrome浏览器被2024年新版流氓软件劫持,总会自动打开hao.360.com和so.com主页

最近我家里电脑的 Chrome 浏览器每次启动时都会自动打开 hao.360.com (有时是 www.so.com)主页。此时在浏览器地址栏手动输入 chrome://version ,可见命令行被强制加上一个 360 链接: 我在网上找解决方法,看到大部分都…