Linux socket编程(5):三次握手和四次挥手分析和SIGPIPE信号的处理

在我之前写的Wireshark抓包:理解TCP三次握手和四次挥手过程中,通过抓包分析了TCP传输的三次握手和四次挥手的过程。在这一节中,将分析在Linux中的三次握手和四次挥手的状态和过程,另外还有一个在我们编程过程中值得注意的SIGPIPE信号的处理。

文章目录

  • 1 TCP连接的11种状态
  • 2 实验:查看TCP状态变化
  • 3 read/recv返回0的作用
  • 4 SIGPIPE信号

1 TCP连接的11种状态

在TCP建立连接的过程中将经历一系列状态,包括:LISTEN(监听)、SYN-SENT(同步已发送)、SYN-RECEIVED(同步已接收)、ESTABLISHED(已建立)、FIN-WAIT-1(等待第一个关闭)、FIN-WAIT-2(等待第二个关闭)、CLOSE-WAIT(等待关闭)、CLOSING(关闭中)、LAST-ACK(最后的确认)、TIME-WAIT(等待时间)、以及虚构的状态CLOSED(关闭)。

  • CLOSED是虚构的,因为它表示当没有传输控制块(TCB)时的状态(没有连接时的状态)

在TCP标准中:

  • 三次握手:首先发送SYN标志的客户端被称为主动打开者,而另一侧的服务器称为被动打开者
  • 四次挥手:首先发送FIN标志的客户端或服务器被称为主动关闭者,而另一端则被称为被动关闭者

如下图所示:客户端通过发送SYN标志成为SYN_SENT状态的过程(主动打开),同时服务器成为LISTEN状态的过程(被动打开)。

在这里插入图片描述

现在来看一下我们熟悉的三次握手和四次挥手的握手流程:

在这里插入图片描述

  1. LISTEN(监听)
    • 表示服务器在接收到来自客户端的SYN标志后可以创建新连接的状态。
    • 在Linux中,通过bind()listen()系统调用,服务器进入LISTEN状态。
  2. SYN_SENT(SYN已发送)
    • 表示关闭状态的客户端发送SYN标志并转换的状态。
    • 在Linux中,客户端可以通过connect()系统调用进入SYN_SENT状态。
    • 根据/proc/sys/net/ipv4/tcp_syn_retries值以最大RTO(重传超时)间隔发送SYN标志。
  3. SYN_RECEIVED(SYN已接收)
    • 表示处于LISTEN状态的服务器在接收到客户端的SYN标志后,响应SYN+ACK标志并转换的状态。
    • 在Linux中,通过accept()系统调用,服务器进入SYN_RECEIVED状态。
    • 根据/proc/sys/net/ipv4/tcp_synack_retries值以最大RTO(重传超时)间隔发送SYN标志。
  4. ESTABLISHED(已建立)
    • 表示在3次握手后建立连接的状态,服务器和客户端可以交换数据。
    • 在Linux中,通过send()recv()系统调用可以进行数据交换。
    • 可以通过在套接字中设置SO_KEEPALIVE选项来定期检查TCP连接的有效性。
  5. FIN_WAIT_1(等待第一次关闭)
    • 表示在已建立的状态中,主动关闭者结束后转换的状态。主动关闭者在调用close()或其进程终止后,由于套接字关闭,主动关闭者发送FIN标志并进入FIN_WAIT_1状态。
  6. FIN_WAIT_2(等待第二次关闭)
    • 表示FIN_WAIT_1状态的主动关闭者接收到被动关闭者的ACK,或者直到由Linux内核设置的FIN_WAIT_2的超时时间过去为止。
  7. TIME_WAIT(等待时间)
    • 表示FIN_WAIT_2状态的主动关闭者接收到被动关闭者的FIN标志后的状态。TIME_WAIT状态应持续2MSL(2 * 最大分段寿命),即直到网络上的所有相关数据包(分段)完全被清除为止,以确保对后续新连接的影响最小化。
  8. CLOSING(关闭中)
    • 表示发生同时关闭,FIN_WAIT_1状态的主动关闭者接收到FIN标志时的状态。
  9. CLOSE_WAIT(等待关闭)
    • 表示被动关闭者从主动关闭者接收到FIN标志并转换的状态。在Linux环境中,CLOSE_WAIT状态没有超时,只有在被动关闭者的套接字关闭时才会结束。
  10. LAST_ACK(最后确认)
    • 表示CLOSE_WAIT状态的被动关闭者发送FIN标志给主动关闭者后,在收到相应的ACK之前保持的状态。

