Uno dei miei progetti su Linux utilizza socket di blocco. Le cose accadono molto seriamente, quindi il non-blocco renderebbe le cose più complicate. Ad ogni modo, sto riscontrando che spesso una chiamata recv()
sta restituendo -1
con errno
impostato su EAGAIN
.Blocco socket restituisce EAGAIN
La pagina man
menziona davvero questo evento solo per socket non bloccanti, il che ha senso. Con il non blocco, il socket potrebbe non essere disponibile, quindi potrebbe essere necessario riprovare.
Cosa potrebbe accadere per una presa di blocco? Posso fare qualcosa per evitarlo?
Al momento, il mio codice a che fare con esso sembra qualcosa di simile (l'ho un'eccezione in caso di errore, ma al di là che è una molto semplice wrapper 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;
È corretto? La condizione EAGAIN
viene colpita abbastanza spesso.
MODIFICA: alcune cose che ho notato che possono essere rilevanti.
faccio a impostare un timeout di lettura sul socket utilizzando
setsockopts()
, ma è impostato a 30 secondi. ilEAGAIN
avviene più spesso di una volta ogni 30 secondi. CORREZIONE il mio debugging era imperfetto,EAGAIN
non succede tutte le volte che pensavo di averlo fatto. Forse è l'attivazione del timeout.Per la connessione, voglio essere in grado di avere un timeout di connessione, quindi ho temporaneamente impostato il socket su non-blocking. Quel codice simile a questo:
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'idea è che ho impostato come non-bloccante, tentare una connessione e selezionare sulla presa in modo da poter rispettare un timeout. Sia il set che il ripristino delle chiamate fcntl()
ritornano correttamente, quindi il socket dovrebbe tornare alla modalità di blocco quando questa funzione sarà completata.
sì, ma è impostato su 30000 millisecondi, ho ottenuto * il modo * di EAGAIN più spesso di quello. Abbastanza decisamente. –
* CORREZIONE * il mio debugging era difettoso, EAGAIN non succede più spesso come pensavo. Forse è l'attivazione del timeout. –