TCP異常斷開是指在突然斷電,直接拔網線等等情況下,如果通信雙方沒有進行數據發送通信等處理的時候,無法獲知連接已經斷開的情況.
在通常的情況下,為了使得socket通信不受操作系統的限制,需要自己在應用層實現心跳包機制,來檢查異常斷開的情況,一般的方式就是服務器在一段時間沒有收到客戶端數據包時,定時發包,然后客戶端回應,如果已經出現異常斷開則服務器接收會返回錯誤,而客戶端在指定時間內沒有收到數據包,則主動向服務器發包,得到錯誤就說明斷開.諸如此類的方式就是自己實現的心跳包機制.
但操作系統本身也自帶了一些心跳包機制,這些機制是由socket的TCP棧底層實現的,不會影響應用層通信,也不需要應用層自己處理,發現異常斷開可以自行檢查出來並返回錯誤(它的本質也是在空閑時發送心跳包).以下介紹一下Windows以及linux下的方法.
首先介紹Windows下的方法,該方式要求通信雙方必須都是Windows NT以上操作系統(如果是其它版本操作系統,如linux等等,不敢保證100%無效).MSDN中有描述WSAIoctl中的SIO_KEEPALIVE_VALS選項,該選項以及struct tcp_keepalive的定義在MSTCPiP.h有,不進行說明了,直接看代碼:
#include <MSTCPiP.h>
DWORD retBytes;
tcp_keepalive inKeepSetting;
tcp_keepalive retKeepSetting;
inKeepSetting.onoff = 1; //探測次數
inKeepSetting.keepalivetime = 5500; // 首次探測開始前的tcp無數據收發空閑時間
inKeepSetting.keepaliveinterval = 3000; // 每次探測的間隔時間
if (WSAIoctl(aptSock, SIO_KEEPALIVE_VALS,
&inKeepSetting,
sizeof(inKeepSetting),
&retKeepSetting,
sizeof(retKeepSetting),
&retBytes,
NULL,
NULL) != 0)
{
printf("WSAIoctl Error: %d/n", WSAGetLastError());
}
Linux下的方式是通過setsockopt來設置選項,見代碼(代碼從網絡上摘錄了部分):
#include <netinet/tcp.h>
……
#define SOCKET_ERROR (-1)
// 以秒為單位
int keepAlive = 1; //設定KeepAlive
int keepIdle = 5; //首次探測開始前的tcp無數據收發空閑時間
int keepInterval = 3; //每次探測的間隔時間
int keepCount = 2; //探測次數
if(setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive)) == SOCKET_ERROR)
printf("Call setsockopt error, errno is %d/n", errno);
if(setsockopt(s,SOL_TCP,TCP_KEEPIDLE,(void *)&keepIdle,sizeof(keepIdle)) == SOCKET_ERROR)
printf("Call setsockopt error, errno is %d/n", errno);
if(setsockopt(s,SOL_TCP,TCP_KEEPINTVL,(void *)&keepInterval,sizeof(keepInterval)) == SOCKET_ERROR)
printf("Call setsockopt error, errno is %d/n", errno);
if(setsockopt(s,SOL_TCP,TCP_KEEPCNT,(void *)&keepCount,sizeof(keepCount)) == SOCKET_ERROR)
printf("Call setsockopt error, errno is %d/n", errno);