Linux网络:传输层协议TCP(二)三次挥手四次握手详解

目录

一、TCP的连接管理机制

1.1三次握手 

 1.2四次挥手

二、理解 TIME_WAIT 状态

2.1解决TIME_WAIT 状态引起的 bind 失败的方法

三、理解CLOSE_WAIT状态

 


一、TCP的连接管理机制

在正常情况下, TCP 要经过三次握手建立连接, 四次挥手断开连接

1.1三次握手 

三次握手顾名思义就是在刚开始建立连接时,一端向另一端发送SYN连接请求(一般时客户端向服务器),服务器收到后做出应答,也向客户端发送SYN连接请求顺便捎带ACK应答,客户端收到后再次向服务器发送ACK应答,至此三次握手完成,双方连接建立完毕。

前两次握手只要有一次没有成功那么连接都是无法建立的双方都是可以清楚的知道连接是没有建立的,那如何保证第三次握手的ACK对方收到了呢?

一般情况下如果前两次握手成功,第三次出现失败的概率很小,如果真的出现,比如客户端给服务器发的携带ACK应答的报头在中途丢包了,那么此时服务器就会进入超时等待,那么此时客户端会认为连接已经建立好了,可能就会直接进行数据报文的传输,此时服务器收到报文,但是对于服务器端,连接还未建立好,此时服务器就会对客户端做出应答,将报头中的RST置为1,表示重置。

此时就会进行重置,双方重新进行三次握手。所以RST主要就是解决连接问题。

所以为什么要三次握手?

1、因为建立连接维护连接是有成本的,如果连接只需要单次握手就能建立,那客户端疯狂的单方面给服务器发送连接最终就会导致服务器花费资源来维护大量连接,这种情况服务器是很容易被攻击的。而这种大量发送SYN的情况,我们也将其称为SYN洪水攻击。而三次握手我们可以发现,客户端也需要进行一次接收和一次应答,付出的代价和服务器是对等的,而在上图的逻辑中,服务器也就倒逼着客户端,服务器和客户端建立连接的前提是客户端先建立连接!所以三次握手虽然不能保证绝对的安全,但是起码可以避免遭受单机攻击就被搞挂掉,这也是为什么不一次两次握手的主要理由。

2、三次握手,client和server双方,都会有确定的一次收发,三次握手也是最小成本确认全双工的方式,确保双方OS是健康且愿意通信的,其实三次握手本质上也是4次握手,只不过中间两次被变成一次捎带应答了。

 1.2四次挥手

一般情况下,依旧是客户端率先起手发送携带FIN的报头,表示断开连接的请求,此时是第一次挥手 ,当客户端发送携带FIN标志位的报头时 ,此时就表明用户层不会再有数据往发送缓冲区进行写入了,因为FIN往往就对应我们使用的close(fd),此时我们在代码中调用了close关闭了对应的文件描述符,那么我们就肯定无法再往对应的fd中去进行写入了。同样的对于服务器端也是一样的,所以双方发送FIN本质上也就是在用户层调用close(),去将对应缓冲区的数据冲刷干净。

而四次挥手,也是双方OS自动完成的(写到这里博主真的是有苦在心口难开啊,明明不需要博主去实现但还是要拿出100%的精力去学,因为唯有真正了解,才能运用自如啊!)。 而这里为什么不使用捎带应答将两次挥手变成一次呢?

因为与建立连接不同,建立连接时双方都是处于空闲,且目的都是与彼此建立连接,而断开时,客户端数据发送完毕调用close,而服务器此时不一定将所有的数据都完整传输给客户端了,所以无法直接无脑二合一,因此才要分为四次。而close也只是用户层的我们将文件描述符关闭,实际上,真正关闭也是等到对方的ACK以后才正式关闭,也可以调用shutdown来选择性关闭文件描述符的读/写功能。

三次握手与四次挥手就像一场凄美的爱情一样,在一起时总是迫不及待,轰轰烈烈,两步并一步奔向彼此,对未来充满向往,而最终因为现实也好,缘尽也罢,终会有一方先挥手作别,尽管再依依不舍,一次次的wait换来的还是双方的分离。

 服务端状态转化:

[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 状态.

二、理解 TIME_WAIT 状态

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

 这是因为,虽然 server 的应用程序终止了,但 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 的值;

想一想, 为什么是 TIME_WAIT 的时间是 2MSL?

MSL 是 TCP 报文的最大生存时间, 因此 TIME_WAIT 持续存在 2MSL 的话

就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服 务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错 误的);

