搜索
bottom↓
回复: 22

Linux TCP通信 一方关闭socket,另一方被强制退出(SIGPI...

[复制链接]

出0入0汤圆

发表于 2014-9-17 21:49:08 | 显示全部楼层 |阅读模式
本帖最后由 阳光天蓝色 于 2014-9-17 21:56 编辑

一、问题来源
Linux上一个TCP服务器负责采集发送图片数据,windows客户端接收数据,当客户端关闭socket或者退出时,服务器进程被强制退出。

二、分析(网络)
        具体的分析可以结合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(SIGPIPE, SIG_IGN);
这样, 第二次调用write方法时, 会返回-1, 同时errno置为SIGPIPE. 程序便能知道对端已经关闭.
        在linux下写socket的程序的时候,如果尝试send到一个disconnected socket上,就会让底层抛出一个SIGPIPE信号。这个信号的缺省处理方法是退出进程,大多数时候这都不是我们期望的。因此我们需要重载这个信号的处理方法。调用以下代码,即可安全的屏蔽SIGPIPE:
        signal (SIGPIPE, SIG_IGN);
系统里边定义了三种处理方法:
(1)SIG_DFL信号专用的默认动作:
  (a)如果默认动作是暂停线程,则该线程的执行被暂时挂起。当线程暂停期间,发送给线程的任何附加信号都不交付,直到该线程开始执行,但是SIGKILL除外。
  (b)把挂起信号的信号动作设置成SIG_DFL,且其默认动作是忽略信号 (SIGCHLD)。
(2)SIG_IGN忽略信号
  (a)该信号的交付对线程没有影响
  (b)系统不允许把SIGKILL或SIGTOP信号的动作设置为SIG_DFL
(3)SIG_ERR  

三、我的用法
1、包含头文件:
  1. #include <signal.h>
复制代码

2、声明定义一个处理函数
  1. void signal_handler(int sigm);
复制代码

2、在产生信号产生前调用一下代码:
  1. struct sigaction act;
  2. memset(&act,0,sizeof(act));
  3. // act.sa_handler = SIG_IGN;//设定接受到指定信号后的动作为忽略
  4. act.sa_handler = signal_handler;//设定接受到指定信号后的动作为 相关函数
  5. act.sa_flags = 0;
  6. sigemptyset(&act.sa_mask);
  7. sigaction(SIGPIPE,&act,NULL);//屏蔽SIGPIPE信号
  8. sigaction(SIGCHLD,&act,NULL);//屏蔽子进程终结信号,让init进程去处
复制代码

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2014-9-17 22:03:38 | 显示全部楼层
谢谢,收藏了

出0入0汤圆

发表于 2014-9-17 22:34:31 | 显示全部楼层
mark

出0入0汤圆

发表于 2014-9-17 23:13:41 来自手机 | 显示全部楼层
以前也遇到类似问题,收藏了

出0入0汤圆

发表于 2014-9-17 23:21:09 来自手机 | 显示全部楼层
收藏了,帮顶

出0入0汤圆

发表于 2014-9-17 23:41:26 | 显示全部楼层
好笔记,顶!

出0入0汤圆

发表于 2014-9-17 23:54:38 | 显示全部楼层
signal(SIGPIPE, SIG_IGN)是对整个进程有效吗,如果SOCKET是在多个子线程里面操作, signal(SIGPIPE, SIG_IGN)应该放在什么位置?

出0入0汤圆

发表于 2014-9-18 09:07:27 | 显示全部楼层
mmyer 发表于 2014-9-17 23:54
signal(SIGPIPE, SIG_IGN)是对整个进程有效吗,如果SOCKET是在多个子线程里面操作, signal(SIGPIPE, SIG_ ...

是对整个进程有效的,而且对 fork 出来的进程也有效。通常信号处理的初始化会放在 main 进去后的地方。

出0入0汤圆

发表于 2014-9-18 09:57:38 | 显示全部楼层
一般来说,只有在往close掉的socket发送时,才会接收到SIGPIPE信号,可以在调用send接口时,把flags设置为MSG_NOSIGNAL,告知系统不要发送SIGPIPE信号。

出0入0汤圆

 楼主| 发表于 2014-9-18 11:54:06 | 显示全部楼层
lkm_unication 发表于 2014-9-18 09:57
一般来说,只有在往close掉的socket发送时,才会接收到SIGPIPE信号,可以在调用send接口时,把flags设置为M ...

我试试,坑爹的书本一般将 flag 设为0;

出0入0汤圆

发表于 2014-9-18 12:38:10 | 显示全部楼层
没遇到过~不明觉厉

出0入0汤圆

发表于 2014-9-18 12:38:37 | 显示全部楼层
没遇到过~不明觉厉

出0入0汤圆

发表于 2014-9-18 13:47:18 | 显示全部楼层
谢谢,收藏了 mask

出0入0汤圆

发表于 2014-9-18 14:38:05 | 显示全部楼层
make  即将遭遇此类问题.

出0入12汤圆

发表于 2014-9-19 01:15:41 | 显示全部楼层
server 为什么要退出?不支持多客户端?

出0入0汤圆

发表于 2014-9-19 08:16:41 | 显示全部楼层
这个算是程序的问题了,一个文件handle如果已经无效,你还在读写,显然会出错,
基本原则应该是在handle无效后再不要对它操作.

出870入263汤圆

发表于 2014-9-19 09:03:41 | 显示全部楼层
lkm_unication 发表于 2014-9-18 09:57
一般来说,只有在往close掉的socket发送时,才会接收到SIGPIPE信号,可以在调用send接口时,把flags设置为M ...

嗯,这是标准有效的方式,仅用socket函数就可以解决这个问题了。

出0入0汤圆

发表于 2014-9-19 09:03:52 | 显示全部楼层
学习了,谢谢

出0入0汤圆

 楼主| 发表于 2014-9-19 09:07:52 | 显示全部楼层
yj_yulin 发表于 2014-9-19 08:16
这个算是程序的问题了,一个文件handle如果已经无效,你还在读写,显然会出错,
基本原则应该是在handle无效后 ...

不太明白你的意思,服务器是被动响应客户端的请求,数据是单方向从服务器传到客户端,服务器如何知道客户端已经不再了或者关闭通信?有其他方法吗?

出0入0汤圆

发表于 2014-9-19 09:10:40 | 显示全部楼层
to abutter and yj_yulin
并不是作者想退出的,因为即使你每时每刻都会检测socket的状态,但你保证不了在发送那一刻,对方强行退出的情况,一旦发生了,系统会发生PIPE信号上来的,如果事先没有处理该信号,进程会被强行关闭。

出0入0汤圆

发表于 2014-9-19 18:06:40 | 显示全部楼层
lkm_unication 发表于 2014-9-19 09:10
to abutter and yj_yulin
并不是作者想退出的,因为即使你每时每刻都会检测socket的状态,但你保证不了在发 ...


还是用标准处理吧(flags参数用MSG_NOSIGNAL ),这个不用安装signal handler,用全局signal总觉得像是在补救,c的处理方式还是检查返回值合理

出0入0汤圆

发表于 2014-9-19 18:09:59 | 显示全部楼层
阳光天蓝色 发表于 2014-9-19 09:07
不太明白你的意思,服务器是被动响应客户端的请求,数据是单方向从服务器传到客户端,服务器如何知道客户 ...

还是用标准处理吧(flags参数用MSG_NOSIGNAL ),这个不用安装signal handler,用全局signal总觉得像是在补救,c的处理方式还是检查返回值合理
如果不想每次都写这个参数,也可以在连接建立后用setsockopt设置一下

出0入0汤圆

发表于 2019-1-21 15:38:47 | 显示全部楼层
不明觉厉
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-29 16:43

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表