理论介绍完了,还是得实际写代码看看Linux中这些状态的切换,来看看实际执行过程中会遇到什么问题。

2 实验:查看TCP状态变化

  • 这里实验使用这篇利用fork实现服务端与多个客户端建立连接最后贴出来的代码

1、运行服务端程序

在这里插入图片描述

可以看到此时服务端处于LISTEN状态。

2、运行客户端程序

在这里插入图片描述

现在客户端和服务端都处于ESTABLISHED建立连接状态。

  • SYN_SENTSYN_RCVD状态切换地过快,这里没体现出来

3、关闭客户端程序

在这里插入图片描述

可以看到客户端进入了TIME_WAIT状态。在等待2MSL时间后,客户端的网络状态将关闭(CLOSED)。

3 read/recv返回0的作用

注意:在一端关闭后,另一端需要关闭自己的程序,否则主动关闭的那一端将无法进入TIME_WAIT状态,而是保持在FIN_WAIT2状态。

那如何判断对端关闭了呢? 当一端关闭后,另一端的readrecv函数将无限非阻塞返回0。

现在修改一下服务端的读取代码:
在这里插入图片描述

这里把break注释掉,也就是在客户端关闭时,服务端不退出fork出来的子进程。现在重复一下前面的实验过程:运行服务端和客户端,然后客户端断开连接。如下图所示,客户端果然处于FIN_WAIT2状态。

在这里插入图片描述

所以无论是客户端还是服务端都需要实时的readrecv来判断对端是否断开,进行资源的回收处理,否则对端的状态将无法处于TIME_WAIT

4 SIGPIPE信号

我们知道,当一方关闭连接时,它会发送一个FIN标志给对方。这是TCP的一种半关闭状态,表示发送方(主动关闭的一方)不再发送数据,但仍然可以接收数据。所以当服务端收到客户端发送的FIN后,它可以继续向客户端发送数据,但这些数据会被送入TCP的发送缓冲区,并不会立即发送。

如果客户端收到服务端的FIN并关闭了连接,而服务端仍然试图向客户端发送数据,这时如果客户端已经关闭了接收端,写入操作就会导致Linux内核发送一个SIGPIPE信号被发送给服务端进程。

  • SIGPIPE:用于通知进程它试图向一个已经关闭的管道(或socket)写数据。

默认情况下,如果进程忽略或者不捕获SIGPIPE信号,进程会被终止。因此,为了避免进程因为SIGPIPE信号而终止,可以在程序中捕获SIGPIPE信号,或者在写入之前使用一些手段来检查连接状态。

现在再来修改一下服务端程序:

在这里插入图片描述

这里我们也把break注释掉,这样理论上这个recv函数会无限非阻塞地返回0,也就是服务端用于处理客户端的子进程会一直输出peer closed,同时客户端还会处于FIN_WAIT2状态。就是前面read/recv返回0的作用中的实验结果。

但现在,在服务端的recv返回0时,表示服务端已经收到了FIN信号,此时服务端使用send往客户端发送一个消息。现在会产生一个SIGPIPE信号,将服务端用于处理客户端的子进程杀掉。如下图所示,可以看到进程确实被杀掉了。

在这里插入图片描述

为了验证一下确实产生了SIGPIPE信号,我们自己来注册这个信号。程序做出如下修改:

在这里插入图片描述

结果如下:

在这里插入图片描述

