L'un de mes projets sur Linux utilise des sockets de blocage. Les choses se passent très en série, donc le non-blocage rendrait les choses plus compliquées. Quoi qu'il en soit, je constate que souvent un appel recv()
renvoie -1
avec errno
défini sur EAGAIN
.Renvoi de la socket EAGAIN
La page man
ne mentionne vraiment que cela se produit pour les sockets non bloquantes, ce qui est logique. Si vous ne bloquez pas, le socket peut être ou ne pas être disponible. Vous devrez peut-être réessayer.
Qu'est-ce qui provoquerait l'apparition d'une socket bloquante? Puis-je faire quelque chose pour l'éviter?
Actuellement, mon code pour traiter avec elle ressemble à quelque chose comme ça (je l'ai lancer une exception en cas d'erreur, mais au-delà, il est un emballage très simple autour 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;
Est-ce que c'est même correct? La condition EAGAIN
est souvent atteinte.
EDIT: certaines choses que j'ai remarqué qui peuvent être pertinentes.
Je ne fixent un délai d'attente de lecture sur la prise en utilisant
setsockopts()
, mais il est de 30 secondes. leEAGAIN
arrive plus souvent qu'une fois toutes les 30 secondes. CORRECTION mon débogage était défectueux,EAGAIN
ne se produisent pas aussi souvent que je le pensais. C'est peut-être le déclenchement du timeout. Pour la connexion, je veux être en mesure d'avoir le délai d'attente de connexion, donc je place temporairement le socket à non-bloquant. Ce code ressemble à ceci: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'idée est que je l'ai mis à la non-blocage, une tentative de connexion et sélectionnez sur la prise afin que je puisse faire respecter un délai d'attente. Les appels fcntl()
set et restore retournent avec succès, de sorte que le socket doit se retrouver en mode de blocage lorsque cette fonction est terminée.
oui, mais il est fixé à 30000 millisecondes, je reçois le * way * EAGAIN plus souvent que cela. Assez souvent. –
* CORRECTION * mon débogage était défectueux, EAGAIN n'arrive pas aussi souvent que je le pensais. C'est peut-être le déclenchement du timeout. –