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