背景
我的服务器是两段式访问确认机制,第二次等待客户端连接的accept操作会一直阻塞,直到连接出现或出错为止。但是我不希望它因为客户端出现某些不明问题而一直挡在那里,所以希望设置阻塞时间来自动释放资源。
思路
根据网上的方案,推荐用select函数来设置超时。 这个函数是通过检查缓冲区是否有数据来判断客户端连接是否到来。 如果我们在确定了客户端连接已经到来之后再执行accept来接受客户端请求的话,就防止了阻塞。
实践
根据网上的代码演示,大致为以下写法:
struct fd_set rdfs;
while(1){
struct timeval tv = {5 , 0}; // 设置5秒时间
FD_ZERO(&rdfs);
FD_SET(sockfd, &rdfs);
if (select(sockfd+1, &rdfs, NULL, NULL, &tv) > 0) // socket就绪
{
recv() / recvfrom() // 从socket读取数据
}
}
以上代码转自:http://www.dtmao.cc/java/76933.html
实测无效。
根据测试发现,该方法设置的超时很奇怪,无论我是否主动让客户端连接延迟回复,select都会阻塞设定的时间(5s),有点像是在select函数中加了个sleep(5s)。这显然不是我想要的。
不过经过测试发现select函数还是可以正确检测到当客户端请求到来后缓冲区变化的。所以可以通过自己设置超时计时,配合select检测的方式实现真正的超时检测。
最终实现
代码如下:
time_t time = getCurTick();
while (1) {
struct fd_set rdfs;
struct timeval tv = { 0 , 0 }; // 设置0秒时间,使得select函数本身不会阻塞等待
FD_ZERO(&rdfs);
FD_SET(inNode->MySocket, &rdfs);
if (select(inNode->MySocket + 1, &rdfs, NULL, NULL, &tv) > 0) // socket就绪检查
{
cSocket = accept(inNode->MySocket, (struct socketaddr*)&c_addr, 0);//检查到了客户端请求,接收请求后退出。
break;
}
if (getCurTick() - time > 5 && time > 0) //检查当前时间距离开始计时的时间是否超过5s
{
//超时清理资源,退出
printf("client timeout 5s\n");
closesocket(inNode->MySocket);
ListRemoveNode(LIST_CONNECT_TCP, inNode);
return -4;
}
}
注:
struct fd_set rdfs;
struct timeval tv = { 0 , 0 };
FD_ZERO(&rdfs);
FD_SET(inNode->MySocket, &rdfs);
这些必须设置,比如如果设置为NULL的话select检测会无效。