如何设置从非阻塞模式的服务器客户端接收消息的超时时间?

时间:2012-06-13 05:14:40

标签: c++ winsock

我有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;
}

我的服务器不再是非阻塞模式了!我必须等到第一次连接结束时才从第二个连接获取消息!那不是我的期望!那么,有没有办法为非阻塞模式设置超时?或者我必须自己处理它?<​​/ p>

1 个答案:

答案 0 :(得分:4)

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

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

如果您坚持使用非阻止模式,我的建议是用其他内容替换select来电。由于您希望select显示单个套接字的读取就绪或超时,因此可以通过使用适当参数传递的recv以及套接字上的相应超时设置来实现类似的效果。 / p>

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的调用被序列化)。这与其作为解复用器的功能一致。它的目的是作为连接就绪的单个仲裁器。因此,它希望由单个线程控制。