2009-04-09 25 views
6

Uno dei miei progetti su Linux utilizza socket di blocco. Le cose accadono molto seriamente, quindi il non-blocco renderebbe le cose più complicate. Ad ogni modo, sto riscontrando che spesso una chiamata recv() sta restituendo -1 con errno impostato su EAGAIN.Blocco socket restituisce EAGAIN

La pagina man menziona davvero questo evento solo per socket non bloccanti, il che ha senso. Con il non blocco, il socket potrebbe non essere disponibile, quindi potrebbe essere necessario riprovare.

Cosa potrebbe accadere per una presa di blocco? Posso fare qualcosa per evitarlo?

Al momento, il mio codice a che fare con esso sembra qualcosa di simile (l'ho un'eccezione in caso di errore, ma al di là che è una molto semplice wrapper recv()):

int ret; 
do { 
    ret = ::recv(socket, buf, len, flags | MSG_NOSIGNAL); 
} while(ret == -1 && errno == EAGAIN); 


if(ret == -1) { 
    throw socket_error(strerror(errno)); 
} 
return ret; 

È corretto? La condizione EAGAIN viene colpita abbastanza spesso.

MODIFICA: alcune cose che ho notato che possono essere rilevanti.

  1. faccio a impostare un timeout di lettura sul socket utilizzando setsockopts(), ma è impostato a 30 secondi. il EAGAIN avviene più spesso di una volta ogni 30 secondi. CORREZIONE il mio debugging era imperfetto, EAGAIN non succede tutte le volte che pensavo di averlo fatto. Forse è l'attivazione del timeout.

  2. Per la connessione, voglio essere in grado di avere un timeout di connessione, quindi ho temporaneamente impostato il socket su non-blocking. Quel codice simile a questo:

    int  error = 0; 
    fd_set rset; 
    fd_set wset; 
    int  n; 
    const SOCKET sock = m_Socket; 
    
    // set the socket as nonblocking IO 
    const int flags = fcntl (sock, F_GETFL, 0); 
    fcntl(sock, F_SETFL, flags | O_NONBLOCK); 
    
    errno = 0; 
    
    // we connect, but it will return soon 
    n = ::connect(sock, addr, size_addr); 
    
    if(n < 0) { 
        if (errno != EINPROGRESS) { 
         return -1; 
        } 
    } else if (n == 0) { 
        goto done; 
    } 
    
    FD_ZERO(&rset); 
    FD_ZERO(&wset); 
    FD_SET(sock, &rset); 
    FD_SET(sock, &wset); 
    
    struct timeval tval; 
    tval.tv_sec = timeout; 
    tval.tv_usec = 0; 
    
    // We "select()" until connect() returns its result or timeout 
    n = select(sock + 1, &rset, &wset, 0, timeout ? &tval : 0); 
    if(n == 0) {  
        errno = ETIMEDOUT; 
        return -1; 
    } 
    
    if (FD_ISSET(sock, &rset) || FD_ISSET(sock, &wset)) { 
        socklen_t len = sizeof(error); 
        if (getsockopt(SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 
         return -1; 
        } 
    } else { 
        return -1; 
    } 
    
    done: 
    // We change the socket options back to blocking IO 
    if (fcntl(sock, F_SETFL, flags) == -1) { 
        return -1; 
    } 
    return 0; 
    

L'idea è che ho impostato come non-bloccante, tentare una connessione e selezionare sulla presa in modo da poter rispettare un timeout. Sia il set che il ripristino delle chiamate fcntl() ritornano correttamente, quindi il socket dovrebbe tornare alla modalità di blocco quando questa funzione sarà completata.

risposta

19

E 'possibile che si dispone di un non nulla riceve timeout impostato sulla presa (via setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,...)) come che anche causare recv per tornare EAGAIN

+0

sì, ma è impostato su 30000 millisecondi, ho ottenuto * il modo * di EAGAIN più spesso di quello. Abbastanza decisamente. –

+0

* CORREZIONE * il mio debugging era difettoso, EAGAIN non succede più spesso come pensavo. Forse è l'attivazione del timeout. –

1

È possibile che si stia utilizzando MSG_DONTWAIT come parte delle bandiere? La pagina man dice EAGAIN si verificherà se nessun dato è disponibile e questo flag è specificato.

Se si desidera forzare un blocco fino a quando lo recv() ha avuto esito positivo, è possibile utilizzare il flag MSG_WAITALL.

+0

Ho appena fatto un grepping dell'albero dei sorgenti, MSG_DONTWAIT non è utilizzato. –

0

Non lo consiglio come soluzione per il primo tentativo, ma se non si dispone di tutte le opzioni, è possibile sempre select() sul socket con un timeout ragionevolmente lungo per forzare l'attesa dei dati.

0

EAGAIN è generato dal sistema operativo quasi come un "Oops! Mi dispiace per disturbarti. ". In caso di questo errore, puoi provare a leggere di nuovo, questo non è un errore grave o fatale.Ho visto questi interrupt verificarsi in Linux e LynxOS ovunque da un giorno a 100 volte al giorno.

Problemi correlati