Le socket de blocage renvoie EAGAIN

Un de mes projets sur Linux utilise des sockets bloquants. Les choses se passent très sérieusement, donc le non-blocage ne ferait que compliquer les choses. Quoi qu’il en soit, je trouve que souvent, un appel recv() renvoie -1 avec errno avec EAGAIN .

La page de manuel ne mentionne que cela pour les sockets non bloquants, ce qui est logique. Avec le non-blocage, le socket peut être ou ne pas être disponible, vous devrez peut-être réessayer.

Que se passerait-il pour une prise de blocage? Puis-je faire quelque chose pour l’éviter?

Pour le moment, mon code à traiter ressemble à quelque chose comme ça (je lui jette une exception en cas d’erreur, mais au-delà c’est un wrapper très simple autour de 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 même correct? La condition EAGAIN est souvent touchée.

EDIT: certaines choses que j’ai remarquées qui peuvent être pertinentes.

  1. Je définis un délai de lecture sur le socket en utilisant setsockopts() , mais il est défini sur 30 secondes. les EAGAIN se produisent beaucoup plus souvent qu’une fois toutes les 30 secondes. CORRECTION mon débogage était défectueux, EAGAIN ne se produit pas aussi souvent que je le pensais. C’est peut-être le déclenchement du timeout.

  2. Pour me connecter, je veux pouvoir avoir un délai de connexion, alors je règle temporairement le socket sur 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 le règle sur non-blocage, tenter une connexion et sélectionnez sur le socket afin que je puisse appliquer un délai d’attente. Les appels set et restore fcntl() retournent tous les deux avec succès, de sorte que le socket doit se retrouver en mode blocage lorsque cette fonction est terminée.

Il est possible que vous ayez un timeout de réception différent de zéro sur la socket (via setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,...) ) car cela provoquerait également le retour de recv à EAGAIN

Est-il possible que vous utilisez MSG_DONTWAIT est spécifié dans le cadre de vos indicateurs? La page de manuel indique que EAGAIN apparaîtra si aucune donnée n’est disponible et que cet indicateur est spécifié.

Si vous voulez vraiment forcer un bloc jusqu’à ce que recv() réussisse, vous pouvez utiliser l’indicateur MSG_WAITALL .

Je ne suggère pas cela comme un correctif de première tentative, mais si vous êtes hors des options, vous pouvez toujours select() sur le socket avec un délai d’attente assez long pour le forcer à attendre des données.

EAGAIN est généré par le système d’exploitation presque comme un “Oups! Je suis désolé de vous déranger.”. En cas de cette erreur, vous pouvez essayer à nouveau de lire, ce n’est pas une erreur grave ou fatale. J’ai vu ces interruptions se produire dans Linux et LynxOS entre 1 et 100 fois par jour.