recv()
调用返回-1
,并且errno
设置为EAGAIN
。
man
页面只提到这种情况发生在非阻塞套接字上,这是有道理的。对于非阻塞套接字,套接字可能可用,也可能不可用,因此您可能需要再试一次。什么会导致阻塞套接字发生这种情况?我能做些什么来避免它发生?
目前,处理它的代码看起来像这样(我在错误时抛出异常,但除此之外,它只是
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;
这是否正确? EAGAIN
条件经常被触发。
编辑: 我注意到一些可能相关的事情。
I do set a read timeout on the socket using
setsockopts()
, but it is set to 30 seconds. theEAGAIN
's happen way more often than once every 30 secs. CORRECTION my debugging was flawed,EAGAIN
's don't happen as often as I thought they did. Perhaps it is the timeout triggering.For connecting, I want to be able to have connect timeout, so I temporarily set the socket to non-blocking. That code looks like this:
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;
我的想法是将其设置为非阻塞状态,尝试连接并在套接字上进行选择以便我可以强制执行超时。无论是设置还是恢复fcntl()
调用都返回成功,因此当此函数完成时,套接字应该再次处于阻塞模式。