2010-07-24 27 views
5

Ho un problema con la funzione select quando ho lavorato su un programma socket Linux. La funzione select ha funzionato bene come dice la pagina man se il client ha collegato il lato server nell'intervallo di tempo configurato dal server. Se si verifica il timeout, la funzione di selezione restituirà 0 per sempre. A quel tempo, eseguo il debug del client e trovo che il client si sia connesso al server. Ma la funzione di selezione restituisce ancora 0. Ho cercato questo problema ma non ne trovai nessuna utile. Qualcuno potrebbe sapere perché selezionare ha fatto così? La mia versione di Linux è RHEL5.4. Grazie per l'aiuto.perché select() restituisce sempre 0 dopo il primo timeout

Il codice è illustrato di seguito.

static const int maxLog = 10000; 

int main() 
{ 
    int servSock; 
    signal(SIGPIPE, SIG_IGN); 
    if((servSock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
    { 
     printf("socket create fail\n"); 
     exit(-1); 
    } 
    int val = 1; 
    if(setsockopt(servSock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))<0) 
    { 
     DieWithUserMessage("setsockopt error"); 
    } 

    struct sockaddr_in serverAddr; 
    memset(&serverAddr, 0, sizeof(serverAddr)); 
    serverAddr.sin_family = AF_INET; 
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    serverAddr.sin_port = htons(22000); 

    if(bind(servSock, (struct sockaddr *) &serverAddr, 
       sizeof(serverAddr)) < 0) 
    { 
     printf("socket bind fail\n"); 
     exit(-1); 
    } 

    if(listen(servSock, maxLog) < 0) 
    { 
     printf("listen failed\n"); 
     exit(-1); 
    } 

    fd_set read_set; 
    FD_ZERO(&read_set); 
    FD_SET(servSock, &read_set); 
    int maxfd1 = servSock + 1; 
    std::set<int> fd_readset; 

    for(;;){  
     struct timeval tv; 
     tv.tv_sec = 5; 
     int ret = select(maxfd1, &read_set, NULL, NULL, tv);  
     if(ret == 0) 
      continue; 

     if(ret < 0) 
      DieWithUserMessage("select error"); 

     if(FD_ISSET(servSock, &read_set)) 
     { 
      struct sockaddr_in clntAddr; 
      socklen_t clntAddrlen = sizeof(clntAddr); 
      int clntSock = accept(servSock, (struct sockaddr *) &clntAddr, &clntAddrlen); 
      if(clntSock < 0) 
      { 
       printf("accept failed()"); 
       exit(-1); 
      } 

      maxfd1 = 1 + (servSock>=clntSock? servSock:clntSock); 
      FD_SET(clntSock, &read_set); 
      fd_readset.insert(clntSock); 
     } 

    } 
} 
+0

consiglio generale: utilizzare sondaggio() anziché selezionare(). – Dummy00001

+0

Devi usare '& tv' invece di' tv' in 'select' –

risposta

19

La funzione 'select()' è frustrante da utilizzare; devi impostare i suoi argomenti ogni volta prima di chiamarlo perché li modifica. Quello che state vedendo è una dimostrazione di cosa succede se non impostate fd_set (s) ogni volta attorno al ciclo.

+0

Grazie per il tuo aiuto. Ho resettato il fs_set all'inizio del ciclo come suggerisci e ora funziona bene. – terry

+1

Grazie! Mi stavo strappando i capelli su questo – Frederik

+0

Sei un uomo che salva la vita !!! molte grazie. –

1

Devi riempire il tuo FD_SET ad ogni iterazione. Il modo migliore per farlo è mantenere una raccolta dei tuoi FD da qualche parte e mettere quello necessario per la chiamata selezionata in un FD_SET temporaneo.

Se è necessario gestire molti client, potrebbe essere necessario modificare la macro FD_SETSIZE (in /usr/include/sys/select.h).

Felice programmazione di rete :)

3

Hai già la risposta giusta - ri-init i fd_set s prima di ogni chiamata a select(2).

Vorrei indicarvi un'alternativa migliore: Linux fornisce la funzione epoll(4). Anche se non è standard, è molto più comodo in quanto è necessario configurare gli eventi che si aspettano solo una volta. Il kernel gestisce le tabelle degli eventi descrittori dei file per te, quindi è molto più efficiente. epoll fornisce anche funzionalità edge-triggered, in cui viene segnalato solo un cambiamento di stato su un descrittore.

Per completezza: i BSD forniscono kqueue(2), Solaris dispone di /dev/poll.

Un'altra cosa: il tuo codice ha una ben nota condizione di competizione tra un client e il server. Dai un'occhiata a Stevens UnP: Nonblocking accept.

+0

'sondaggio' è standardizzato in POSIX. –

+1

Ma non è meglio allora selezionare. –

1

Lo stesso effetto sembra accadere se non si ripristina la struttura temporale prima di ogni chiamata da selezionare.

+0

POSIX dice che la funzione 'select()' può modificare il parametro di timeout.Presumibilmente, se sei su un sistema che lo modifica, il suo valore modificato registra il tempo rimasto fino alla scadenza del timeout, che è zero se il timeout è scaduto, e quindi dopo la prima iterazione la chiamata degenera in un sondaggio perché il timeout azzerato significa "ritorna immediatamente" invece di "aspetta se necessario". –

0

Ho lo stesso problema nei miei codici simili. Ho seguito il suggerimento di fare l'inizializzazione ogni volta prima di chiamare select() e funziona. Nei codici in questo caso, il solo fatto di portare le due linee in loop lo farà funzionare.

FD_ZERO(&read_set); 
FD_SET(servSock, &read_set); 
Problemi correlati