Linux:传输层(2) -- TCP协议(1)

目录

1. TCP协议段格式

2. 解包/分用

3. 确认应答(ACK)机制

4. 超时重传机制

5. 连接管理机制

5.1 三次握手

5.2 四次挥手

5.3 TIME_WAIT状态

5.4 CLOSE_WAIT状态

1. TCP协议段格式

  • /目的端口号: 表示数据是从哪个进程来, 到哪个进程去;
  • 32位序号/32位确认号: 后面详细讲;
  • 4TCP报头长度: 表示该TCP头部有多少个32bit(有多少个4字节); 所以TCP头部最大长度是15 * 4 = 60
  • 6位标志位:
            URG: 紧急指针是否有效
            ACK: 确认号是否有效
            PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走
            RST:  对方要求重新建立连接; 我们把携带RST标识的称为复位报文段
            SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段
            FIN:   通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段
  • 16位窗口大小: 后面会详细介绍
  • 16位校验和: 发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP首部,
    包含 TCP 数据部分 .
  • 16位紧急指针: 标识哪部分数据是紧急数据 ;
  • 40字节头部选项: 暂时忽略 ;
//TCP头部,总长度20字节  
typedef struct _tcp_hdr  
{  unsigned short src_port;    //源端口号  unsigned short dst_port;    //目的端口号  unsigned int seq_no;        //序列号  unsigned int ack_no;        //确认号  #if LITTLE_ENDIAN  unsigned char reserved_1:4; //保留6位中的4位首部长度  unsigned char thl:4;        //tcp头部长度  unsigned char flag:6;       //6位标志  unsigned char reseverd_2:2; //保留6位中的2位  #else  unsigned char thl:4;        //tcp头部长度  unsigned char reserved_1:4; //保留6位中的4位首部长度  unsigned char reseverd_2:2; //保留6位中的2位  unsigned char flag:6;       //6位标志   #endif  unsigned short wnd_size;    //16位窗口大小  unsigned short chk_sum;     //16位TCP检验和  unsigned short urgt_p;      //16为紧急指针  
}tcp_hdr;

2. 解包/分用

解包:

  • tcp协议是有固定首部的长度的,为20字节,先读取20字节
  • 将前20字节转换为上述的结构化数据,立马提取标准报头中的4位首部长度(最大为15)
  • 就得到了后续报头(TCP选项)的剩余大小:选项长度 = 4*4位首部长度 - 20
  • 只要把tcp报头处理读取完毕,剩下的就是有效载荷的长度。

分用:报头中包含目的Port,就可以找到应用层的进程,数据交付给进程。 Port和PCB实际上被存储在hash中,key:port,value:PCB

3. 确认应答(ACK)机制

        在网络传输中,由于传输的距离变长了,就会出现一些不可靠的问题。例如:丢包、乱序、校验错误、重复...... 为了保证tcp传输的相对可靠性,提出了一种确认应答机制。

        在双方通信的过程中,除了传输信息的数据段,还包括确认应答的数据段。tcp数据段需要有方式标识数据段,为此包头中包含了序号和确认序号。

确认序号为1001,表明已经收到了1001之前的所有连续的报文。TCP将每个字节的数据都编号:

每个ACK都带有对应的确认序号,意思是告诉发送者,我已经收到了哪些数据,下一次从哪里发送。在TCP中,数据到达的顺序和数据发送的顺序可能是不同的,那么接收方就根据数据段段报头中的序号来确认顺序,将数据排好序。

4. 超时重传机制

  • 主机A发送数据给B之后, 可能因为网络拥堵等原因, 数据无法到达主机B;
  • 如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发

但是, 主机A未收到B发来的确认应答, 也可能是因为ACK丢失了;

因此主机 B 会收到很多重复数据 . 那么 TCP 协议需要能够识别出那些包是重复的包 , 并且把重复的丢弃掉 .。这时候我们可以利用前面提到的序列号, 就可以很容易做到去重的效果。
那么 , 如果超时的时间如何确定? -- TCP 为了保证无论在任何环境下都能比较高性能的通信 , 因此会动态计算这个最大超时时间。
  • Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时
  • 时间都是500ms的整数倍.
  • 如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传.
  • 如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增.
  • 累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接

        TCP在发送数据的时候,不能发送的过快(接收方可能来不及接收),也不能太慢(效率低)。那么如何保证传输的速度呢?-- 根据对方的接收缓冲区的大小来确认发送速度的。

