小弟想让服务端检测与客户端的TCP连接是否已经非正常断开(如网线断开或客户端所在机子突然断电),参考网上的资料小弟在服务端开启keepalive来检测,客户端没有开启keepalive。服务端的部分代码如下:
int set_keepalive(SOCKET s, TCP_KEEPALIVE *pTCP_KeepAlive)
{
int res = 0;BOOL bKeepAlive = TRUE;
int keepalive = 1;TCP_KEEPALIVE inKeepAlive = {0}; //输入参数   
unsigned long ulInLen = sizeof(TCP_KEEPALIVE);   
TCP_KEEPALIVE outKeepAlive = {0}; //输出参数   
unsigned long ulOutLen = sizeof(TCP_KEEPALIVE);   unsigned long ulBytesReturn = 0;unsigned long param=1;  //res = ioctlsocket(s,FIONBIO,&param);//设置KeepAlive   
res = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof(bKeepAlive));   if (res == 0) {   
//设置KeepAlive检测时间和次数   
//设置socket的keep alive为10秒,并且发送次数为3次   
inKeepAlive.onoff = pTCP_KeepAlive->onoff;   
inKeepAlive.keepaliveinterval = pTCP_KeepAlive->keepaliveinterval; //两次KeepAlive探测间的时间间隔   
inKeepAlive.keepalivetime = pTCP_KeepAlive->keepalivetime; //开始首次KeepAlive探测前的TCP空闭时间   res = WSAIoctl(s,   
SIO_KEEPALIVE_VALS,   
(LPVOID)&inKeepAlive,   
ulInLen,   
(LPVOID)&outKeepAlive,   
ulOutLen,   
&ulBytesReturn,   
NULL,   
NULL);
}return res;
}//接收线程
ULONG __stdcall _Receive_Thread(LPVOID args)
{
int res;
NETCTX *pNetCtx= (NETCTX *)args;char buf[MAX_RECEIVE_BUFF_SIZE];fd_set fdread;struct timeval timeout;TCP_KEEPALIVE stKeepAlive = {0};
  stKeepAlive.onoff = 1;
stKeepAlive.keepaliveinterval = 5000;
stKeepAlive.keepalivetime = 1000;  set_keepalive(pNetCtx->s, &stKeepAlive);while (1){
FD_ZERO(&fdread);
FD_SET(pNetCtx->s, &fdread);timeout.tv_sec = 1;
timeout.tv_usec = 0;__try {
if ((res = select(0, &fdread, NULL, NULL, &timeout)) == SOCKET_ERROR) {
  ……
break; 
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
  ……
}if (res > 0) {  
res = recv(pNetCtx->s, buf, sizeof(buf), 0);
if (res > 0) {
  //处理客户端数据
  ……
}
  else {
  ……
  break;
}  ……
}
  else{
break;
}  ……
}shutdown(pNetCtx->s, SD_BOTH);
closesocket(pNetCtx->s);
  ……return 0;
}// 连接侦听线程  
ULONG __stdcall _ListenThread(LPVOID args)
{
int res = 0;
  NETCTX *pNetCtx= (NETCTX *)args;
fd_set fdread;
int addr_len;struct timeval timeout;SOCKET s;
  NETCTX *pRcvNetCtx;DWORD dwThreadID = 0;for (;;) {FD_ZERO(&fdread);
FD_SET(pNetCtx->s, &fdread);timeout.tv_sec = 1;
timeout.tv_usec = 0;if ((res = select(0, &fdread, NULL, NULL, &timeout)) == SOCKET_ERROR)  
  {
  ……
  break;
  }if (res > 0) {
s = accept(pNetCtx->s, &remoteaddr, &addr_len);if (s != INVALID_SOCKET) {
  pRcvNetCtx =(NETCTX *)malloc(sizeof(NETCTX));
  pRcvNetCtx->s = s;
  ……
recvctx->hThread = CreateThread(NULL,0,_Receive_Thread, pRcvNetCtx,0,&dwThreadID);
  ……
}
else{
closesocket(s);
}
}
  ……
}shutdown(pNetCtx->s, SD_BOTH);
closesocket(pNetCtx->s);
}客户端以tcp方式连接上服务端后,通过抓包发现,服务端定时向客户端发送keepalive数据包,客户端也返回了响应包。
然后小弟把客户端所在机子的网线拔掉,大约5秒钟过后,服务端不再向客户端发送keepalive数据包。
然而服务端直到大约20秒钟后,因为recv返回-1而得知socket无效,并不是在停止keepalive检测那一刻就得知socket无效的。而且,上面说的“客户端异常断开,服务端约20秒后就发现socjet无效”,并不是因为设置了keepalive才会20秒后发现,即使服务端不开启keepalive,一样在20秒后发现socket无效。如果客户端的并发量很大的话,那就不止20秒了,还会更长久。所以很困惑,感觉开启keepalive似乎没有效果。
各位大侠请指点一下,看小弟的代码是否有缺陷修正的,或者有什么更好的办法检测异常断开,除了自己去实现心跳。
呵呵,小弟分不多,请体谅!!