2015-05-22 15 views
9

Ho letto dalla presa utilizzando la funzione recv. Ho un problema quando nessun dato disponibile per la lettura. Il mio programma si ferma. Ho scoperto che posso impostare il timeout usando la funzione select. Ma guarda che il timeout influisce sulla funzione di selezione stessa e su recv che va dopo che la selezione è ancora in attesa ininterrottamente.Impostazione del timeout per la funzione di recv

fd_set set; 
struct timeval timeout; 
FD_ZERO(&set); /* clear the set */ 
FD_SET(s, &set); /* add our file descriptor to the set */ 
timeout.tv_sec = SOCKET_READ_TIMEOUT_SEC; 
timeout.tv_usec = 0; 
int rv = select(s, &set, NULL, NULL, &timeout); 
if((recv_size = recv(s , rx_tmp , bufSize ,0)) == SOCKET_ERROR) 
     { 
     ... 
     } 

Come chiedere recv funzione di ritorno dopo qualche timout?

risposta

6

Si dovrebbe controllare il valore di ritorno di select. select torneranno 0 in caso di timeout scaduto, così si dovrebbe verificare la presenza di errori e chiamare recv solo se select restituito valore positivo:

In caso di successo, select() e pselect() restituisce il numero di descrittori di file contenuti nel tre set di descrittori restituiti (ovvero il numero totale di bit impostati in readfds, writefds, exceptfds) che potrebbero essere zero se il timeout scade prima che accada qualcosa di interessante.

int rv = select(s + 1, &set, NULL, NULL, &timeout); 
if (rv == SOCKET_ERROR) 
{ 
    // select error... 
} 
else if (rv == 0) 
{ 
    // timeout, socket does not have anything to read 
} 
else 
{ 
    // socket has something to read 
    recv_size = recv(s, rx_tmp, bufSize, 0); 
    if (recv_size == SOCKET_ERROR) 
    { 
     // read failed... 
    } 
    else if (recv_size == 0) 
    { 
     // peer disconnected... 
    } 
    else 
    { 
     // read successful... 
    } 
} 
+0

Questo esempio di codice contiene un errore ... la linea che dice: 'int rv = seleziona (s, & set, NULL, NULL e timeout);' dovrebbe leggere 'int rv = select (s + 1, & set, NULL, NULL, e timeout);' – drbobdugan

+0

Grazie mille, tu hai ragione davvero! 'nfds' è il descrittore di file con il numero più alto + 1. –

1

utilizzare la macro FD_ISSET() per verificare se sono presenti dati da leggere. Se restituisce false, non eseguire la lettura.

http://linux.die.net/man/3/fd_set

21

Un altro modo per impostare un timeout recv() stessa senza utilizzare select() è quello di utilizzare setsockopt() per impostare l'opzione del socket SO_RCVTIMEO (sulle piattaforme che lo supportano).

Su Windows, il codice sarebbe simile a questa:

DWORD timeout = SOCKET_READ_TIMEOUT_SEC * 1000; 
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); 

//... 

recv_size = recv(s, rx_tmp, bufSize, 0); 
if (recv_size == SOCKET_ERROR) 
{ 
    if (WSAGetLastError() != WSAETIMEDOUT) 
     //... 
} 

Su altre piattaforme, il codice sarebbe simile a questo, invece:

struct timeval timeout; 
timeout.tv_sec = SOCKET_READ_TIMEOUT_SEC; 
timeout.tv_usec = 0; 
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); 

//... 

recv_size = recv(s, rx_tmp, bufSize, 0); 
if (recv_size == -1) 
{ 
    if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) 
     //... 
} 
+1

FYI Questa soluzione funziona solo su piattaforme Windows, non può essere utilizzata su piattaforme Unix/Linux/OSX. – drbobdugan

+1

@drbobdugan: Windows non è l'unica piattaforma che supporta 'SO_RCVTIMEO', anche se su altre piattaforme il parametro di input è in genere una struttura' timeval'. [Linux implementa 'SO_RCVTIMEO' (e' SO_SNDTIMEO')] (http://man7.org/linux/man-pages/man7/socket.7.html). [Così fa OSX] (https://developer.apple.com/library/ios/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setsockopt.2.html). –

+0

la tua soluzione non funzionerà su una piattaforma Linux/Unix/OSX perché contiene frammenti di codice come DWORD, WSAGetLastError() che sono artefatti di Windows. – drbobdugan

Problemi correlati