同时也是在理论上保证最后一个报文可靠到达(假设最后一个 ACK 丢失, 那么 服务器会再重发一个 FIN. 这时虽然客户端的进程不在了, 但是 TCP 连接还在, 仍然 可以重发 LAST_ACK);

2.1解决TIME_WAIT 状态引起的 bind 失败的方法

在 server 的 TCP 连接没有完全断开之前不允许重新监听, 某些情况下可能是不合理的

服务器需要处理非常大量的客户端的连接(每个连接的生存时间可能很短, 但是 每秒都有很大数量的客户端来请求).

这个时候如果由服务器端主动关闭连接(比如某些客户端不活跃, 就需要被服务 器端主动清理掉), 就会产生大量 TIME_WAIT 连接.

由于我们的请求量很大, 就可能导致 TIME_WAIT 的连接数很多, 每个连接都会 占用一个通信五元组(源 ip, 源端口, 目的 ip, 目的端口, 协议). 其中服务器的 ip 和端 口和协议是固定的. 如果新来的客户端连接的 ip 和端口号和 TIME_WAIT 占用的链 接重复了, 就会出现问题.

使用 setsockopt()设置 socket 描述符的 选项 SO_REUSEADDR 为 1, 表示允许创建端 口号相同但 IP 地址不同的多个 socket 描述符

三、理解CLOSE_WAIT状态

当你看到CLOSE_WAIT状态时,说明此时只是断开了双方中一个方向上的连接,并没有完成四次挥手。我们可以做一个实验,让客户端一方主动断开连接,而服务端处在一个死循环中或者服务端不调用close函数。

假设现在客户端和服务端建立好连接,服务端一旦建立连接服务端到客户端方向上的连接,就会处于ESTABLISHED状态。

对于服务器上出现大量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭 socket, 导致四次挥手没有正确完成. 这是一个 BUG。只需要加上对应的 close 即可解决问题。

服务端结束服务以后一定要调用close函数,否则会发生内存泄漏

发生CLOSE_WAIT 的原因:

  • 没有处理 read 返回值为0的情况。一般read 返回值为0 ,write一端就会认为对端要连接断开;如果read 返回值小于0,说明读取出错。
  • 主要原因可能是客户端一端关闭了连接,然而此时服务端忙于处理其他 IO任务,没有关闭双方连接。

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

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

相关文章

【设计模式】代理模式详解

1.简介 代理模式是常用的Java设计模式,该模式的特点是代理类与委托类共享相同的接口。代理类主要负责预处理消息、过滤消息、将消息转发给委托类,并在事后处理消息等。代理类与委托类之间通常存在关联关系,一个代理类对象与一个委托类对象关…

SpringBoot Mysql->达梦8 activiti6.0.0 项目迁移

全部源码:公众号搜索资小库,回复dm获取源码 1.整合达梦 1.1 达梦驱动下载 MyBatis-Plus 框架 | 达梦技术文档 (dameng.com) 1.2 数据迁移 怎么安装数据库,很多大佬有帖子,搜一下达梦先建立用户,使用DM管理工具 链…

SQL Server 数据误删的恢复

在日常的数据库管理中,数据的误删操作是难以避免的。为了确保数据的安全性和完整性,我们必须采取一些措施来进行数据的备份和恢复。本文将详细介绍如何在 SQL Server 中进行数据的备份和恢复操作,特别是在发生数据误删的情况下。假设我们已经…

使用visual studio编译C++项目时无法找到 enum中的某些项

vs 2017 编译一个cocos2dx 的老项目时,报错: 在项目中搜索关键字 ARMATURE_LOOP_COMPLETE,发现在文件EventType.h中是有定义的,是 enum Event 的一项,而且确认了报错的文件已经引入了这个头文件: 这太奇怪了…

傻瓜式PHP-Webshell免杀学习手册,零基础小白也能看懂

项目描述 一、PHP相关资料 PHP官方手册: https://www.php.net/manual/zh/ PHP函数参考: https://www.php.net/manual/zh/funcref.php 菜鸟教程: https://www.runoob.com/php/php-tutorial.html w3school: https://www.w3school…

【React】全面解析:从基础知识到高级应用,掌握现代Web开发利器