16位窗口大小:是用来进行流量控制(后面会详细讲解)的,在接收方的确认ACK中设置,标识接受方的接受缓冲区中还剩余多少空间,也可以说是:发送方在接收到下一个ACK之前可以发送的最大的数据量大小。也就是说,该位置在填充的时候是填充自己接收缓冲区的大小。

6个标志位:

  • SYN:链接时设为1
  • FIN:断开连接时设为1
  • PSH:催促接收方让上层尽快把数据拿走
  • ACK:确认数据段设置为1
  • URG:指示TCP段中存在紧急数据,当URG被设置为1时,表明该段存在紧急数据,告知接收方应该优先处理该段中的紧急数据。这个紧急数据又被16位紧急指针指出:指出紧急数据的最后一个字节的序列号。紧急数据的开始位置 = (紧急指针 - 当前段的序列号) + 1;
  • RST:强制断开一个TCP链接(不是一个正常的关闭链接的机制)。

5. 连接管理机制

在正常情况下,TCP都要经过三次握手链接和四次挥手断开。

服务端状态转化:
  • [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状态

5.1 三次握手

        上图所示,第一次握手客户端向服务端发送的SYN请求段中的初始序号为client_isn

第二次握手时服务端向客户端发送SYN请求和ACK应答初始序号为server_isn确认序号为client_isn+1(即服务端下次期待收到客户端发出的序号为client_isn+1的段);

第三次握手客户端向服务端发送ACK应答序号seq为client_isn+1确认序号为server_isn+1(即客户端下次期待收到服务端发出的序号为server_isn+1的段),到此握手结束,服务端和客户端建立链接。

为什么需要三次握手呢?

一次握手?-- 不行,一次握手无法同步序列号、无法验证对方的接收能力、受到SYN洪水;

两次握手? -- 不行,无法防止旧的连接请求,造成资源的浪费、SYN洪水;

三次及以上? -- 三次就足够了,多了会浪费时间空间资源。

那三次握手为什么行呢?

  • 用最小成本验证全双工通信信道时通畅的
  • 可以有效防止单机对服务器进行攻击

三次握手最害怕的时第三次握手的ACK丢失,若其丢失,在经过2*MSL后服务端会再次发送SYN-ACK数据段,客户端不会将其丢弃,而会接收并且再发一次确认ACK,直到客户端服务端建立连接。且在一个OS中,必然存在很多个TCP链接,这些TCP是要被管理起来的(先描述再组织),也就意味着是有成本的。

5.2 四次挥手

         如上图所示,在TCP链接中,若一方不想继续通信/没有要发送的数据时,会主动的提出关闭TCP链接;

第一次挥手,客户端发送FIN段到服务端,FIN=1,序列号为m,此时客户端不在发送数据但仍可以接收数据服务端接收到FIN后,进入半关闭状态其可以接受和发送数据

第二次挥手服务端向客户端发送一个ACK段,以此来确认收到了客户端FIN请求;在第三次挥手之前服务端仍然可以向客户端发送数据(不会发送新的数据),客户端也会发送ACK确认;在所有发送的有效数据的段都接收到ACK应答后,服务器才会第三次挥手;第三次挥手之前所有的数据传输和ACK应答都应经被确认。

第三次挥手,当服务端么已有数据向客户端发送时,也会向客户端发送FIN段

第四次三挥手,客户端收到服务端发送的FIN段后,会向服务端发送一个ACK确认报文,服务端收到后,断开连接。

而客户端在发送完最后一个ACK会等待2*MSL的时间在断开连接。-- 目的:1. 保证最后一个ACK尽可能的被对方收到 2. 防止旧的连接请求被当为新的连接请求。这段时间内存在在网络中的数据报是之前由于网络拥堵或者其他情况,导致的服务端/客户端是未应答的报文。

主动断开连接的一方,最终状态未TIME_WAIT状态;被动断开连接的一方,两次挥手后会进入CLOSE_WAIT状态。

5.3 TIME_WAIT状态

现在做一个测试 , 首先启动 server, 然后启动 client, 然后用 Ctrl-C 使 server 终止 , 这时马上再运行server, 结果是:

这是因为 , 虽然 server 的应用程序主动终止了 ,但是其处于TIME_WIT状态 TCP 协议层的连接并没有完全断开 , 因此不能再次监听同样的 server 端口
我们用 netstat 命令查看一下 :

  • TCP协议规定,主动关闭连接的一方要处于TIME_ WAIT状态,等待两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态.
  • 我们使用Ctrl-C终止了server, 所以server是主动关闭连接的一方, 在TIME_WAIT期间仍然不能再次监听同样的server端口;
  • MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同, 在Centos7上默认配置的值是60s;
  • 可以通过 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看msl的值
  • MSL是TCP报文的最大生存时间, 因此TIME_WAIT持续存在2MSL的话就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启, 可能会收到 来自上一个进程的迟到的数据, 但是这种数据很可能是错误的); 同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN. 这 时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以重发LAST_ACK)。

