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.
he puesto un tiempo de espera de lectura en el zócalo usando
setsockopts()
, pero se establece en 30 segundos. elEAGAIN
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.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.
sí, pero está configurado en 30000 milisegundos, obtengo el * modo * de EAGAIN * más a menudo que eso. Bastante constatnly. –
* 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. –