2012-07-28 2 views
2

정기적으로 터미널에서 사용자 입력을받는 C++/Linux 서버를 구현하려고합니다. 처음에는이 동작을 처리하기 위해 두 개의 개별 스레드를 구현했습니다. 하지만 사용자가 서버를 종료하고자 할 때 서버 스레드를 취소하기 위해 pthread_cancel과 같은 것이 필요하다는 것을 알게되었습니다.select를 사용하여 stdin과 accept를 결합 할 수 있습니까?

그런 다음 동일한 스레드에서 두 작업을 모두 처리하는 것이 더 좋을 수 있으므로 리소스 유출에 대해 걱정할 필요가 없습니다. 그래서 제가 지금 가지고있는 것은 '표준'호출뿐입니다.이 호출은 표준 fd와 수락 fd를 선택합니다. 내 코드는 다음과 유사합니다 ...

fdset readfds; 
FD_SET(acceptfd, &readfds); 
FD_SET(stdinfd, &readfds); 
while(1) { 
    select(n, &readfds, NULL, NULL, NULL); 
    .... 
} 

나는 더 이상 표준 입력에서 입력을 읽을 수 없습니다. 이것은 내 fd 세트에서 두 fds 중 하나를 제거하면 잘 작동하고, 다른 ome는 예상대로 수행됩니다. 하지만 acceptfd가 들어오는 연결을 받아들이는 동안 둘 다 남겨두면 stdinfd는 터미널 입력에 응답하지 않습니다.

내가 여기 잘못하고 있을지 누가 ​​알 수 있습니까? 이 접근 방식은 본질적으로 결함이 있습니까? 두 작업을 별도의 스레드로 유지하고 대신 깔끔하게 종료하는 방법을 찾는 데 집중해야합니까?

읽어 주셔서 감사합니다.

+0

그것은 확실히 가능하다. 정확히 무슨 일이 일어나고있는거야? 몇개의 printf를 사이에 삽입하십시오. 파일 설명자를 비 블로킹 모드로 놓는 것을 잊었거나, 즉시 반환하는 대신 read() 또는 accept() 블록을 사용하지 않는 것이 좋습니다. –

답변

2

Ambroz가 주석을 달았으므로 표준 입력 다중화와 일부 청취 가능한 fd가 가능합니다.

그러나 select은 오래되었지만 거의 사용되지 않는 syscall이므로 poll(2)을 선호해야합니다. select(2) syscall을 계속 사용하고 싶다면 먼저 readfds을 지우고 루프 안에 FD_ZERO을 입력해야합니다. selectreadfds을 수정할 수 있으므로 FD_SET 매크로는 while 루프 내에 있어야합니다. select 파일의 번호로 유선에 제한을 부과하는 과정 (일반적으로 1024을 가질 수 디스크립터 때문에 커널이 예를 들어 FDS의 큰 번호, 65536 처리 할 수 ​​오늘 동안

poll 콜은 select에 바람직하다). 즉, select은 모든 fd가 < 1024 (오늘은 false) 여야합니다. poll은 모든 fd 세트를 처리 할 수 ​​있습니다. poll의 첫 번째 인수는 원하는 크기라면 calloc 일 수있는 배열입니다. 크기는 다중화하려는 fds 수입니다. 귀하의 경우에는 두 가지 (표준 및 두 번째 청취 된 fd)이므로 로컬 변수로 지정할 수 있습니다. poll으로 전화하기 전에 먼저 초기화하고 초기화하십시오.

당신은 gdb 같은 디버거를 디버깅하거나 strace

+0

신속한 답장을 보내 주셔서 감사합니다. 설문 조사를 사용하는 것이 좋습니다. 내가 아는 바에 따르면 설문 조사와 함께 작업하려면 폴링 세트의 크기를 정적으로 알아야합니다. 이 올바른지? 그래서 select 대신에 fds를 내 세트에 추가하고 싶은 부분을 동적으로 사용할 수 있습니다. 이 문제는 내 소켓이 여전히 블로킹 모드에있는 문제 일 수 있다고 생각합니다. 두 가지 모두 비 차단이라고 제안 하시겠습니까?나는 그것을 시도했다. 그리고 그것은 didnt한다 효과를 가지고있는 것처럼 보인다. 그러나 나는 이유를 찾는 것을 계속할 것이다. 다시 한 번 감사드립니다! – spervez

+0

그래, 문제가 해결되었습니다! while 루프 안에 코드를 넣어야했습니다. 내 인생의 2 시간 동안 나는 결코 돌아 오지 않을 것이다. :) 모두에게 감사드립니다! – spervez

1

epoll 코드는 나를 위해 작동 사용할 수 있습니다 : 표준 입력을 muliplex와 FD를 듣고

#include <stdlib.h> 
#include <string.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <sys/epoll.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 

#define PORT 4711 

int main(void) { 
    int sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    struct sockaddr_in addr; 
    addr.sin_family = AF_INET; 
    addr.sin_port = htons(PORT); 
    addr.sin_addr.s_addr = htons(INADDR_ANY); 
    bind(sockfd, (struct sockaddr*) &addr, sizeof (addr)); 
    listen(sockfd, 10); 

    int epollfd = epoll_create1(0); 
    struct epoll_event event; 
    // add stdin 
    event.events = EPOLLIN|EPOLLPRI|EPOLLERR; 
    event.data.fd = STDIN_FILENO; 
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &event) != 0) { 
     perror("epoll_ctr add stdin failed."); 
     return 1; 
    } 
    // add socket 
    event.events = EPOLLIN|EPOLLPRI|EPOLLERR; 
    event.data.fd = sockfd; 
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &event) != 0) { 
     perror("epoll_ctr add sockfd failed."); 
     return 1; 
    } 

    char *line = NULL; 
    size_t linelen = 0; 
    for (;;) { 
     int fds = epoll_wait(epollfd, &event, 1, -1); 
     if (fds < 0) { 
      perror("epoll_wait failed."); 
      return 2; 
     } 
     if (fds == 0) { 
      continue; 
     } 

     if (event.data.fd == STDIN_FILENO) { 
      // read input line 
      int read = getline(&line, &linelen, stdin); 
      if (read < 0) { 
       perror("could not getline"); 
       return 3; 
      } 
      printf("Read: %.*s", read, line); 
     } else if (event.data.fd == sockfd) { 
      // accept client 
      struct sockaddr_in client_addr; 
      socklen_t addrlen = sizeof (client_addr); 
      int clientfd = accept(sockfd, (struct sockaddr*) &client_addr, &addrlen); 
      if (clientfd == -1) { 
       perror("could not accept"); 
       return 4; 
      } 
      send(clientfd, "Bye", 3, 0); 
      close(clientfd); 
     } else { 
      // cannot happen™ 
      fprintf(stderr, "Bad fd: %d\n", event.data.fd); 
      return 5; 
     } 
    } 

    close(epollfd); 
    close(sockfd); 
    return 0; 

} 
관련 문제