如何解决这种问题呢?使用setsockopt()设置socket描述符的 选项SO_REUSEADDR1, 表示允许创建端口号相同但IP地址不同的多个socket描述符。

int opt = 1;

setsockopt(listenfd, SOL_SOCKET, SOREUSEADDR|SO_REUSEPORT, &opt, sizeof(opt));

5.4 CLOSE_WAIT状态

        在基于tcp编写的服务器中,我们在最后不关闭sock,也就是不执行close(sock),服务端就不会发起第三次挥手,不会发送FIN段,此时的服务器就会一直处于CLOSE_WAIT状态,原因就是服务器没有正确的关闭 socket, 导致四次挥手没有正确 完成. 这是一个 BUG. 只需要加上对应的 close 即可解决问题。

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

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

相关文章

FreeModbus学习——读输入寄存器eMBFuncReadInputRegister

FreeModbus版本:1.6 当功能码为04时,也就是读输入寄存器MB_FUNC_READ_INPUT_REGISTER 看一下它是怎么调用读输入寄存器处理函数的 当功能码为04时,调用读输入寄存器处理函数 这个函数在数组xFuncHandlers中,也就是eMBFuncRead…

实战:MyBatis适配多种数据库:MySQL、Oracle、PostGresql等

概叙 很多时候,一套代码要适配多种数据库,主流的三种库:MySQL、Oracle、PostGresql,刚好mybatis支持这种扩展,如下图所示,在一个“namespace”,判断唯一的标志是iddatabaseId,刚好写…

mysql索引结构

多种数据结构 在数据库索引领域,特别是MySQL的InnoDB存储引擎中,聚簇索引(Clustered Index)和非聚簇索引(也称为二级索引,Secondary Index)是两种主要的索引类型。这些索引类型在数据结构的选择…

最优化原理(笔记)

内积是线性代数运算的一个结果,一行*一列。 内积的性质! 什么是范数??? 对称矩阵:关于主对角线对称! 正定对称矩阵: 二阶导是正定的,f(x)就是严格的凸函数!&a…

spring部分源码分析及Bean的生命周期理解

前言: 本文整体框架是通过refresh方法这个入口进入分析:分析IOC容器的创建及一些Bean的生命周期的知识点,写得确实一般般,感觉自己的有些前置知识并没有理解的很到位,所以,这篇文件先记录一下,…

推荐一款开箱即用、开源、免费的中后台管理系统模版

项目介绍 vue-pure-admin 是推荐一款开箱即用、开源(遵循MIT License开源协议)、免费的中后台管理系统模版,完全采用 ECMAScript 模块(ESM)规范来编写和组织代码,使用了最新的 Vue3、 Vite、Element-Plus、…

无人机图像目标检测技术详解

当前研究领域的热点之一。无人机搭载的高清摄像头能够实时捕获大量图像数据,对这些数据进行有效的目标检测对于军事侦察、环境监测、灾害救援等领域具有重要意义。本文将对无人机图像目标检测技术进行详解,包括图像处理技术、目标检测算法、关键技术应用…

