我在Linux上的一个项目使用阻塞套接字。事情发生的非常连续,所以非阻塞会让事情变得更加复杂。无论如何,我发现通常recv()
呼叫返回-1
与errno
设置为EAGAIN
。阻塞套接字返回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秒。每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;
的想法是,我将它设置为非阻塞,尝试连接,并选择在插座上,所以我可以强制超时。设置和恢复fcntl()
调用都会成功返回,所以当此函数完成时,套接字应该再次处于阻止模式。
是的,但它设置为30000毫秒,我比EAGAIN的*路*更频繁。非常常态。 – 2009-04-09 18:24:12