2009-04-09 7 views
6

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.

  1. Je ne fixent un délai d'attente de lecture sur la prise en utilisant setsockopts(), mais il est de 30 secondes. le EAGAIN 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.

Répondre

19

Il est possible que vous disposez d'un non nul délai de réception sur la socket (via setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,...)) car cela aussi causer recv pour revenir EAGAIN

+0

oui, mais il est fixé à 30000 millisecondes, je reçois le * way * EAGAIN plus souvent que cela. Assez souvent. –

+0

* 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. –

1

Est-il possible que vous utilisiez MSG_DONTWAIT dans vos drapeaux? La page man indique EAGAIN si aucune donnée n'est disponible et que ce drapeau est spécifié.

Si vous voulez vraiment forcer un bloc jusqu'à ce que le recv() soit un peu réussi, vous pouvez utiliser le drapeau MSG_WAITALL.

+0

J'ai juste grepped mon arbre source, MSG_DONTWAIT n'est pas utilisé. –

0

Je ne suggère pas cela comme une première tentative de correction, mais si vous êtes à court d'options, vous pouvez toujours select() sur le socket avec un délai d'attente raisonnablement long pour le forcer à attendre les données.

0

EAGAIN est généré par le système d'exploitation presque comme un « Oops! Je suis désolé pour vous déranger. ". Dans le cas de cette erreur, vous pouvez essayer de relire, Ce n'est pas une erreur grave ou fatale.J'ai vu ces interruptions se produire dans Linux et LynxOS n'importe où d'un jour à 100 fois par jour.

Questions connexes