文章目录 一、React 的基础知识1. 什么是 React?2. React 的基本概念3. 基本示例 二、React 的进阶概念1. 状态(State)和属性(Props)2. 生命周期方法(Lifecycle Methods)3. 钩子(Hoo…

Spring Cloud微服务项目统一封装数据响应体

在微服务架构下,处理服务之间的通信和数据一致性是一个重要的挑战。为了提高开发效率、保证数据的一致性及简化前端开发,统一封装数据响应体是一种非常有效的实践。本文博主将介绍如何在 Spring Cloud 微服务项目中统一封装数据响应体,并分享…

ValueError: invalid literal for int() with base 10: ‘a‘

ValueError: invalid literal for int() with base 10: ‘a‘ 目录 ValueError: invalid literal for int() with base 10: ‘a‘ 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页,我是博主英杰&#xff…

基于web3区块链的名酒资产数字化、个人闲置资产收藏系统,实现联盟链、NFT数据上链、智能合约开发

系统背景: 国内有众多历史悠久却极具收藏价值的名酒品类,但是传统名酒投资存在着保真、流通和收藏三大痛点,极大影响了名酒产业的发展。基于区块链的分布式、不可篡改、可追溯、透明性、多方维护、交叉验证等特性,数据权属可以被有…

【Linux】软连接|硬链接|当前路径(.)|上级路径(..)|硬链接不能链接目录

目录 前言 软连接 ​编辑 删除源文件 快捷应用 总结 硬链接 硬链接为何不能链接目录 为什么软连接可以 软硬链接区别 当前路径(.)和上级路径(..) ​编辑 前言 在 Linux 中,文件的存储位置和数据(属性内容)是由 inode 号来唯一标…

错误:请查看是否设备未加入到证书列表或者确认证书类型是否匹配

这个问题实际上网上都有解法,但是可能没有那么的清楚,大家在各种问,我既然搞定了,就分享给大家吧网上解法: 开发调试需要另外创建开发证书和描述文件,描述文件同时绑定开发设备解读: 实际上这句…

electron 主进程和渲染进程

最近在整理electron 相关的项目问题,对自己来说也是温故知新,也希望能对小伙伴们有所帮助,大家共同努力共同进步。加油!!!! 虽然最近一年前端大环境不好,但是大家还是要加油鸭&#…

SmartInitializingSingleton和InitializingBean的区别

SmartInitializingSingleton:接口里面就一个方法afterSingletonsInstantiated,它是spring容器将所有bean都初始化完成之后,才会去调用,要求实现它接口的bean必须是单例的。 应用场景:可以在服务启动之后去处理一些逻辑…

科普文:从源码解读5种Redis基本数据类型

键值对字符串 char* 与 SDS char* 的不足: 操作效率低:获取长度需遍历,O(N)复杂度 二进制不安全:无法存储包含 \0 的数据 SDS 的优势: 操作效率高:获取长度无需遍历,O(1)复杂度&#xff08…

60个常见的 Linux 指令

常见60个Linux指令 1.ssh 登录到计算机主机2.ls 列出目录内容3.pwd 当前终端会话所在的完整路径4.cd 切换当前工作目录5.touch 创建空文件或更新文件的时间戳6.echo 终端输出文本或变量值7.nano 在终端中编辑文件8.vim 文本编辑器9.cat 查看、连接和创建文件10.shred 安全删除敏…

XPathParser类

XPathParser类是mybatis对 javax.xml.xpath.XPath的包装类。 接下来我们来看下XPathParser类的结构 1、属性 // 存放读取到的整个XML文档private final Document document;// 是否开启验证private boolean validation;// 自定义的DTD约束文件实体解析器,与valida…

科研绘图系列:R语言山脊图(Ridgeline Chart)

介绍 山脊图(Ridge Chart)是一种用于展示数据分布和比较不同类别或组之间差异的数据可视化技术。它通常用于展示多个维度或变量之间的关系,以及它们在不同组中的分布情况。山脊图的特点: 多变量展示:山脊图可以同时展示多个变量的分布情况,允许用户比较不同变量之间的关…

FastAPI(七十二)实战开发《在线课程学习系统》接口开发-- 留言列表开发

源码见:"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 之前我们分享了FastAPI(七十一)实战开发《在线课程学习系统》接口开发-- 查看留言,这次我们分享留言列表开发。 获…

i2c中结构体 数据传输 i2c Tools使用

I2C中重要结构体 在I2C(Inter-Integrated Circuit)通信中,涉及的主要结构体通常用于描述设备、消息和传输的配置。以下是一些常见的I2C结构体及其作用: i2c_adapter: 这是一个代表I2C总线适配器的结构体。它包含与该I2C总线相关的…

Hive3:Centos7环境部署Hive服务

一、安装说明 1、Hadoop集群情况 3台机器:4G2C、2G2C、2G2C 安装教程:Centos7环境安装Hadoop集群 2、安装MySQL,用于存储Hive的元数据 在102机器上安装MySQL 安装MySQL使用服务器的root账号 3、最后安装Hive 安装hive过程使用服务器的atgu…