Я написал сервер, который принимает подключение к сокету через вторичный порт для потоковой отладочной информации, которая обычно переходит на stderr
.Этот второй порт - порт обслуживания ошибок - предназначен только для одного соединения за раз, что удобно, поскольку позволяет перенаправить stderr
с помощью вызова dup2(2)
.(См. Можно ли перенаправить stderr родительского процесса в дескриптор файла сокета на разветвленном процессе? ).
Следующий код почти удовлетворителен во всех отношениях.Когда клиент входит в порт, поток stderr
направляется в сокет.Когда другой клиент входит в систему, поток снова перенаправляется, и первый клиент перестает получать: вполне удовлетворительно.
В проекте не хватает места, когда клиент закрывает соединение, сервер падает, потому что он пытаетсяна write()
на сокет, который закрыт.
У меня есть элементарный обработчик сигналов для нормальных дочерних процессов, но я не уверен, как обрабатывать определенный сигнал от родительского процесса при ошибкерозетка закрывается.
Как я могу перехватить сигнал (в родительском элементе), что соединение на ERR_PORT_NUM закрыто и обработчик сигнала повторно открывает (или dup
) stderr обратно к /dev/null
для следующего ожиданияошибка клиента?
Кроме того, что я должен делать с исходным соединением клиента ошибки при подключении второго?В настоящее время первый клиент остался болтаться.Допустимо даже незаметное отключение первого соединения.
#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, '\0', sizeof(serv_addr));
memset((char *) &err_serv_addr, '\0', 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;
}