我的服务器代码中有类似的接收语句:
do {
FD_ZERO(&read_fds);
FD_SET((unsigned int)sock, &read_fds);
FD_ZERO(&write_fds);
if (out_queue)
FD_SET((unsigned int)sock, &write_fds);
tm.tv_sec = 0;
tm.tv_usec = 50;
count = select(sock + 1, &read_fds, &write_fds, NULL, &tm);
} while (count == 0);
if (FD_ISSET((unsigned int)sock, &read_fds)) {
// Try to read some data in
bytes = Read(buff, currlen);
...
}
1)当客户端以PC形式登录时,断开客户端的网线,能够跳出select的循环,并在read中判断出错误,并得到socket断线信息
2)当客户端以手机形式登录时(使用wifi),断开客户端的wifi连接,则始终不能跳出select循环,至少测试过等待5分钟后仍不能跳出,count始终为0.
于是从网上搜索的相关的讨论话题,发现这确实是一个很普遍的问题,很多的描述与此类似。一个基本的思路是靠在send中发送数据来检测断线,但在我的应用中没有心跳包,不便使用send判断。
为此找到了一篇官方文档,在http://support.microsoft.com/kb/140325,感觉到必须使用KEEPALIVE来尝试,为此加入了相关的代码进行实验,很幸运,实验成功!
需要加入的代码是:
#ifdef __WIN32__
tcp_keepalive inKeepAlive = {0};
unsigned long ulInLen = sizeof(tcp_keepalive);
tcp_keepalive outKeepAlive = {0};
unsigned long ulOutLen = sizeof(tcp_keepalive);
unsigned long ulBytesReturn = 0;
inKeepAlive.onoff = 1;
inKeepAlive.keepaliveinterval = 1000;
inKeepAlive.keepalivetime = 60000;
if (SOCKET_ERROR == WSAIoctl(sock, SIO_KEEPALIVE_VALS,
(LPVOID)&inKeepAlive, ulInLen,
(LPVOID)&outKeepAlive, ulOutLen,
&ulBytesReturn, NULL, NULL))
{
}
#endif
此后,当手机侧的WIFI手动关闭或按下电源键黑屏后,确实约1分钟后,select函数返回并在read中得到WSAECONNRESET异常!