Linux服务器C代码在收到信号后停止

时间:2017-05-12 12:27:57

标签: c linux server signals signal-handling

我对这个简单的服务器代码有疑问,它会按预期工作,直到收到信号。 对于调试I,在使用以下行调用select之前打印服务器和客户端文件描述符:

fprintf(stderr,"server_socket_fd=%d client_socket_fd=%d fd_max=%d\n", server_socket_fd, client_socket_fd, fd_max);

正常运行时会继续打印

server_socket_fd=3 client_socket_fd=4 fd_max=4

但是当它收到信号时,它会打印一行

server_socket_fd=3 client_socket_fd=-1 fd_max=3

然后程序停止。

使用GDB我在signal_handler中放了一个断点,当它断开时我无法看到client_socket_fd变量,gdb说

No symbol "client_socket_fd" in current context.

它没有从signal_handler函数正确返回..如果我看后面的跟踪:

(gdb) bt
#0  0xb7fdccf9 in ?? ()
#1  0xb7e26af3 in __libc_start_main (main=0x8048bdd <main>, argc=1, argv=0xbfffef24, init=0x8049a00 <__libc_csu_init>, 
    fini=0x8049a70 <__libc_csu_fini>, rtld_fini=0xb7fed160 <_dl_fini>, stack_end=0xbfffef1c) at libc-start.c:287
#2  0x08048b01 in _start ()

我不知道如何更深入地调试。

这是主要代码:

char receive_buf[2048];
int main(int argc, char *argv[]){

    int server_socket_fd;
    int client_socket_fd = -1;
    int fd_max;

    struct sockaddr_in s_in;
    int one = 1;
    int status;

    fd_set readfds;

    int port;

    int next_option;
    const char* short_options = "hp:d:";
    const struct option long_options[] = {
        { "help",   0,  NULL,   'h'},
        { "port",   1,  NULL,   'p'},
        { "debug",   1,  NULL,   'd'},
        { NULL,     0,  NULL,   0}
    };

    program_name = argv[0];

    port = DEFAULT_PORT;
    debug = 0;

    do{
        next_option = getopt_long(argc, argv, short_options, long_options, NULL);
        switch(next_option){
            case 'h':
                print_usage(stdout, 0);
                break;
            case 'p':
                port = atoi(optarg);
                if((port < 0)||(port > 65535)){
                   fprintf(stderr, "Invalid port number (%d), using default: %d", port, DEFAULT_PORT);
                   port = DEFAULT_PORT;
                }
                break;
            case 'd':
                debug = atoi(optarg);
                if(debug < 0 || debug > 3)
                    debug = 0;
                break;
            case '?':
                print_usage(stderr, 1);
                break;
            case -1:
                break;
            default:
                abort();
        }
    }while(next_option != -1);

    /*************************  SIGNAL DEFINITIONS  ***************************/

    signal_action.sa_handler = (void *)signal_handler;
    sigemptyset(&signal_action.sa_mask);
    signal_action.sa_flags = SA_RESTART; // | SA_NOCLDSTOP;

    if(sigaction(SIGINT, &signal_action, NULL) == -1){
        fprintf(stderr, "Error setting SIGINT signal handler\n");
        exit(1);
    }

    if(sigaction(SIGTERM, &signal_action, NULL) == -1){
        fprintf(stderr, "Error setting SIGTERM signal handler\n");
        exit(1);
    }

    if(sigaction(SIGWINCH, &signal_action, NULL) == -1){
        fprintf(stderr, "Error setting SIGWINCH signal handler\n");
        exit(1);
    }

    /*  // ALSO TRIED WITH SIGNAL WITH SAME RESULT
    if(signal(SIGWINCH, signal_handler) == SIG_ERR){
        fprintf(stderr, "signal error\n");
        return 1;
    }
   */

    s_in.sin_family = PF_INET;
    s_in.sin_port = htons(port);
    s_in.sin_addr.s_addr = INADDR_ANY;

    if ((server_socket_fd = socket(s_in.sin_family, SOCK_STREAM, IPPROTO_TCP)) == -1){
        perror("Error creating socket");
        return 1;
    }


    if(setsockopt(server_socket_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1){
        perror("Error setting socket parameters");
        return 1;
    }

    ////////////////////////////////////////////
    int x;
    x=fcntl(server_socket_fd,F_GETFL,0);              // Get socket flags
    fcntl(server_socket_fd,F_SETFL,x | O_NONBLOCK);   // Add non-blocking flag
    ////////////////////////////////////////////

    if(bind(server_socket_fd, (struct sockaddr*) &s_in, sizeof(s_in)) == -1){
        perror("Error binding socket");
        return 1;
    }


    if(listen(server_socket_fd, 1) == -1){
        perror("Error creating listening socket");
        return 1;
    }else{
        printf("Server (%d) listening on port %d\n", server_socket_fd, port);
    }

    memset(receive_buf, '\0', sizeof(receive_buf));
    gettimeofday(&t_print, NULL);

    while(1){

        // SERVER
        FD_ZERO(&readfds);
        FD_SET(server_socket_fd, &readfds);
        fd_max = server_socket_fd;

        // ADD CLIENT IF CONNECTED
        if(client_socket_fd > 0){
            FD_SET(client_socket_fd, &readfds);
            if(client_socket_fd > server_socket_fd)
                fd_max = client_socket_fd;
        }

        // ADDED THIS FPRINTF TO CHECK VARIABLES  <----------------------------------
        fprintf(stderr,"server_socket_fd=%d client_socket_fd=%d fd_max=%d\n", server_socket_fd, client_socket_fd, fd_max);

        if(select(fd_max+1, &readfds, NULL, NULL, NULL) == -1){
            if(errno != EINTR){
                perror("select failed");
            }
        }

        // ACCEPT CLIENT
        if(FD_ISSET(server_socket_fd, &readfds)){

            struct sockaddr_in s_in;
            socklen_t len;

            len = sizeof(s_in);
            if((client_socket_fd = accept(server_socket_fd, (struct sockaddr*) &s_in, &len)) < 0){
                if(errno != EWOULDBLOCK){
                    perror("En accept");
                }
            }else
                printf("New client connected from %s\n", inet_ntoa(s_in.sin_addr));
        }

        // RECEIVE FROM CLIENT
        if(client_socket_fd > 0){
            if(FD_ISSET(client_socket_fd, &readfds)){
                handle_client(client_socket_fd);
            }
        }
    }

    return 0;
    }


int handle_client(int cl_fd){

    int n;

    n = recv(cl_fd, receive_buf, sizeof(receive_buf) - 1, MSG_DONTWAIT);
    if(n == 0){
        fprintf(stderr,"--------------> DEBUG: handle_client:client %d closed connection\n", cl_fd);
    }else if(n < 0){
        if(errno == EAGAIN){
            return 0;
        }else{
            fprintf(stderr,"--------------> DEBUG: handle_client: recv ERROR: client %d closed connection (errno: %d : %s)\n", cl_fd, errno, strerror(errno));
            memset(receive_buf, 0, sizeof(receive_buf));
            return -1;
        }
    }else{
        receive_buf[n] = '\0';
        fprintf(stderr, "%s\n", receive_buf);
    }
        return 0;
   }
void signal_handler(int sig){

    switch(sig){

    case SIGINT:
        exit_properly(0);
        break;
    case SIGTERM:
        exit_properly(1);
        break;
    case SIGABRT:
        fprintf(stderr, "SIGABRT signal received\n");
        break;

    case SIGWINCH:
        fprintf(stderr, "\33[2J");
        fflush(stdout);
        break;

    default:
        fprintf(stderr, "Unhandled signal %d received\n",sig);
        break;
    }
}

我不知道我还能做些什么来调试这个问题而且我被困住了。任何帮助将非常感谢!

编辑:

这是失败时的strace输出,因为你可以看到它打印(并选择使用)正确的文件描述符然后,在信号发生后,client_socket_fd是错误的,因为接受失败了EAGAIN。我已经评论了exit_properly调用以及SIGTERM和SIGINT的信号处理。对于SIGWINH信号,我什么都不做,只需返回。

STRACE输出:

write(2, "server_socket_fd=3 client_socket"..., 47server_socket_fd=3 client_socket_fd=4 fd_max=4
) = 47
select(5, [3 4], NULL, NULL, NULL)      = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
sigreturn() (mask [])                   = -1 EINTR (Interrupted system call)
accept(3, 0xbf981e7c, [16])             = -1 EAGAIN (Resource temporarily unavailable)
write(2, "server_socket_fd=3 client_socket"..., 48server_socket_fd=3 client_socket_fd=-1 fd_max=3
) = 48
select(4, [3], NULL, NULL, NULL)        = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
sigreturn() (mask [])                   = -1 EINTR (Interrupted system call)
accept(3, 0xbf981e7c, [16])             = -1 EAGAIN (Resource temporarily unavailable)
write(2, "server_socket_fd=3 client_socket"..., 48server_socket_fd=3 client_socket_fd=-1 fd_max=3
) = 48
select(4, [3], NULL, NULL, NULL

现在是signal_handler:

void signal_handler(int sig){

    switch(sig){
    /*
    case SIGINT:
        exit_properly(0); //sigint_flag = 1;
        break;
    case SIGTERM:
        exit_properly(1); //sigterm_flag = 1;
        break;

*/
    case SIGWINCH:
        //sigwinch_flag = 1;
/*
        fprintf(stderr, "\33[2J");
        fflush(stdout);
    */
        break;

    default:
        //fprintf(stderr, "Unhandled signal %d received\n",sig);
        break;
    }
}

在没有SA_RESTART标志的情况下尝试了......同样的结果......?:/

2 个答案:

答案 0 :(得分:2)

select()将被信号中断,返回-1,并将errno设置为EINTR,但您的代码无法处理此问题。 即使您使用SA_RESTART安装信号处理程序,仍有许多系统调用将被中断,返回错误条件并将errno设置为EINTR

请参阅“信号处理程序中断系统调用和库函数”  http://man7.org/linux/man-pages/man7/signal.7.html/

部分

如果选择失败,您的代码会继续检查readfds,如下所示:

if(FD_ISSET(server_socket_fd, &readfds)){

但是,如果select()失败,则传递给它的fd_set变量处于不确定状态,您不应该依赖它们的值。

相反,如果选择失败,您应该重新启动循环,例如像这样的继续声明:

  if(select(fd_max+1, &readfds, NULL, NULL, NULL) == -1){
        if(errno != EINTR){
            perror("select failed");
           //Might be severe enough to quit your program... 
        }
       continue; 
    }

答案 1 :(得分:0)

我发现解决方案仅使用时间变量。

我不知道为什么,但是信号使得选择标志server_socket_fd被读取(如果有人知道为什么要分享),就像客户端会尝试连接,并接受返回的EAGAIN错误,所以client_socket_fd变量写到-1。

我只是使用临时变量(ret)解决了这个问题,而不是将接受结果直接分配给client_socket_fd:

// ACCEPT CLIENT
        if(FD_ISSET(server_socket_fd, &readfds)){

            struct sockaddr_in s_in;
            socklen_t len;

            len = sizeof(s_in);
            if((ret = accept(server_socket_fd, (struct sockaddr*) &s_in, &len)) < 0){
                if(errno != EWOULDBLOCK){
                    perror("En accept");
                }
            }else{
                client_socket_fd = ret;
                printf("New client connected from %s\n", inet_ntoa(s_in.sin_addr));
            }
        }

只有在你确定它们没问题时才更新变量......经验教训! :)