不注册的话,内核默认会将程序杀掉,注册了这个信号的话,内核就不会杀掉进程,就由我们自己处理了。在这里由于服务端的socket_read中没有break掉,内核也没有杀掉这个子进程,所以这里recv将无限非阻塞返回0,加上在这个分支中一直调用send发送数据,所以服务端在这里无限输出peer closed,并无限收到SIGPIPE信号。


所以为了防止服务端程序在客户端被关闭后,由于程序之间没有及时的同步,导致服务端继续往客户端写数据,最后异常地被SIGPIPE信号关闭,我们可以忽略掉SIGPIPE信号以防止服务端被误关闭。

signal(SIGPIPE, SIG_IGN);

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《微信小程序开发从入门到实战》学习二十四

3.3.12开发创建投票多选投票页面 创建投票多选投票页面和创建单选投票页面没有区别,唯一区别仅在于向服务端发送数据时,告诉服务器这个投票是什么类型的投票。这个类型用三种数据类型表示都可以,分别如下所示: multiple:true/fa…

【计算机网络笔记】路由算法之距离向量路由算法

系列文章目录 什么是计算机网络? 什么是网络协议? 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能(1)——速率、带宽、延迟 计算机网络性能(2)…

软件设计中如何画各类图之一实体关系图(ER图):数据库设计与分析的核心工具

目录 1 前言2 符号及作用:3 绘制清晰的ER图步骤4 实体关系图的用途5 使用场景6 实际应用场景举例7 结语 1 前言 当谈到数据库设计与分析的核心工具时,实体关系图(ER图)无疑是其中最重要的一环。在软件开发、信息管理以及数据库设…

从大模型到内容生成,初窥门径的AI新次元

视频云AI进化新纪元。 最近Gartner发布2024年十大战略技术趋势,AI显然成为其背后共同的主题。全民化的生成式人工智能、AI增强开发、智能应用......我们正在进入一个AI新纪元。 从ChatGPT的横空出世,到开发者大会的惊艳亮相,OpenAI以一己之力…

909-2015-T1

文章目录 1.原题2.算法思想3.关键代码4.完整代码5.运行结果 1.原题 线性表使用公式化描述方式存储。编写一个函数&#xff0c;从一给定的线性表A中删除值在x ~ y&#xff08;x到y&#xff0c;x<y&#xff09;之间的所有元素&#xff0c;要求以较高的效率来实现。提示&#…

Redis(事务和持久化)(很重要!)

事务的定义&#xff1a; Redis中的事务是指一组命令的集合&#xff0c;这些命令可以在一个原子操作中执行。在Redis中&#xff0c;可以使用MULTI命令开始一个事务&#xff0c;然后使用EXEC命令来执行事务中的所有命令&#xff0c;或者使用DISCARD命令来取消事务。事务可以确保…

Python+Qt虹膜检测识别

程序示例精选 PythonQt虹膜检测识别 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonQt虹膜检测识别》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习与应用推…

从0开始学习JavaScript--JavaScript类型化数组进阶

前面的文章&#xff0c;已经介绍了JavaScript类型化数组的基本概念、常见类型和基本操作。在本文中&#xff0c;我们将深入探讨类型化数组的一些进阶特性&#xff0c;包括共享内存、大端小端字节序、以及类型化数组与普通数组之间的转换&#xff0c;通过更丰富的示例代码&#…

读像火箭科学家一样思考笔记05_思想实验

1. 思想实验室 1.1. 思想实验至少可以追溯到古希腊时期 1.1.1. 从那时起&#xff0c;它们就跨越各个学科&#xff0c;在哲学、物理学、生物学、经济学等领域取得重大突破 1.1.2. 它们为火箭提供动力&#xff0c;推翻政府&#xff0c;发展进化生物学&#xff0c;解开宇宙的奥…

算法的奥秘:常见的六种算法(算法导论笔记2)

算法的奥秘&#xff1a;种类、特性及应用详解&#xff08;算法导论笔记1&#xff09; 上期总结算法的种类和大致介绍&#xff0c;这一期主要讲常见的六种算法详解以及演示。 排序算法&#xff1a; 排序算法是一类用于对一组数据元素进行排序的算法。根据不同的排序方式和时间复…

