如果主機崩潰,write是否阻塞取決于內核的tcp緩沖區,但read將一直阻塞,直到超時ETIMEOUT,或由于某些中間路由器的原因返回EHOSTUNREACH/ENETUNREACH。select不能檢測到該情況。
如果主機崩潰并重起,客戶的write到達主機時主機響應RST,客戶的read將返ECONNRESET。
此處的”非正常斷開”指TCP連接不是以優雅的方式斷開,如網線故障等物理鏈路的原因,還有突然主機斷電等原因
有兩種方法可以檢測:1.TCP連接雙方定時發握手消息 2.利用TCP協議棧中的KeepAlive探測
第二種方法簡單可靠,只需對TCP連接兩個Socket設定KeepAlive探測,所以本文只講第二種方法在Linux,Window2000下的實現(在其它的平臺上沒有作進一步的測試)
Windows 2000平臺下
- ??
- struct?TCP_KEEPALIVE?{ ??
- u_longonoff; ??
- u_longkeepalivetime; ??
- u_longkeepaliveinterval; ??
- }?; ??
- #define?SIO_KEEPALIVE_VALS?_WSAIOW(IOC_VENDOR,4) ??
- ??
- 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?=?5000;???
- inKeepAlive.keepalivetime?=?5000;???
- if?(WSAIoctl((unsigned?int)s,?SIO_KEEPALIVE_VALS, ??
- (LPVOID)&inKeepAlive,?ulInLen, ??
- (LPVOID)&outKeepAlive,?ulOutLen, ??
- &ulBytesReturn,?NULL,?NULL)?==?SOCKET_ERROR) ??
- { ??
- ACE_DEBUG?((LM_INFO, ??
- ACE_TEXT?("(%P|%t)?\WSAIoctl?failed.?error?code(%d)!\n"),WSAGetLastError())); ??
- }??
//定義結構及宏
struct TCP_KEEPALIVE {
u_longonoff;
u_longkeepalivetime;
u_longkeepaliveinterval;
} ;
#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
//KeepAlive實現
TCP_KEEPALIVE inKeepAlive = {0}; //輸入參數
unsigned long ulInLen = sizeof(TCP_KEEPALIVE);
TCP_KEEPALIVE outKeepAlive = {0}; //輸出參數
unsigned long ulOutLen = sizeof(TCP_KEEPALIVE);
unsigned long ulBytesReturn = 0;
//設置socket的keep alive為5秒,并且發送次數為3次
inKeepAlive.onoff = 1;
inKeepAlive.keepaliveinterval = 5000; //兩次KeepAlive探測間的時間間隔
inKeepAlive.keepalivetime = 5000; //開始首次KeepAlive探測前的TCP空閉時間
if (WSAIoctl((unsigned int)s, SIO_KEEPALIVE_VALS,
(LPVOID)&inKeepAlive, ulInLen,
(LPVOID)&outKeepAlive, ulOutLen,
&ulBytesReturn, NULL, NULL) == SOCKET_ERROR)
{
ACE_DEBUG ((LM_INFO,
ACE_TEXT ("(%P|%t) \WSAIoctl failed. error code(%d)!\n"),WSAGetLastError()));
}
Linux平臺下
- #include ??
- …… ??
- ??
- ??
- int?keepAlive?=?1;??
- int?keepIdle?=?5;??
- int?keepInterval?=?5;??
- int?keepCount?=?3;??
- 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"))); ??
- }??