Linux 如何为关闭的套接字捕获信号(`SIGPIPE`)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8829238/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 04:02:10  来源:igfitidea点击:

How can I trap a signal (`SIGPIPE`) for a socket that closes?

linuxsocketsstdio

提问by Jamie

I've written a server that accepts a socket connection on a secondary port for the purposes of streaming debugging information that normally goes to stderr. This second port --an error serving port-- is only intended to have one connection at a time, which, is convenient, because it allows to me redirect stderrusing a dup2(2)call. (See Can I redirect a parent process's stderr to a socket file descriptor on a forked process?).

我编写了一个服务器,它在辅助端口上接受套接字连接,以便流式传输通常转到stderr. 第二个端口——一个错误服务端口——一次只打算有一个连接,这很方便,因为它允许我stderr使用dup2(2)调用重定向。(请参阅我可以将父进程的 stderr 重定向到分叉进程上的套接字文件描述符吗?)。

The following code is nearly satisfactory in every regard. When a client logs into the port, the stderrstream is directed to the socket. When another client logs in, the stream is redirected again, and the first client stops receiving: entirely satisfactory.

以下代码在各方面都几乎令人满意。当客户端登录到端口时,stderr流被定向到套接字。当另一个客户端登录时,流再次重定向,第一个客户端停止接收:完全满意。

Where it falls short in the design is when the client closes the connection, the server crashes because it is trying to write()to a socket that is closed.

设计中的不足之处在于当客户端关闭连接时,服务器崩溃,因为它试图连接write()已关闭的套接字。

I've got a rudimentary signal handler for the normal child processes, but I'm not sure how to handle the specific signal from the parent process when the error socket closes.

我有一个普通子进程的基本信号处理程序,但我不确定当错误套接字关闭时如何处理来自父进程的特定信号。

How can I trap the signal (in the parent) that the connection on the ERR_PORT_NUM has closed and have the signal handler reopen (or dup) stderr back to /dev/nullfor the next awaiting error client?

如何捕获 ERR_PORT_NUM 上的连接已关闭的信号(在父级中)并使信号处理程序重新打开(或dup)stderr 返回到/dev/null下一个等待错误客户端?

Also, what should I do with an original error client connection when a second connects? Currently the first client is left dangling. Even a non-graceful shut-down of the first connection is acceptable.

另外,当第二次连接时,我应该如何处理原始错误客户端连接?目前,第一个客户端悬而未决。即使第一个连接的非正常关闭也是可以接受的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <errno.h>
#include <pwd.h>
#include <signal.h>
#include <netinet/in.h>
#include <sys/mman.h>

#define PORT_NUM 12345
#define ERR_PORT_NUM 54321

static void child_handler(int signum)
{
    switch (signum) {
        case SIGALRM:
            exit(EXIT_FAILURE);
            break;
        case SIGUSR1:
            exit(EXIT_SUCCESS);
            break;
        case SIGCHLD:
            exit(EXIT_FAILURE);
            break;
    }
}

static void daemonize(void)
{
    /* Trap signals that we expect to recieve */
    signal(SIGUSR1, child_handler);
    signal(SIGALRM, child_handler);

    signal(SIGCHLD, SIG_IGN);   /* A child process dies */
    signal(SIGTSTP, SIG_IGN);   /* Various TTY signals */
    signal(SIGTTOU, SIG_IGN);
    signal(SIGTTIN, SIG_IGN);
    signal(SIGHUP, SIG_IGN);    /* Ignore hangup signal */
    signal(SIGTERM, SIG_DFL);   /* Die on SIGTERM */

    freopen("/dev/null", "r", stdin);
    freopen("/dev/null", "w", stdout);
    freopen("/dev/null", "w", stderr);
}

static void server_work(void)
{
    int sockfd, err_sockfd;
    socklen_t clilen;
    struct sockaddr_in serv_addr, cli_addr, err_serv_addr, err_cli_addr;
    struct timeval tv = { 0 };
    int new_stderr;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    err_sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0 || err_sockfd < 0)
        return;

    memset((char *) &serv_addr, '
signal(SIGPIPE, SIG_IGN);
', sizeof(serv_addr)); memset((char *) &err_serv_addr, '##代码##', sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(PORT_NUM); err_serv_addr.sin_family = AF_INET; err_serv_addr.sin_addr.s_addr = INADDR_ANY; err_serv_addr.sin_port = htons(ERR_PORT_NUM); if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) return; if (bind (err_sockfd, (struct sockaddr *) &err_serv_addr, sizeof(err_serv_addr)) < 0) return; listen(sockfd, 5); listen(err_sockfd, 5); clilen = sizeof(cli_addr); while (1) { int maxfd; fd_set read_sockets_set; FD_ZERO(&read_sockets_set); FD_SET(sockfd, &read_sockets_set); FD_SET(err_sockfd, &read_sockets_set); maxfd = (err_sockfd > sockfd) ? err_sockfd : sockfd; if (select(maxfd + 1, &read_sockets_set, NULL, NULL, NULL) < 0) { break; } if (FD_ISSET(sockfd, &read_sockets_set)) { /* Typical process fork(2) and such ... not gremaine to the question. */ } if (FD_ISSET(err_sockfd, &read_sockets_set)) { new_stderr = accept(err_sockfd, (struct sockaddr *) &err_cli_addr, &clilen); dup2(new_stderr, STDERR_FILENO); } } close(sockfd); close(err_sockfd); return; } int main(int argc, char *argv[]) { daemonize(); /* greatly abbreviated for question */ server_work(); return 0; }

采纳答案by John Kugelman

You could simply ignore SIGPIPE. It's a useless, annoying signal.

您可以简单地忽略 SIGPIPE。这是一个无用的、烦人的信号。

##代码##

If you ignore it then your program will instead receive an EPIPEerror code from the failed write() call. This lets you handle the I/O error at a sensible place in your code rather than in some global signal handler.

如果您忽略它,那么您的程序将从失败的 write() 调用中收到一个EPIPE错误代码。这使您可以在代码中的合理位置而不是在某些全局信号处理程序中处理 I/O 错误。

EPIPE

fd is connected to a pipe or socket whose reading end is closed. When this happens the writing process will also receive a SIGPIPE signal. (Thus, the write return value is seen only if the program catches, blocks or ignores this signal.)

EPIPE

fd 连接到读取端关闭的管道或套接字。当这种情况发生时,写入进程也会收到一个 SIGPIPE 信号。(因此,只有在程序捕获、阻止或忽略此信号时才能看到写入返回值。)