弄懂Rust编程中的Trait

1.定义 trait trait 定义了某个特定类型拥有可能与其他类型共享的功能。可以通过 trait 以一种抽象的方式定义共享的行为。可以使用 trait bounds 指定泛型是任何拥有特定行为的类型。 一个类型的行为由其可供调用的方法构成。如果可以对不同类型调用相同的方法的话&#xff…

web:[GXYCTF2019]禁止套娃

题目 打开页面显示为 没有其他信息&#xff0c;查看源代码也是空的 用dirsearch扫一下 可能是git源码泄露&#xff0c;可以用githack获取源码 python Githack.py http://5063c85b-a33d-4b6f-ae67-262231a4582e.node4.buuoj.cn:81/.git/去工具所在的目录找到index.php文件 打开…

USART的标准库编程

使用USART与计算机通信 电脑上只有usb端口 没有TX 和RX需要一个USB转TTL电平模块来实现通信 芯片C8T6中只有三个UASRT 选其中一个UASRT来通信即可 那么如何定位那个USART的TX 和RX引脚呢&#xff1f; 方式1 查找最小系统板引脚分布图 查找USART1的引脚 RTS CTS是硬件流控 CK…

5 个适用于 Linux 的开源日志监控和管理工具

当Linux等操作系统运行时&#xff0c;会发生许多事件和在后台运行的进程&#xff0c;以实现系统资源的高效可靠的使用。这些事件可能发生在系统软件中&#xff0c;例如 init 或 systemd 进程或用户应用程序&#xff0c;例如 Apache、MySQL、FTP 等。 为了了解系统和不同应用程序…

【Python数据结构与算法】--- 递归算法应用-五行代码速解汉诺塔问题.

&#x1f308;个人主页: Aileen_0v0 &#x1f525;系列专栏:PYTHON数据结构与算法学习系列专栏&#x1f4ab;"没有罗马,那就自己创造罗马~" 汉诺塔 两层汉诺塔的演示 三层汉诺塔的走法演示 我不知道有没有朋友跟我一样有一个疑问,如果我们顶端的先放到中间柱子呢?…

从零开始学习typescript——数据类型

数据类型 以前我们用js编写代码的时候&#xff0c;都是直接使用let、var、const 来定义数据类型&#xff1b;js会在运行时来确定数据类型&#xff0c;但是在ts中&#xff0c;可以在声明时就可以指定数据类型。如果你学过其他编程语言&#xff0c;比如c、java就能更好的理解了。…

上门维修安装派单系统小程序APP开发之会员级别设计深度解析

啄木鸟鲁班大师上门安装维修平台APP开发之VIP会员解析&#xff0c;在APP或者小程序里设置的会员叫VIP级别会员&#xff0c;系统一共分为4种会员&#xff0c;注册会员&#xff0c;正式会员&#xff0c;VIP金卡会员&#xff0c;VIP钻卡会员。注册用户是指注册了平台但是没有消费记…

安防视频监控管理平台EasyCVR定制首页开发与实现

视频监控平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多路视频流&#xff0c;也能支持视…

0时区格林威治时间转换手机当地时间-Android

假设传入的是2023-11-01T12:59:10.420987这样的格式 要将格式为2023-11-01T12:59:10.420987的UTC时间字符串转换为Android设备本地时间&#xff0c;您可以使用java.time包中的类&#xff08;在API 26及以上版本中可用&#xff09;。如果您的应用需要支持较低版本的Android&…

基于ubuntu20.04安装ros系统搭配使用工业相机

基于ubuntu20.04安装ros系统搭配使用工业相机 1. ROS系统安装部署1.1更新镜像源1.1.1 备份源文件1.1.2 更新阿里源1.1.3 更新软件源 1.2 ros系统安装1.2.1 添加ros软件源1.2.2 添加秘钥1.2.3 更新软件源1.2.4 配置及更换最佳软件源1.2.5 ROS安装1.2.6 初始化rosdep1.2.7 设置环…