tcp连接,一端关闭另一端莫名挂掉问题

场景一:

今天在项目的集成测试当中,两个模块通过tcp建立连接。发现如果一端被kill掉,另一端的程序也会莫名的崩溃,且没有生成core文件。
最后分析确定:对一个对端已经关闭的socket调用两次 send/write,第二次将会生成SIGPIPE信号,该信号默认结束进程。
具体的分析可以结合TCP的”四次握手”关闭。 TCP是全双工的信道,可以看作两条单工信道,TCP连接两端的两个端点各负责一条。 当对端调用close时,虽然本意是关闭整个两条信道,但本端只是收到FIN包。 按照TCP协议的语义,表示对端只是关闭了其所负责的那一条单工信道,仍然可以继续接收数据。 也就是说,因为TCP协议的限制,一个端点无法获知对端的socket是调用了close还是shutdown。
对一个已经收到FIN包的socket调用recv/read方法,如果接收缓冲已空,则返回0,这就是常说的表示连接关闭。但第一次对其调用send/write方法时,如果发送缓冲没问题,会返回正确写入(发送)。 但发送的报文会导致对端发送RST报文,因为对端的socket已经调用了close,完全关闭,既不发送,也不接收数据。 所以,第二次调用write方法(假设在收到RST之后),会生成SIGPIPE信号,导致进程退出。

场景二:

在linux下写socket的程序的时候,如果尝试send到一个disconnected socket上,就会让底层抛出一个SIGPIPE信号。
产生这个信号的原因是:
client端通过 pipe 发送信息到server端后,就关闭client端,这时server端,返回信息给 client 端时就产生Broken pipe 信号了,服务器就会被系统结束了。

解决方案

  1. send(fd,buf,length,MSG_NOSIGNAL) ,不向系统发送SIGPIPE信号,程序不退出。
  2. 可以捕获SIGPIPE信号,或者忽略它,给它设置SIG_IGN信号处理函数:
    signal(SIGPIPE,SIG_IGN);
    这样,第二次调用send/write方法时,会返回-1,同时errno置为SIGPIPE,程序便能知道对端已经关闭。

解决方案

  1. 不向系统发送SIGPIPE信号,程序不退出:
send(fd, buf, length, MSG_NOSIGNAL)
  1. 对于产生信号,我们可以在产生信号前利用方法 signal(int signum,sighandler_t handler) 设置信号的处理。如果没有调用此方法,系统就会调用默认处理方法:中止程序,显示提示信息(就是我们经常遇到的问题)。我们可以调用系统的处理方法,也可以自定义处理方法。 可以捕获SIGPIPE信号,或者忽略它,给它设置SIG_IGN信号处理函数:
signal(SIGPIPE, SIG_IGN)

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

关于 signal 函数

    #include <signal.h>
    void (*signal(int signo, void (*func)(int))) (int);
    返回值:若成功则返回信号以前的处理配置,否则返回SIG_ERR
    signo:是表10-1中的信号名。
    func:是常量SIG_IGN、常量SIG_DFL或者当接到此信号后要调用的函数的地址。
        SIG_IGN:向内核表示忽略此信号,但是SIGKILL、SIGSTOP不可忽略;
        SIG_DFL:表示接到此信号后的动作是系统默认的;
        函数地址:当信号发生时,调用该函数,即"捕获"该信号。该函数即为信号处理程序或信号捕获函数。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值