0

我有一个server有 2 个连接SOCKET的连接,clients我将其设置server为非阻塞模式,在发送或接收消息时不会停止。我想为每个连接设置超时SOCKET,但如果我使用以下代码:

 void getMessage(SOCKET connectedSocket, int time){ 
    string error = R_ERROR;
    // Using select in winsock
    fd_set  set;
    timeval tm;

    FD_ZERO(&set);
    FD_SET(connectedSocket, &set);

    tm.tv_sec = time; // time 
    tm.tv_usec = 0; // 0 millis
    switch (select(connectedSocket, &set, 0, 0, &tm))
    {
    case 0:
        // timeout
        this->disconnect();
        break;
    case 1:
        // Can recieve some data here
        return this->recvMessage();
        break;
    default:
        // error - handle appropriately.
        break;
    }
return error;
}

我的服务器不再是非阻塞模式!我必须等到第一个连接超时才能从第二个连接收到消息!这不是我所期望的!那么,有没有办法为非阻塞模式设置超时?还是我必须自己处理?

4

1 回答 1

4

select是一种解复用机制。当您使用它来确定数据何时在单个套接字上准备好或超时时,它实际上被设计为在许多套接字上返回数据准备状态(因此是fd_set)。从概念上讲,它与poll,epoll和相同kqueue。结合非阻塞 I/O,这些机制为应用程序编写者提供了实现单线程并发服务器的工具。

在我看来,您的应用程序不需要那种能力。您的应用程序将只处理两个连接,并且您已经在每个连接使用一个线程。我相信让套接字处于阻塞 I/O 模式更合适。

如果您坚持非阻塞模式,我的建议是select用其他方式替换呼叫。由于您想要的select是单个套接字的读取就绪或超时的指示,因此您可以recv通过传递适当的参数并在套接字上设置适当的超时来实现类似的效果。

tm.tv_sec = time;
tm.tv_usec = 0;
setsockopt(connectedSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tm, sizeof(tm));
char c;
swtich (recv(connectedSocket, &c, 1, MSG_PEEK|MSG_WAITALL)) {
case -1:
    if (errno == EAGAIN) {
        // handle timeout ...
    } else {
        // handle other error ...
    }
    break;
case 0: // FALLTHROUGH
default:
    // handle read ready ...
    break;
}

来自man recv

MSG_PEEK——该标志使接收操作从接收队列的开头返回数据,而不从队列中删除该数据。因此,后续的接收调用将返回相同的数据。

MSG_WAITALL(自 Linux 2.2 起)——此标志请求操作阻塞,直到满足完整请求。但是,如果捕获到信号、发生错误或断开连接,或者要接收的下一个数据与返回的数据类型不同,则调用返回的数据仍可能少于请求的数据。

至于为什么select以您观察到的方式行事。虽然select调用是线程安全的,但它很可能完全防止重入。因此,一个线程的调用select只会在另一个线程的调用完成后才会出现(调用select被序列化)。这与其作为解复用器的功能是一致的。它的目的是充当连接就绪的单个仲裁器。因此,它希望由单个线程控制。

于 2012-06-13T06:27:43.613 回答