所謂的心跳包就是用戶端定時放送簡單的資訊給伺服器端,告訴它我還在而已。代碼就是每隔幾分鐘發送一個固定資訊給伺服器端,伺服器端回複一個固定資訊。如果伺服器端幾分鐘後沒有收到用戶端資訊則視用戶端斷開。比如有些通信軟體長時間不适用,要想知道它的狀态是線上還是離線,就需要心跳包,定時發包收包。
心跳包之是以叫心跳包是因為:它像心跳一樣每隔固定時間發一次,以此來告訴伺服器,這個用戶端還活在。事實上這是為了保持長連接配接,至于這個包的内容,是沒有什麼特别規定的,不過一般都是很小的包,活着隻包含標頭的一個空包。
在TCP機制裡面,本身是存在有心跳包機制的,也就是TCP選項:SO_KEEPALIVE. 系統預設是設定的2小時的心跳頻率。
【引用參考】我們知道,TCP有一個連接配接檢測機制,就是如果在指定的時間内(一般為2個小時)沒有資料傳送,會給對端發送一個Keep-Alive資料報,使用的序列号是曾經發出的最後一個封包的最後一個位元組的序列号,對端如果收到這個資料,回送一個TCP的ACK,确認這個位元組已經收到,這樣就知道此連接配接沒有被斷開。如果一段時間沒有收到對方的響應,會進行重試,重試幾次後,向對端發一個reset,然後将連接配接斷掉。
在Windows中,第一次探測是在最後一次資料發送的兩個小時,然後每隔1秒探測一次,一共探測5次,如果5次都沒有收到回應的話,就會斷開這個連接配接。但兩個小時對于我們的項目來說顯然太長了。我們必須縮短這個時間。那麼我們該如何做呢?我要利用Socket類的IOControl()函數。我們來看看這個函數能幹些什麼:
使用 IOControlCode 枚舉指定控制代碼,為 Socket 設定低級操作模式。 【引用參考】
MSDN中IOControlCode的說明:為 Socket 設定低級别操作模式。兩個重載如下:
1、IOControl(Int32, array[]()[], array[]()[])
2、IOControl(IOControlCode, array<Byte>[]()[], array<Byte>[]()[])
我們采用第二種重載。
首先需要弄清參數的意義。
【引用參考】
在C++裡它是一個結構體。我們來看看這個結構體:
struct tcp_keepalive
{
u_long onoff; //是否啟用Keep-Alive
u_long keepalivetime; //多長時間後開始第一次探測(機關:毫秒)
u_long keepaliveinterval; //探測時間間隔(機關:毫秒)
};
在C#中,我們直接用一個Byte數組傳遞給函數:
uint dummy = 0;
byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);//是否啟用Keep-Alive
BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy));//多長時間開始第一次探測
BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);//探測時間間隔
【引用參考】
結合以上兩種種方法,伺服器端對網絡連接配接的斷開都能作出及時的響應,至此問題解決。