pdf2docx - pdf 提取内容转 docx

文章目录 一、关于 pdf2docx主要功能限制 二、安装1、 PyPI2、从remote安装3、从源码安装4、卸载 三、转化 PDF例 1: convert all pages例 2: 转换指定页面例 3: multi-Processing例 4: 转换加密的pdf 四、提取表格五、命令行交互1、按页面范围2、按页码3、Multi-Processing 六…

gitee设置ssh公钥密码频繁密码验证

gitee中可以创建私有项目,但是在clone或者push都需要输入密码, 比较繁琐。 公钥则可以解决该问题,将私钥放在本地,公钥放在gitee上,当对项目进行操作时带有的私钥会在gitee和公钥进行验证,避免了手动输入密…

C语言数据结构课设:基于EasyX前端界面的飞机订票系统

数据结构课程设计说明书 学 院、系: 软件学院 专 业: 软件工程 班 级: 学 生 姓 名: 范 学 号: 设 计 题 目: 飞机订票系统 起 迄 日 期: 2024年6月18日~ 20…

【测试能力提升-AI】AI介绍

注释: 搞python的最终梦想,搞机器,玩深度,通网络,知模型,拿下AI技术,尽管只是测试,但是也是有梦想的 1. 目标 完成AI任务 ---- 掌握成熟、标准的任务解决方法掌握AI工具 ---- 完成…

2022 年中高职组“网络安全”赛项-海南省省竞赛任务书-1-B模块-B-4Web渗透测试

前言 本章节我将尝试操作B-4模块的渗透测试,搭建环境很难,还望大家点点赞多多支持! 任务概览 最后4、5、6有一定的难度。 环境要求 kali Linux192.168.41.2Web服务器(假设为PYsystem 2020 模拟平台)192.168.41.7交换…

postman接口测试实战篇

击杀小游戏接口测试 接口测试简单介绍击杀小游戏代码下载单接口测试(postman)接口关联并参数化接口测试简单介绍 首先思考两个问题:1.接口是什么?2.接口测试是什么? 1.我们总是把接口想的很复杂,其实呢,它就是一个有特定输入和输出参数的交互逻辑处理单元,它不需要知…

【实战】Spring Cloud Stream3.0 整合RocketMq

文章目录 前言技术积累Spring Cloud Stream3.0新特性RocketMq简介 实战演示引入Maven依赖增加application配置消息生产者消息消费者 前言 相信很多同学用使用过rocketmq消息中间件,且大多情况下是使用原生的rocketmq-spring-boot-starter 进行集成然后创建一个rock…

Spring中Bean的循环依赖

目录 定义: 循环依赖的后果: 一:三级缓存 1、大概的思路: 注意: 2、执行过程: A半完成: B完成: A完成: 注: 二:Lazy 定义: …

入门C语言只需一个星期(星期三)

点击上方"蓝字"关注我们 01、基本数据类型 char 1 字节 −128 ~ 127 单个字符/字母/数字/ASCIIsigned char 1 字节 −128 ~ 127 -unsigned char 1 字节 0 ~ 255 -int…

【SpringCloud】微服务远程调用OpenFeign

工作原理流程图 上代码 common中添加依赖&#xff1a; <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency><groupId>org.spri…

CV13_混淆矩阵、F1分数和ROC曲线

1.1 混淆矩阵Confusion Matrix 混淆矩阵&#xff08;Confusion Matrix&#xff09;是机器学习和统计学中用于描述监督学习算法性能的特定表格布局。它是一种特定类型的误差矩阵&#xff0c;可以非常直观地表示分类模型在测试数据集上的预测结果与实际结果之间的对比。 混淆矩…

【数据结构】初识集合框架

&#x1f387;&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳&#xff0c;欢迎大佬指点&#xff01; 人生格言: 当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友…

Python爬虫(6) --深层爬取

深层爬取 在前面几篇的内容中&#xff0c;我们都是爬取网页表面的信息&#xff0c;这次我们通过表层内容&#xff0c;深度爬取内部数据。 接着按照之前的步骤&#xff0c;我们先访问表层页面&#xff1a; 指定url发送请求获取你想要的数据数据解析 我们试着将以下豆瓣读书页…