2017-05-12 7 views
0

이 간단한 서버 코드에 문제가 있습니다. 신호를 수신 할 때까지 예상대로 작동합니다. 일반적으로리눅스 서버 C 코드가 신호 수신 후 멈춤

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; 
    } 
} 

나는이 문제를 디버깅 할 내가 끼 었어 수있는 다른 모르겠어요 :

이 주요 코드입니다. 어떤 도움을 주시면 감사하겠습니다!

편집 : 실패 할 때 당신이 인쇄 참조 (및 용도를 선택) 올바른 파일 설명을하고 신호가 발생한 후 다음의 client_socket_fd 때문에 실패 동의를 잘못 수

이것은의 strace를 출력입니다 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 :

또한 SA_RESTART 플래그 ... 동일한 결과없이 시도
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; 
    } 
} 

... :/

+0

fprintf()는 비동기 안전하지 않습니다. – EOF

+1

문제가 발생하면 프로그램에서 어떤 특정 신호가 발생합니까? 디버깅을 위해서조차도 fprintf()를 시그널 핸들러에서 사용하면 안되며, 프로그램이 교착 상태에 빠질 수 있습니다 (문제가 원인 일 수 있음). 예 : write()를 직접적으로 사용하지만 FILE *에서 작동하는 함수를 사용하지 않거나 그렇지 않으면 신호 처리기를 호출하기에 안전하지 않은 함수를 사용하지 마십시오. strace 도구 (예 : strace -f ./myprogram)를 사용하여 실행할 경우 프로그램이 수행하는 작업에 대한 단서를 얻을 수도 있습니다. 이는 어떤 시스템 콜이 – nos

+0

에 머물러 있는지 알려주고 exit_properly 무엇입니까? 방금 SA_RESTART를 설정하는 것은 좋은 생각이 아닙니다. 신호를 받으면 select 문에 영향을 주어 영원히 기다려 무한정 계속할 수 있습니다. exit_properly는 정상적인 종료를 위해 어떤 이유로이 선택 항목이 죽을 때까지 기다릴 수 있습니다 (누락 된 기호를 설명 할 수 있음). 그냥 여기에 야생 짐작은 – Aviv

답변

2

select()은 신호로 인터럽트되고 -1을 반환하고 errno를 EINTR로 설정하지만 코드가이를 처리하지 못합니다. SA_RESTART과 함께 신호 처리기를 설치하더라도 인터럽트되는 시스템 호출 수가 여전히 있으며 오류 조건을 반환하고 errno를 EINTR으로 설정합니다. 그러나

if(FD_ISSET(server_socket_fd, &readfds)){ 

:

은 당신의 코드는 다음과 같은 readfds을 확인하기 위해 계속, 섹션 http://man7.org/linux/man-pages/man7/signal.7.html/

선택이 실패 할 경우에 "시그널 핸들러에 의해 시스템 호출과 라이브러리 함수의 중단"을 참조하십시오 select()가 실패하면, 변수에 전달하는 변수 fd_set은 비 결정적 상태에 있으므로 값에 의존해서는 안됩니다.

대신 select가 실패하면 예를 들어 루프를 다시 시작해야합니다. 다음과 같은 continue 문 :

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; 
    } 
+0

맞습니다. 선택에 실패하면 계속해서는 안됩니다. 나는 코드를 업데이트 할 것이다. 감사! (링크가 깨졌습니다.) –

+0

아주 좋은 통찰력은 리눅스 man 페이지를 읽는 동안보고 싶습니다. – Aviv

+0

@japjap이 대답이 EAGIAN 신호 문제를 해결했는지 알려주겠습니다. – Aviv

0

I는 솔루션을 발견 단지 시간 변수를 사용합니다.

나는 왜 그런지 알지는 못한다. 그러나 클라이언트가 연결을 시도하는 것처럼 서버 선택 플래그에 server_socket_fd를 플래그로 표시하고, 반환 된 EAGAIN 오류를 허용하므로 client_socket_fd 변수가 -1로 기록되었습니다.

// 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)); 
      } 
     } 

만 변수를 갱신 당신은 그들이 OK ... 교훈을 배운 있는지 때 :

나는이 단순히 임시 변수 (RET)를 사용하는 대신이 client_socket_fd 직접 결과를 받아 들일 할당 해결! :)

+0

먼저이 함수에서 오류를 검사하면 EAGAIN 및 EWOULDBLOCK도 검사해야합니다. 올라와. 둘째, 클라이언트 코드를 처리 한 후 다음 클라이언트를 기다리는 것 같습니다. 그렇다면 모든 handle_client 호출 후에 수동으로 소켓을 닫아야합니다. 이제 이전 client_socket_fd를 계속 읽을 수 있습니다. strace에있는 오류는 비 차단 소켓을 사용할 때만 발생해야합니다. 하지만 당신은 몰라요. 어쩌면 명시 적으로 설정 해제해야합니까? – Aviv

+0

@Aviv 이것은 클라이언트를 테스트하기위한 빠른 더러운 코드이므로 이식성에 대해서는 신경 쓰지 않고 내 시스템에서는 EAGAIN이 실제로 EWOULDBLOCK을 정의합니다. 같은 이유로 서버는 하나의 클라이언트 만 허용합니다. –

+0

Jus는 소켓이 닫혀 있지 않기 때문에 EAGAIN이 시작되었다고 생각했습니다. 외부 적으로 닫히고 신호로 인해 서버가 서버를 닫지 못하게되어 리소스를 사용할 수 없게됩니다. 그냥 추측. 수동으로 닫으면 무시됩니다. – Aviv