MaxDataRetries"="5"
對于實用的程序來說,2小時的空閑時間太長。因此,我們需要手工開啟Keepalive功能并設(shè)置合理的Keepalive參數(shù)。
// 開啟KeepAlive
BOOL bKeepAlive = TRUE;
int nRet = ::setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof(bKeepAlive));

if (nRet == SOCKET_ERROR)


{
return FALSE;
}

// 設(shè)置KeepAlive參數(shù)

tcp_keepalive alive_in =
{0};

tcp_keepalive alive_out =
{0};
alive_in.keepalivetime = 5000; // 開始首次KeepAlive探測前的TCP空閉時間

alive_in.keepaliveinterval = 1000; // 兩次KeepAlive探測間的時間間隔

alive_in.onoff = TRUE;
unsigned long ulBytesReturn = 0;

nRet = WSAIoctl(socket_handle, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in),
&alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL);
if (nRet == SOCKET_ERROR)


{
return FALSE;


}


開啟Keepalive選項之后,對于使用IOCP模型的服務(wù)器端程序來說,一旦檢測到連接斷開,GetQueuedCompletionStatus函數(shù)將立即返回FALSE,使得服務(wù)器端能及時清除該連接、釋放該連接相關(guān)的資源。對于使用select模型的客戶端來說,連接斷開被探測到時,以recv目的阻塞在socket上的select方法將立即返回SOCKET_ERROR,從而得知連接已失效,客戶端程序便有機會及時執(zhí)行清除工作、提醒用戶或重新連接。
另一種技術(shù),由應(yīng)用程序自己發(fā)送心跳包來檢測連接的健康性。客戶端可以在一個Timer中或低級別的線程中定時向發(fā)服務(wù)器發(fā)送一個短小精悍的包,并等待服務(wù)器的回應(yīng)。客戶端程序在一定時間內(nèi)沒有收到服務(wù)器回應(yīng)即認為連接不可用,同樣,服務(wù)器在一定時間內(nèi)沒有收到客戶端的心跳包則認為客戶端已經(jīng)掉線。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
windows下此處的”非正常斷開”指TCP連接不是以優(yōu)雅的方式斷開,如網(wǎng)線故障等物理鏈路的原因,還有突然主機斷電等原因.
有兩種方法可以檢測:
1.TCP連接雙方定時發(fā)握手消息
2.利用TCP協(xié)議棧中的KeepAlive探測
第二種方法簡單可靠,只需對TCP連接兩個Socket設(shè)定KeepAlive探測,
所以本文只講第二種方法在Linux,Window2000下的實現(xiàn)(在其它的平臺上沒有作進一步的測試)

Windows 2000平臺下 頭文件
#include <mstcpip.h>
//定義結(jié)構(gòu)及宏
/*

struct TCP_KEEPALIVE
{
u_longonoff;
u_longkeepalivetime;
u_longkeepaliveinterval;
} ;
*/

tcp_keepalive live,liveout;
live.keepaliveinterval=5000; //每5秒發(fā)一次探測報文,發(fā)5次沒有回應(yīng),就斷開
live.keepalivetime=30000;//超過30s沒有數(shù)據(jù),就發(fā)送控測包
live.onoff=TRUE;
int Opt = 1;
int iRet = setsockopt(Accept,SOL_SOCKET,SO_KEEPALIVE,(char *)&Opt,sizeof(int));
if(iRet == 0){
DWORD dw;
if(::WSAIoctl(Accept,SIO_KEEPALIVE_VALS,
&live,sizeof(live),&liveout,sizeof(liveout),
&dw,NULL,NULL)== SOCKET_ERROR){
}
}


ACE下代碼 //by rainfish blog.csdn.net/bat603
int Opt = 1;
//在測試過程中,發(fā)現(xiàn)檢測的次數(shù)是5次,即下面的設(shè)置中,從最近一次消息開始計算的10秒后,每次間隔5秒,連續(xù)發(fā)送5次,即35秒發(fā)現(xiàn)網(wǎng)絡(luò)斷了
tcp_keepalive live,liveout;
live.keepaliveinterval=5000; //每次檢測的間隔 (單位毫秒)
live.keepalivetime=10000; //第一次開始發(fā)送的時間(單位毫秒)
live.onoff=TRUE;
int iRet = stream.set_option(SOL_SOCKET,SO_KEEPALIVE,&Opt,sizeof(int));
if(iRet == 0){
DWORD dw;
//此處顯示了在ACE下獲取套接字的方法,即句柄的(SOCKET)化就是句柄
if(WSAIoctl((SOCKET)h,SIO_KEEPALIVE_VALS,&live,sizeof(live),
&liveout,sizeof(liveout),&dw,NULL,NULL)== SOCKET_ERROR){
//Delete Client
return;
}
}
Linux平臺下
#include "/usr/include/linux/tcp.h"
#include "/usr/include/linux/socket.h"
////KeepAlive實現(xiàn),單位秒
//下面代碼要求有ACE,如果沒有包含ACE,則請把用到的ACE函數(shù)改成linux相應(yīng)的接口
int keepAlive = 1;//設(shè)定KeepAlive
int keepIdle = 5;//開始首次KeepAlive探測前的TCP空閉時間
int keepInterval = 5;//兩次KeepAlive探測間的時間間隔
int keepCount = 3;//判定斷開前的KeepAlive探測次數(shù)
if(setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive)) == -1)
{
ACE_DEBUG ((LM_INFO,
ACE_TEXT ("(%P|%t) setsockopt SO_KEEPALIVE error!/n")));
}
if(setsockopt(s,SOL_TCP,TCP_KEEPIDLE,(void *)&keepIdle,sizeof(keepIdle)) == -1)
{
ACE_DEBUG ((LM_INFO,
ACE_TEXT ("(%P|%t) setsockopt TCP_KEEPIDLE error!/n")));
}
if(setsockopt(s,SOL_TCP,TCP_KEEPINTVL,(void *)&keepInterval,sizeof(keepInterval)) == -1)
{
ACE_DEBUG ((LM_INFO,
ACE_TEXT ("(%P|%t) setsockopt TCP_KEEPINTVL error!/n")));
}
if(setsockopt(s,SOL_TCP,TCP_KEEPCNT,(void *)&keepCount,sizeof(keepCount)) == -1)
{
ACE_DEBUG ((LM_INFO,
ACE_TEXT ("(%P|%t)setsockopt TCP_KEEPCNT error!/n")));
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++