我该如何尝试使用超时从套接字中读取数据? 我知道,select、pselect和poll都有一个超时字段,但是使用它们会禁用tcp reno堆栈中的“tcp快速路径”。
我唯一想到的办法是在循环中使用recv(fd, ..., MSG_DONTWAIT)。
您可以使用setsockopt函数在接收操作上设置超时时间:
SO_RCVTIMEO
设置超时值,指定输入函数等待完成的最长时间。它接受一个timeval结构体,其中秒和微秒数指定等待输入操作完成的限制时间。如果接收操作已经阻塞了这么长时间而没有接收到其他数据,则它将返回带有部分计数或errno设置为[EAGAIN]或[EWOULDBLOCK](如果没有接收到数据)。此选项的默认值为零,表示接收操作不会超时。这个选项需要一个timeval结构体。请注意,不是所有的实现都允许设置这个选项。
// LINUX
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
// WINDOWS
DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);
// MAC OS X (identical to Linux)
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
据报道,在 Windows 上应该在调用 bind
之前执行此操作。我通过实验验证,在 Linux 和 OS X 上无论是在 bind
之前还是之后执行都可以。
struct timeval tv;
,那么这是否意味着 select() 也无法工作?我尝试将我的 select() 代码移植到 Windows 上,但它立即超时,看起来它忽略了我设置的 timeval 值。 - kuchi这里是一些简单的代码,可以使用C中的poll
函数为您的recv
函数添加超时:
struct pollfd fd;
int ret;
fd.fd = mySocket; // your socket handler
fd.events = POLLIN;
ret = poll(&fd, 1, 1000); // 1 second for timeout
switch (ret) {
case -1:
// Error
break;
case 0:
// Timeout
break;
default:
recv(mySocket,buf,sizeof(buf), 0); // get your data
break;
}
poll
函数将等待接收至少一个字节或超时,而调用 recv
函数时,它将等待 sizeof(buf)
字节,如果此计数尚未到达,则会再次阻塞,但这次没有超时。 - LoPiTaLrecv
函数应该返回最多 sizeof(buf)
字节的任意可用数据。因此,这个解决方案应该是可行的。 - GILGAMESH// 在 Windows 上进行绑定操作后也可以使用
DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);
SIGALRM
的处理程序,然后在常规阻塞的 recv()
之前使用 alarm()
或 ualarm()
。如果闹钟响了,recv()
将返回一个带有 errno
设置为 EINTR
的错误。LINUX
struct timeval tv;
tv.tv_sec = 30; // 30 Secs Timeout
tv.tv_usec = 0; // Not init'ing this can cause strange errors
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv,sizeof(struct timeval));
WINDOWS
DWORD timeout = SOCKET_READ_TIMEOUT_SEC * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
注意:您需要在bind()
函数调用之前设置此选项以确保正确运行