我在Linux上的一个项目使用阻塞套接字。事情是非常连续地发生的,因此非阻塞只会使事情变得更加复杂。无论如何,我发现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
条件经常被击中。编辑:一些我注意到的事情可能是相关的。
setsockopts()
在套接字上设置了读取超时,但是将其设置为30秒。 EAGAIN
发生的频率比每30秒发生一次的频率高。 更正我的调试存在缺陷,EAGAIN
的发生频率不像我想象的那样频繁。也许是超时触发。 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;
我的想法是将其设置为非阻塞,尝试连接并在套接字上进行选择,以便强制超时。 set和restore
fcntl()
调用均成功返回,因此该函数完成后,套接字应再次以阻塞模式结束。 最佳答案
您可能在套接字上设置了非零的接收超时(通过setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,...)
),因为这也会导致recv返回EAGAIN
关于c++ - 堵头返回EAGAIN,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/735249/