2012-07-28 89 views
2

我試圖在C++/Linux中實現一個定期從終端接收用戶輸入的服務器。最初我已經實現了兩個獨立的線程來處理這種行爲。但是我意識到,如果用戶想關閉服務器,我需要像pthread_cancel這樣的取消服務器線程。我可以使用select來結合stdin和accept嗎?

然後我決定在同一個線程中處理兩個動作可能會更好,所以我不必擔心資源泄漏。所以我現在有一個'select'調用,它可以選擇stdin fd以及我接受的fd。我的代碼看起來像這樣...

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

由於某種原因,我不再能夠讀取來自stdin的輸入。這工作正常,當我從我的fd集合中刪除兩個fds中的任何一個時,其他ome按預期執行。但是,當我將它們都放入時,acceptfd仍然接受傳入連接,但stdinfd無法響應終端輸入。

有沒有人知道我可能會在這裏做錯嗎?這種方法本質上是否有缺陷?我是否應該專注於將這兩項行動作爲單獨的線索並找出一種乾淨地退出的方式?

感謝您的閱讀!

+0

這絕對是可能的muliplex標準輸入和聽FD。究竟發生了什麼?在中間插入一些printf來查看。很可能您忘記將文件描述符設置爲非阻塞模式,而您的read()或accept()塊將不會立即返回。 – 2012-07-28 21:38:33

回答

2

正如Ambroz所評論的,複用stdin和一些聽過的fd是可能的。

但是select是一個陳舊的系統調用,你應該更喜歡使用poll(2)。如果你堅持仍然使用系統調用select(2),你應該首先在FD_ZERO內清除readfds。並且FD_SET宏應位於while循環內,因爲允許select修改readfds

poll系統調用最好select因爲select並處以有線的限制文件的數量描述符的進程可以(通常是1024,而今天能夠處理更大數量的FDS,例如65536內核)。換句話說,select要求每個fd是< 1024(今天是錯誤的)。 poll能夠處理任何一組fd。 poll的第一個參數是一個數組(如果您願意,您可以使用calloc),它的大小是要複用的fds的數量。在你的情況下,它是兩個(stdin和第二個監聽fd),所以你可以把它變成一個局部變量。每次致電poll之前,務必清除並初始化它。

你可以用調試器調試像gdb或只使用strace

+0

感謝您的即時回覆。我一定會考慮使用民意調查。據我所知,爲了使用民意調查,我需要靜態地知道我的民意調查組的大小。它是否正確?這就是爲什麼我使用select,而我可以動態地找出哪些fds我想添加到我的設置。 我認爲這可能是我的套接字仍處於阻塞模式的問題。你會建議讓它們都是非阻塞的嗎?我試過了,它似乎沒有效果,但我會繼續尋找原因。再次感謝! – spervez 2012-07-28 21:46:55

+0

好的,問題解決了!我需要在while循環中有我的代碼。花了我2個小時的生活,我永遠不會回來。 :) 感謝大家! – spervez 2012-07-28 21:57:59

1

epoll代碼工作對我來說:

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

} 
相關問題