我有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>
答案 0 :(得分:4)
select
是一种解复用机制。当您使用它来确定单个套接字上的数据何时就绪或超时时,它实际上被设计为在许多套接字上返回数据就绪状态(因此fd_set
)。从概念上讲,它与poll
,epoll
和kqueue
相同。结合非阻塞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
的调用被序列化)。这与其作为解复用器的功能一致。它的目的是作为连接就绪的单个仲裁器。因此,它希望由单个线程控制。