SIGPIPE[通俗易懂]

SIGPIPE[通俗易懂]当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。我写了一个服务器程序,在Linux下测试,然后用C++写了客户端用千万级别数量的短链接进行压力测试.  但是服务器总是莫名退出,没有core文件.最后问题确

大家好,又见面了,我是你们的朋友全栈君。

当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。

我写了一个服务器程序,在Linux测试,然后用C++写了客户端用千万级别数量的短链接进行压力测试.  但是服务器总是莫名退出,没有core文件.

最后问题确定为, 对一个对端已经关闭的socket调用两次write, 第二次将会生成SIGPIPE信号, 该信号默认结束进程.

具体的分析可以结合TCP的”四次握手”关闭. TCP是全双工的信道, 可以看作两条单工信道, TCP连接两端的两个端点各负责一条. 当对端调用close时, 虽然本意是关闭整个两条信道, 但本端只是收到FIN包. 按照TCP协议的语义, 表示对端只是关闭了其所负责的那一条单工信道, 仍然可以继续接收数据. 也就是说, 因为TCP协议的限制, 一个端点无法获知对端的socket是调用了close还是shutdown.

对一个已经收到FIN包的socket调用read方法, 如果接收缓冲已空, 则返回0, 这就是常说的表示连接关闭. 但第一次对其调用write方法时, 如果发送缓冲没问题, 会返回正确写入(发送). 但发送的报文会导致对端发送RST报文, 因为对端的socket已经调用了close, 完全关闭, 既不发送, 也不接收数据. 所以, 第二次调用write方法(假设在收到RST之后), 会生成SIGPIPE信号, 导致进程退出.

为了避免进程退出, 可以捕获SIGPIPE信号, 或者忽略它, 给它设置SIG_IGN信号处理函数:

signal(SIGPIPESIG_IGN);

这样, 第二次调用write方法时, 会返回-1, 同时errno置为SIGPIPE. 程序便能知道对端已经关闭.


linux下写socket的程序的时候,如果尝试send到一个disconnected socket上,就会让底层抛出一个SIGPIPE信号。
这个信号的缺省处理方法是退出进程,大多数时候这都不是我们期望的。因此我们需要重载这个信号的处理方法。调用以下代码,即可安全的屏蔽SIGPIPE

signal (SIGPIPE, SIG_IGN);

我的程序产生这个信号的原因是: 
client端通过 pipe 发送信息到server端后,就关闭client端, 这时server端,返回信息给 client 端时就产生Broken pipe 信号了,服务器就会被系统结束了。


对于产生信号,我们可以在产生信号前利用方法 signal(int signum, sighandler_t handler) 设置信号的处理。如果没有调用此方法,系统就会调用默认处理方法:中止程序,显示提示信息(就是我们经常遇到的问题)。我们可以调用系统的处理方法,也可以自定义处理方法。 

系统里边定义了三种处理方法: 
(1)SIG_DFL信号专用的默认动作:
  (a)如果默认动作是暂停线程,则该线程的执行被暂时挂起。当线程暂停期间,发送给线程的任何附加信号都不交付,直到该线程开始执行,但是SIGKILL除外。
  (b)把挂起信号的信号动作设置成SIG_DFL,且其默认动作是忽略信号 (SIGCHLD)。
(2)SIG_IGN忽略信号
  (a)该信号的交付对线程没有影响
  (b)系统不允许把SIGKILL或SIGTOP信号的动作设置为SIG_DFL
3)SIG_ERR   

项目中我调用了signal(SIGPIPESIG_IGN), 这样产生  SIGPIPE 信号时就不会中止程序,直接把这个信号忽略掉。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/139006.html原文链接:https://javaforall.net

(0)
上一篇 2022年5月7日 上午8:00
下一篇 2022年5月7日 上午8:20


相关推荐

  • 用飞书多维表调用Coze工作流 | Coze捣鼓日记

    用飞书多维表调用Coze工作流 | Coze捣鼓日记

    2026年3月12日
    2
  • SpringBoot整合JDBC、整合Druid数据源详解教程

    SpringBoot整合JDBC、整合Druid数据源详解教程目录一、整合JDBC1.环境准备1.创建数据库2.创建SpringBoot项目3.IDEA连接数据库2.编写数据库配置信息3.编写测试类测试4.CRUD操作数据库1.JDBCTemplate简介2.CRUD测试二、整合Druid数据源1.Druid简介2.部分基本配置参数3.使用Durid数据源1.导入依赖2.切换数据源3.设置数据源属性4.使添加属性生效5.配置Druid后台监控Servlet6.配置Druid监控过滤器filter一、整合JDBC1.环境准备.

    2022年7月23日
    16
  • graylog查询语法

    graylog查询语法graylog 查询语法

    2026年3月18日
    1
  • php代码进行CC防御

    php代码进行CC防御很多站长在做站的时候会碰到被人 cc 攻击 有时候真是苦不堪言呐 其实本人结合多年的做站经验对于攻防还是有一定的见解的 对于防御我想说的是 只要服务器真实 ip 不暴露 服务器域名直接访问做禁止处理 域名加上 cdn 可以很大程度上起到防御作用 国内已备案的域名可以使用百度云加速 免费又好用 不过有点鸡肋的是移动的线路总是会出问题 好了 废话不多说 其实使用 php 代码也可以进行一定的 cc 防御的 以下就是用于防御 cc 攻击的 php 代码 phpempty SERVER HTTP VIA o

    2026年3月18日
    2
  • SpringBoot项目连接MySQL数据库

    SpringBoot项目连接MySQL数据库前言本篇基于 MySQL 数据库 8 0 29 版本进行说明 需要提前安装 MySQL 数据库 具体教程详见 http t csdn cn PZo5S 一 导入依赖一般在新建 SpringBoot 项目时 勾选了 MySQL 以及 JDBC 依赖 可以直接使用 无须再次导入依赖依赖查找 https mvnrepositor com 1 在 pom 文件中导入 MySQL 依赖 dependency groupId mysql groupId dependency

    2026年3月17日
    2
  • PIXI入门-PIXI文档翻译(1)

    PIXI入门-PIXI文档翻译(1)b 1 创建渲染器和舞台 b 第一步是创建一个矩形显示区域 您可以开始在其上显示图像 Pixi 有一个 renderer 对象为你创建这个 它自动生成一个 HTML 元素 并计算如何在画布上显示您的图像 然后需要创建一个特殊的 PixiContaine 对象称为 stage 正如你将看到的 这个阶段对象将被用作根容器 包含你想要 Pixi 显示的所有东西 这里是你需要写的代码来创建 render

    2026年3月17日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号