小弟想让服务端检测与客户端的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,¶m);//设置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似乎没有效果。
各位大侠请指点一下,看小弟的代码是否有缺陷修正的,或者有什么更好的办法检测异常断开,除了自己去实现心跳。
呵呵,小弟分不多,请体谅!!
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,¶m);//设置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似乎没有效果。
各位大侠请指点一下,看小弟的代码是否有缺陷修正的,或者有什么更好的办法检测异常断开,除了自己去实现心跳。
呵呵,小弟分不多,请体谅!!
如果设置,就会在一定时候内,通知你socket无效,但这个时间并不完全是你用WSAIoctl设置的时间,它只是表示间隔多少时间发,尝试发几次