2010-08-09 67 views
16

我有一個事件驅動的網絡服務器程序。該程序接受來自其他主機上其他進程的連接。同一個遠程IP上的不同端口可能會有很多短暫的連接。同時使用accept()和select()?

目前,我有一個while(1)循環,它調用accept(),然後產生一個線程來處理新的連接。讀取消息後,每個連接都將關閉。在遠程端,連接在發送消息後關閉。

我想消除設置和通過緩存打開的套接字文件描述符拆除連接的開銷。在發件人方面,這很容易 - 我只是不關閉連接,並保持它們。

在接收端,這有點困難。我知道我可以將accept()返回的FD存儲在一個結構中,並使用poll()select()監聽所有此類套接字上的消息,但我想同時通過accept()監聽所有緩存連接的新連接。

如果我使用兩個線程,一個在poll(),一個在accept(),那麼當accept()調用返回(一個新的連接被打開),我要喚醒其他線程等待舊的連接集。我知道我可以用信號和pselect()來做到這一點,但是對於一些如此簡單的事情來說,這種混亂看起來像是太多的工作。

是否有電話或優越的方法,可以讓我同時處理被打開新的連接和舊的連接發送的數據?

回答

23

我最後一次檢查,你可能只是一個插座上listen,然後selectpoll,看是否有連接進來如果是這樣,它accept。它不會阻止(但你 可能要 真的應該集O_NONBLOCK只是要確定)

+9

嗯,這是一個衆所周知的競爭條件 - 在'接受(2)如果客戶端下降的兩次系統調用之間的連接嘗試'將阻止。你*需要*監聽套接字是非阻塞的。 – 2010-08-10 00:35:25

+6

這是正確的 - 你可以在你的'select()'調用中將你的監聽文件描述符添加到'readfds'中,如果文件描述符有連接準備好,''接受()'。 @Nikolai也是正確的 - 監聽套接字應該是非阻塞的並且'accept()'調用準備處理'EAGAIN'。 – caf 2010-08-10 00:35:36

0

我把在單獨的進程(線程)的監聽器不能把事情搞得一團糟。並且在另一個上運行一個工作進程來處理現有的套接字。真的不需要非阻塞偵聽器。並且沒有線程開銷運行2個線程。

它應該這樣:你對你的監聽線程接受,直到它會返回客戶端套接字的描述符,並把它傳遞給工人這是做所有髒讀/寫的工作就可以了。

如果你要聽多個端口,不想持有每聽者,我建議一個過程,你設置你的插座O_NONBLOCK和做點事,如:

// loop through listeners here and poll'em for read, when read is successful call accept, get descriptor, pass it to worker and continue listen 
    while(1){ 
     foreach(serverSocket in ServerSockets){ 
      if(serverSocket.Poll(10, SelectRead)){ 
        clientSocket = serverSocket.Accept(); 
        // pass to worker here and release 
      } 

     } 
    } 
+0

你在那裏紡紗,不好。每個套接字一個線程的方式太多了。 – Borealid 2010-08-11 14:13:11

+0

這個紡紗有什麼不好? :)它不會吃任何CPU,除此之外它允許你在一個線程中監聽多個端口。 – hoodoos 2010-08-23 12:22:03

+0

這很糟糕,你正忙着等待每個監聽套接字上的10usec超時,這樣會吃掉CPU。最好在所有監聽套接字上同時使用'select',它會阻塞,直到至少有一個連接有傳入,然後嘗試從每個套接字中「接受」(確保,被標記爲'O_NONBLOCK',併爲'accept'準備好爲那些沒有連接的套接字返回'EAGAIN'或'EWOULDBLOCK')。 – 2012-06-28 00:50:33

5

你可以使用聽,然後使用或選擇然後調查接受

if (listen (socket_fd, Number_connection) <0) 
{ 
    perror("listen"); 
    return 1; 
} 
fd_set set; 
struct timeval timeout; 
int rv; 
FD_ZERO(&set); /* clear the set */ 
FD_SET(socket_fd, &set); /* add our file descriptor to the set */ 

timeout.tv_sec = 20; 
timeout.tv_usec = 0; 

rv = select(socket_fd + 1, &set, NULL, NULL, &timeout); 
if(rv == -1) 
{ 
    perror("select"); /* an error accured */ 
    return 1; 
} 
else if(rv == 0) 
{ 
    printf("timeout occurred (20 second) \n"); /* a timeout occured */ 
    return 1; 
} 
else 
    client_socket_fd = accept (socket_fd,(struct sockaddr *) &client_name, &client_name_len); 
+0

如果fd_set還有數據可能要讀取且不在監聽的fds,該怎麼辦? – Abhishek 2014-04-09 07:11:12