我有一个小的服务器程序,它接受TCP或本地UNIX套接字上的连接,读取一个简单的命令并(取决于命令)发送一个应答。

问题是客户可能对答案不感兴趣,有时会提前退出。因此写入该套接字将导致SIGPIPE并使我的服务器崩溃。

在这里防止崩溃的最佳实践是什么?有没有办法检查另一边是否还在阅读?(select()似乎在这里不起作用,因为它总是说套接字是可写的)。或者我应该用处理程序捕获SIGPIPE并忽略它?


当前回答

或者我应该用处理程序捕获SIGPIPE并忽略它?

我相信这是对的。您想知道另一端什么时候关闭了它们的描述符,这就是SIGPIPE告诉您的。

Sam

其他回答

我加入这个聚会非常晚,但是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 );

您通常希望忽略SIGPIPE并直接在代码中处理错误。这是因为C语言中的信号处理程序对它们能做的事情有很多限制。

最可移植的方法是将SIGPIPE处理程序设置为SIG_IGN。这将防止任何套接字或管道写入导致SIGPIPE信号。

要忽略SIGPIPE信号,请使用以下代码:

signal(SIGPIPE, SIG_IGN);

如果使用send()调用,另一个选项是使用MSG_NOSIGNAL选项,该选项将在每个调用的基础上关闭SIGPIPE行为。注意,并非所有操作系统都支持MSG_NOSIGNAL标志。

最后,您可能还想考虑SO_SIGNOPIPE套接字标志,该标志可以在某些操作系统上使用setsockopt()设置。这将防止SIGPIPE仅仅因为写入它所设置的套接字而引起。

或者我应该用处理程序捕获SIGPIPE并忽略它?

我相信这是对的。您想知道另一端什么时候关闭了它们的描述符,这就是SIGPIPE告诉您的。

Sam

本地处理SIGPIPE

通常最好在本地处理错误,而不是在全局信号事件处理程序中处理错误,因为在本地您将有更多关于正在发生什么以及采取什么求助的上下文。

我在我的一个应用程序中有一个通信层,它允许我的应用程序与外部附件通信。当发生写错误时,我在通信层抛出一个异常,并让它冒泡到try catch块来处理它。

代码:

忽略SIGPIPE信号以便在本地处理它的代码是:

// We expect write failures to occur but we want to handle them where 
// the error occurs rather than in a SIGPIPE handler.
signal(SIGPIPE, SIG_IGN);

这段代码将防止引发SIGPIPE信号,但是在尝试使用套接字时将会得到一个读/写错误,因此需要检查这一点。

Linux手册说:

EPIPE面向连接的本端已经关闭 套接字。在这种情况下,进程还将接收一个SIGPIPE 除非设置了MSG_NOSIGNAL。

但是对于Ubuntu 12.04来说,这是不对的。我为这种情况编写了一个测试,我总是收到没有SIGPIPE的EPIPE。如果我试图第二次写入同一个损坏的套接字,就会生成SIGPIPE。所以你不需要忽略SIGPIPE,如果这个信号发生了,这意味着你的程序中有逻辑错误。