2009-04-09 16 views
6

Uno de mis proyectos en Linux usa sockets de bloqueo. Las cosas suceden en serie, por lo que el no bloqueo simplemente complicaría las cosas. De todos modos, estoy descubriendo que a menudo una llamada recv() está devolviendo -1 con errno establecido en EAGAIN.El socket de bloqueo devuelve EAGAIN

La página man solo menciona realmente que esto sucede para las tomas sin bloqueo, lo que tiene sentido. Si no se bloquea, es posible que el socket esté o no disponible, por lo que es posible que deba volver a intentarlo.

¿Qué causaría que ocurriera un socket de bloqueo? ¿Puedo hacer algo para evitarlo?

Por el momento, mi código para hacer frente a lo que parece algo como esto (lo tengo una excepción en caso de error, pero más allá de que es un envoltorio muy simple en torno 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; 

¿Esto es incluso correcto? La condición EAGAIN se golpea con bastante frecuencia.

EDIT: algunas cosas que he notado que pueden ser relevantes.

  1. he puesto un tiempo de espera de lectura en el zócalo usando setsockopts(), pero se establece en 30 segundos. el EAGAIN ocurre mucho más de una vez cada 30 segundos. CORRECCIÓN mi depuración fue defectuosa, EAGAIN no ocurren tan a menudo como pensé que lo hicieron. Tal vez es el desencadenamiento de tiempo de espera.

  2. Para conectar, quiero poder tener tiempo de espera de conexión, por lo que temporalmente configuro el socket como no bloqueante. Ese código es el siguiente:

    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; 
    

La idea es que me puse a no bloqueante, un intento de conexión y seleccione en el zócalo para que pueda cumplir un tiempo de espera. Las llamadas set y restore fcntl() regresan con éxito, por lo que el socket debe volver a estar en modo de bloqueo cuando se complete esta función.

Respuesta

19

Es posible que usted tiene un distinto de cero reciben tiempo de espera establecido en el zócalo (a través de setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,...)) como que también causaría recv para volver EAGAIN

+0

sí, pero está configurado en 30000 milisegundos, obtengo el * modo * de EAGAIN * más a menudo que eso. Bastante constatnly. –

+0

* CORRECCIÓN * mi depuración fue defectuosa, EAGAIN no ocurre tan a menudo como pensé que lo hicieron. Tal vez es el desencadenamiento de tiempo de espera. –

1

¿Es posible que esté usando MSG_DONTWAIT se está especificando como parte de sus banderas? La página man dice EAGAIN si no hay datos disponibles y esta bandera está especificada.

Si realmente quiere forzar un bloque hasta que el recv() sea algo exitoso, puede usar el indicador MSG_WAITALL.

+0

Acabo de grepped mi árbol fuente, MSG_DONTWAIT no se utiliza. –

0

No sugiero esto como una solución de primer intento, pero si no tiene opciones, siempre puede select() en el socket con un tiempo de espera razonablemente largo para obligarlo a esperar datos.

0

EAGAIN es generado por el sistema operativo casi como un "Vaya! Lo siento por molestarte ". En caso de este error, puede intentar leer de nuevo. Este no es un error grave o fatal.He visto estas interrupciones en Linux y LynxOS desde uno al día hasta 100 veces al día.

Cuestiones relacionadas