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);

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

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

相关文章

JAVAEE---多线程

内核 内核时操作系统的核心 操作系统有内核态和用户态,像我们平时所用到的qq音乐,微信等都属于用户态执行的程序。那么qq音乐播放音乐需要用到扬声器,扬声器的操作就是在内核空间进行操作,用户态不能对其进行操作。 操作系统 …

Macs Fan Control Pro:掌握您的Mac风扇,提升散热效率

在Mac的世界里,每一个细节都显得格外重要。而其中,风扇的控制与调节则显得尤为重要。然而,原生的Mac系统并不提供直观的风扇控制工具,这使得许多Mac用户在处理高负荷任务时,风扇无法有效地进行散热,导致机器…

Python+Selenium定位不到元素常见原因及解决办法(报:NoSuchElementException)

这篇文章主要介绍了PythonSelenium定位不到元素常见原因及解决办法(报:NoSuchElementException),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 在做web应用的自动…

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

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

Linux中的MFS分布式文件系统

目录 一、MFS分布式文件系统 1、MooseFS简介 2、Moose File System的体系结构 (1)MooseFS Master (2)MooseFS Chunk Server (3)MooseFS Metalogger (4)MooseFS Client &…

华为云cce健康检查有什么用?配置需要注意什么?

华为云cce健康检查 如上图,华为云健康检查可用来探测cce的实例运行状态,必要时cce会自动重启实例,达到cce持续服务。 但是配置时需要注意一下几个方面,否则cce的状态总是有些不正常。 1、http探查比较友好。因为我们的在cce里面…

物联网AI MicroPython学习之语法 ADC数模模块

学物联网,来万物简单IoT物联网!! ADC 介绍 模块功能: ADC数模转换模块 ADC功能在ESP32引脚32-39上可用,使用默认配置时,ADC引脚上的输入电压必须介于0.0v和1.0v之间(任何高于1.0v的值都将读为4095&#x…

【信息安全】浅谈三种XSS(跨站脚本攻击)的攻击流程与防御措施

XSS 跨站脚本攻击(Cross-Site Scripting,简称XSS)是一种常见的Web安全漏洞,攻击者通过在Web应用中注入恶意脚本,使得浏览器在解析页面时执行该脚本,从而实现攻击目的。 类型 存储型XSS:攻击者…

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

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

Keil MDK 安装

0 Preface/Foreword 1 下载和安装 官网:Keil Embedded Development Tools for Arm, Cortex-M, Cortex-R4, 8051, C166, and 251 processor families. Keil MDK 下载链接:Keil MDK 1.1 下载 根据需求下载对应的Keil MDK edition。 不同的editions包括 …

Monoxide relay机制和连弩挖矿

这篇文章就两个点,relay机制 、 连弩挖矿 relay 最终原子性 Eventual Atomicity 一笔跨链交易,从取款shard中发出,到存款shard中. 当收款shard中将这笔夸片交易打包上链后,原子性才执行结束。 这样做的延迟是比较小的。 如何应…

深度神经网络下的风格迁移模型

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 斯坦福大学李飞飞团队的风格迁移模型是一种基于深度学习的图像处理技术,可以将一张图像的风格转移到另一张图像上。该模型…

编写函数求定积分的通用函数

归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍 收藏⭐ 留言​📝 不积跬步无以至千里,…

Hfish安全蜜罐部署

一、Hfish蜜罐介绍 HFish蜜罐官网 HFish是一款社区型免费蜜罐,侧重企业安全场景,从内网失陷检测、外网威胁感知、威胁情报生产三个场景出发,为用户提供可独立操作且实用的功能,通过安全、敏捷、可靠的中低交互蜜罐增加用户在失陷…

注册中心CAP架构剖析

Nacos 支持 AP 或 CP AP Nacos 通过临时节点实现 AP 架构,将服务列表放在内存中; CP Nacos 通过持久化节点实现 CP 架构,将服务列表放在文件中,并同步到内存,通过 Raft 协议算法实现; 通过配置 epheme…

智能座舱架构与芯片 - (3) 硬件篇 上

一、介绍 在了解智能座舱的基本架构之后,我们有必要针对智能座舱域的硬件平台,软件平台,SOC等进行逐一介绍。从它们的整体结构中去认识最新的智能座舱组成部件,以及主要功能等。 如上图,是中央计算-区域控制架构下的智…

Java Finalization‘s Memory-Retention Issues 及Reference类解析

引言 《Effective Java Programming Language Guide》 一书中强烈建议不要使用java的finalize()方法去做对象消亡前的清理。因为jvm调用finalize()方法的时机并不确定,容易导致Memory-Retention Issues。通俗点讲就是内存没办法及时回收。 详细的见oracle的官方说明…

数字电路的基础知识

一、数字电路概述 用数字信号完成对数字量进行逻辑运算和算术运算的电路称为数字电路。 由于它具有逻辑运算和逻辑处理功能,所以又称为数字逻辑电路。 现代的数字电路由半导体工艺制成的数字集成器件构造而成。 逻辑门是数字电路的基本单元电路,就如同在…

ES 查询语法-详解

文章目录 1.DSL查询文档1.1.DSL查询分类1.2.全文检索查询1.2.1.使用场景1.2.2.基本语法1.2.3.总结 1.3.精准查询1.3.1.term查询1.3.2.总结 1.DSL查询文档 elasticsearch的查询依然是基于JSON风格的DSL来实现的。 1.1.DSL查询分类 Elasticsearch提供了基于JSON的DSL&#xff…

vue el-form表单嵌套组件时正则校验不生效

vue el-form表单嵌套组件时正则校验不生效 上图 组件选中数据&#xff0c;但是正则校验未检测到并且红字提示不会消失。直接上代码 <template><div class"created_report"><el-form :model"formData" :rules"isRules" ref"…