socket的关闭检测及处理
检测socket关闭
reference SIGPIPE 信号处理整理
调用write, send, sendto等发送函数时,触发 SIGPIPE 信号,导致程序直接退出。
Program received signal SIGPIPE, Broken pipe.
0x00007ffff7af2224 in write () from /lib/x86_64-linux-gnu/libc.so.6
程序将 errno 设置为 EPIPE 之后,程序接受到内核发送过来的 SIGPIPE 信号退出。
产生 SIGPIPE 的条件:
-
对一个已经收到 FIN 包的 socket 调用 read 方法,如果接收缓冲已空,则返回 0,这就是常说的“连接关闭”表示。
-
对一个已经收到 FIN 包的 socket 第一次调用 write 方法时,如果发送缓冲没问题,则 write 调用会返回写入的数据量,同时进行数据发送。但是发送出去的报文会导致对端发回 RST 报文。因为对端的 socket 已经调用了 close 进行了完全关闭,已经处于既不发送,也不接收数据的状态。所以第二次调用 write 方法时(假设在收到 RST 之后)会生成 SIGPIPE 信号,导致进程退出(这就是为什么第二次 write 才能触发 SIGPIPE 的原因)。 <----- 这个应该是阻塞socket才这样,没有试验过
处理SIGPIPE How to prevent SIGPIPEs (or handle them properly)
-
设置信号处理函数
signal(SIGPIPE, SIG_IGN);
-
设置socket option,忽略SIGPIPE信号,转为处理对应的errno,这样就不用安装信号处理函数
int set = 1; setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
注意:在Linux中没有SO_NOSIGPIPE信号: SO_NOSIGPIPE was not declared,但是我们可以在调用send或者recv的信号带上MSG_NOSIGNAL
send(fd, buf, nBytes, MSG_NOSIGNAL);
服务端关闭socket
做了如下测试,如下代码是客户端代码片段,服务端用nc监听一个端口作为服务端
sleep(10);
char buf[32];
ret = recv(event[i].data.fd, buf, 32, MSG_NOSIGNAL);
LOG_DEBUG("recv ret: %d, errno: %d, error: %s", ret, errno, strerror(errno));
ret = send(event[i