我有一个小的服务器程序,它接受TCP或本地UNIX套接字上的连接,读取一个简单的命令并(取决于命令)发送一个应答。
问题是客户可能对答案不感兴趣,有时会提前退出。因此写入该套接字将导致SIGPIPE并使我的服务器崩溃。
在这里防止崩溃的最佳实践是什么?有没有办法检查另一边是否还在阅读?(select()似乎在这里不起作用,因为它总是说套接字是可写的)。或者我应该用处理程序捕获SIGPIPE并忽略它?
我有一个小的服务器程序,它接受TCP或本地UNIX套接字上的连接,读取一个简单的命令并(取决于命令)发送一个应答。
问题是客户可能对答案不感兴趣,有时会提前退出。因此写入该套接字将导致SIGPIPE并使我的服务器崩溃。
在这里防止崩溃的最佳实践是什么?有没有办法检查另一边是否还在阅读?(select()似乎在这里不起作用,因为它总是说套接字是可写的)。或者我应该用处理程序捕获SIGPIPE并忽略它?
当前回答
我加入这个聚会非常晚,但是SO_NOSIGPIPE是不可移植的,并且可能无法在您的系统上工作(这似乎是BSD的事情)。
如果您在一个没有SO_NOSIGPIPE的Linux系统上,可以在send(2)调用上设置MSG_NOSIGNAL标志。
示例用send(…,MSG_NOSIGNAL)替换write(…)(参见nobar的注释)
char buf[888];
//write( sockfd, buf, sizeof(buf) );
send( sockfd, buf, sizeof(buf), MSG_NOSIGNAL );
其他回答
我加入这个聚会非常晚,但是SO_NOSIGPIPE是不可移植的,并且可能无法在您的系统上工作(这似乎是BSD的事情)。
如果您在一个没有SO_NOSIGPIPE的Linux系统上,可以在send(2)调用上设置MSG_NOSIGNAL标志。
示例用send(…,MSG_NOSIGNAL)替换write(…)(参见nobar的注释)
char buf[888];
//write( sockfd, buf, sizeof(buf) );
send( sockfd, buf, sizeof(buf), MSG_NOSIGNAL );
Linux手册说:
EPIPE面向连接的本端已经关闭 套接字。在这种情况下,进程还将接收一个SIGPIPE 除非设置了MSG_NOSIGNAL。
但是对于Ubuntu 12.04来说,这是不对的。我为这种情况编写了一个测试,我总是收到没有SIGPIPE的EPIPE。如果我试图第二次写入同一个损坏的套接字,就会生成SIGPIPE。所以你不需要忽略SIGPIPE,如果这个信号发生了,这意味着你的程序中有逻辑错误。
另一种方法是改变套接字,这样它就不会在write()时生成SIGPIPE。这在库中更方便,因为在库中您可能不需要SIGPIPE的全局信号处理程序。
在大多数基于bsd (MacOS, FreeBSD…)的系统上,(假设你使用的是C/ c++),你可以通过以下方法做到这一点:
int set = 1;
setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
这样,就不会生成SIGPIPE信号,而是返回EPIPE信号。
You cannot prevent the process on the far end of a pipe from exiting, and if it exits before you've finished writing, you will get a SIGPIPE signal. If you SIG_IGN the signal, then your write will return with an error - and you need to note and react to that error. Just catching and ignoring the signal in a handler is not a good idea -- you must note that the pipe is now defunct and modify the program's behaviour so it does not write to the pipe again (because the signal will be generated again, and ignored again, and you'll try again, and the whole process could go on for a long time and waste a lot of CPU power).
您通常希望忽略SIGPIPE并直接在代码中处理错误。这是因为C语言中的信号处理程序对它们能做的事情有很多限制。
最可移植的方法是将SIGPIPE处理程序设置为SIG_IGN。这将防止任何套接字或管道写入导致SIGPIPE信号。
要忽略SIGPIPE信号,请使用以下代码:
signal(SIGPIPE, SIG_IGN);
如果使用send()调用,另一个选项是使用MSG_NOSIGNAL选项,该选项将在每个调用的基础上关闭SIGPIPE行为。注意,并非所有操作系统都支持MSG_NOSIGNAL标志。
最后,您可能还想考虑SO_SIGNOPIPE套接字标志,该标志可以在某些操作系统上使用setsockopt()设置。这将防止SIGPIPE仅仅因为写入它所设置的套接字而引起。