在以TCP為連接方式的伺服器中,為什麼在服務端設計當中需要考慮心跳?

這個心跳包除了告知服務端我在線,還有其他作用嗎?比如有答案提到的運營商主動斷掉沒有數據報的網路連接?


你ssh連上伺服器的情況下,拔掉網線,你任然檢測不到網路斷開了(沒有FIN),這時候把網線插回去,敲兩下鍵盤,終端一樣有反應,鏈接還活著。因為tcp的鏈接是否有效,依賴鏈接兩端的狀態確定,你在你機器上拔掉網線,你是知道這件事情的,但是中間網路設備拔掉網線,或者出現什麼問題,你完全無法得知鏈接是否還有效,依賴tcp本身的keepalive機制需要半個小時以上才能檢測得出來。

所幸,keepalive如今也可以設置檢測時間了:

/* ikeepalive: tcp keep alive option */
int ikeepalive(int sock, int keepcnt, int keepidle, int keepintvl)
{
int enable = (keepcnt &< 0 || keepidle &< 0 || keepintvl &< 0)? 0 : 1; unsigned long value; #if (defined(WIN32) || defined(_WIN32) || defined(_WIN64) || defined(WIN64)) #define _SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR, 4) unsigned long keepalive[3], oldkeep[3]; OSVERSIONINFO info; int candoit = 0; info.dwOSVersionInfoSize = sizeof(info); GetVersionEx(info); if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) { if ((info.dwMajorVersion == 5 info.dwMinorVersion &>= 1) ||
(info.dwMajorVersion &>= 6)) {
candoit = 1;
}
}

value = enable? 1 : 0;
isetsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)value,
sizeof(value));

if (candoit enable) {
int ret = 0;
keepalive[0] = enable? 1 : 0;
keepalive[1] = ((unsigned long)keepidle) * 1000;
keepalive[2] = ((unsigned long)keepintvl) * 1000;
ret = WSAIoctl((unsigned int)sock, _SIO_KEEPALIVE_VALS,
(LPVOID)keepalive, 12, (LPVOID)oldkeep, 12, value, NULL, NULL);
if (ret == SOCKET_ERROR) {
return -1;
}
} else {
return -2;
}

#elif defined(SOL_TCL) defined(TCP_KEEPIDLE) defined(TCP_KEEPINTVL)

value = enable? 1 : 0;
isetsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)value, sizeof(long));
value = keepcnt;
isetsockopt(sock, SOL_TCP, TCP_KEEPCNT, (char*)value, sizeof(long));
value = keepidle;
isetsockopt(sock, SOL_TCP, TCP_KEEPIDLE, (char*)value, sizeof(long));
value = keepintvl;
isetsockopt(sock, SOL_TCP, TCP_KEEPINTVL, (char*)value, sizeof(long));
#elif defined(SO_KEEPALIVE)
value = enable? 1 : 0;
isetsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)value, sizeof(long));
#else
return -1;
#endif

return 0;
}

如今,大部分平台下,確實可以不用自己做心跳了,直接調整下KeepAlive的參數到你期望的水平即可。然而如你所見,代碼中大量的條件判斷。KeepAlive參數調整的實現對平台依賴很大,並非所有平台都支持這樣的,你如果能夠確定自己代碼運行的平台,那麼放心大膽的用這個keepalive。你如果無法確定平台,不想依賴平台的keepalive,又需要自己精確控制鏈接超時,那麼請自己實現心跳。


理想網路環境下TCP本身的心跳就夠了,但由於LVS、運營商HTTP劫持等現實中各種HTTP、TCP劫持的存在,TCP層面的心跳不能保證是真正的伺服器發送的,所以要在應用層增加「心跳」


遊戲伺服器常常有心跳包的設計。


我們的心跳包就是為了防止Socket斷開連接,或是TCP的連接斷開嗎?

答案是否定的,TCP連接的通道是個虛擬的,連接的維持靠的是兩端TCP軟體對連接狀態的維護。

TCP 連接自身有維護連接的機制,說白了就是自身有長時間沒有數據包情況下的判斷連接是否還存在的檢測,清除死連接,即使在沒有數據來往的時候,TCP也就可以(在啟動TCP這個功能的前提下)自動發包檢測是否連接正常,這個不需要我們處理。


服務端設計心跳包的目的:
探知對端應用是否存活,服務端客戶端都可以發心跳包,一般都是客戶端發送心跳包,服務端用於判斷客戶端是否在線,從而對服務端內存緩存數據進行清理(玩家下線等);問題在於,通過TCP四次握手斷開的設定,我們也是可以通過Socket的read方法來判斷TCP連接是否斷開,從而做出相應的清理內存動作,那麼為什麼我們還需要使用客戶端發送心跳包來判斷呢?

第一種判斷客戶端是否在線策略:
直接監控TCP傳輸協議的返回值,通過返回值處理應用層的存活判斷
比如在C++當中
使用poll的IO復用方法時:
if(fds[i].revents POLLERR)
if(fds[i].events POLLDHUP)
通過上述判斷可以探知TCP連接的正確性從而在伺服器也關閉對應的連接
close() on the file descriptor will release resources that are still being reserved on behalf of the socket. 此時調用close()函數才會釋放相關的資源。

比如說進行如下處理:

/*如果客戶端關閉連接,則伺服器也關閉對應的連接,並將用戶總數減1*/
users[fds[i].fd] = users[fds[user_counter].fd];
close(fds[i].fd);
fds[i] = fds[user_counter];
i--;
user_counter--;
printf("a client left
");

又比如在Java中:
在Java的阻塞編程中:通過

ServerSocket ss = new ServerSocket(10021);
Socket so = ss.accept();
// 獲取相關流對象
InputStream in = so.getInputStream();
byte[] bytes = new byte[1024];
int num = in.read(bytes);
if(num == -1)//表明讀到了流的末尾,事實也就是client端斷開了連接,比如調用close()
{
so.close();
}

在Java的非阻塞編程當中:通過

SelectionKey key = selector.register(socketChannel,ops,handle);
SocketChannel socketChanel = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int num = socketChannel.read(buffer);
if(num == -1)
{
key.channel().close();
}

上述連接處理方式,返回-1也好,收到POLLERR POLLDHUP也好,都是收到了客戶端的fin或者rst之後的反應,所以根據四次分手原則,我們調用close方法,發送fin給客戶端。

上面這種策略通過TCP協議的返回值來得知客戶端TCP斷開,從而得知客戶端掉線。
當前提是如果提前根據ip或者mac做了記錄,所以可以在伺服器端收到TCP連接中斷的消息後,調用close,並且通過socket得到玩家socket數據(具體如IP地址),從而獲得user信息從而清除數據。

那麼這種方式有什麼不完美呢?或者說有什麼缺陷呢?

主要原因就是TCP的斷開可能有時候是不能瞬時探知的,甚至是不能探知的,也可能有很長時間的延遲,如果前端沒有正常的斷開TCP連接,四次握手沒有發起,服務端無從得知客戶端的掉線,那麼就要依靠開啟TCP的keep alive機制,but TCP協議的keep alive機制是不建議開啟的,即使開啟了默認的時間間隔是2h,淚奔!如果要服務端維持一個2h的死鏈接,那是可怕的,如果我們調整了時間間隔,也是有problem的,因為TCP本身就不建議TCP層的心跳檢測,因為這可能導致一個完好的TCP連接在中間網路中斷的情況下重啟⊙▂⊙。

下面有必要先介紹下TCP的keep-alive機制。
關於TCP自己的keep-alive機制,參看TCP/IP詳解當中有如下介紹:
使用keeplive機制,可以檢測到網路異常。
一、什麼是keepalive定時器?

在一個空閑的(idle)TCP連接上,沒有任何的數據流,許多TCP/IP的初學者都對此感到驚奇。也就是說,如果TCP連接兩端沒有任何一個進程在向對方發送數據,那麼在這兩個TCP模塊之間沒有任何的數據交換。你可能在其它的網路協議中發現有輪詢(polling),但在TCP中它不存在。言外之意就是我們只要啟動一個客戶端進程,同伺服器建立了TCP連接,不管你離開幾小時,幾天,幾星期或是幾個月,連接依舊存在。中間的路由器可能崩潰或者重啟,電話線可能go down或者back up,只要連接兩端的主機沒有重啟,連接依舊保持建立。

這就可以認為不管是客戶端的還是伺服器端的應用程序都沒有應用程序級(application-level)的定時器來探測連接的不活動狀態(inactivity),從而引起任何一個應用程序的終止。然而有的時候,伺服器需要知道客戶端主機是否已崩潰並且關閉,或者崩潰但重啟。許多實現提供了存活定時器來完成這個任務。

存活定時器是一個包含爭議的特徵。許多人認為,即使需要這個特徵,這種對對方的輪詢也應該由應用程序來完成,而不是由TCP中實現。此外,如果兩個終端系統之間的某個中間網路上有連接的暫時中斷,那麼存活選項(option)就能夠引起兩個進程間一個良好連接的終止。例如,如果正好在某個中間路由器崩潰、重啟的時候發送存活探測,TCP就將會認為客戶端主機已經崩潰,但事實並非如此。

存活(keepalive)並不是TCP規範的一部分。在Host Requirements RFC羅列有不使用它的三個理由:(1)在短暫的故障期間,它們可能引起一個良好連接(good connection)被釋放(dropped),(2)它們消費了不必要的寬頻,(3)在以數據包計費的互聯網上它們(額外)花費金錢。然而,在許多的實現中提供了存活定時器。

一些伺服器應用程序可能代表客戶端佔用資源,它們需要知道客戶端主機是否崩潰。存活定時器可以為這些應用程序提供探測服務。Telnet伺服器和Rlogin伺服器的許多版本都默認提供存活選項。

個人計算機用戶使用TCP/IP協議通過Telnet登錄一台主機,這是能夠說明需要使用存活定時器的一個常用例子。如果某個用戶在使用結束時只是關掉了電源,而沒有註銷(log off),那麼他就留下了一個半打開(half-open)的連接。在圖18.16,我們看到如何在一個半打開連接上通過發送數據,得到一個複位(reset)返回,但那是在客戶端,是由客戶端發送的數據。如果客戶端消失,留給了伺服器端半打開的連接,並且伺服器又在等待客戶端的數據,那麼等待將永遠持續下去。存活特徵的目的就是在伺服器端檢測這種半打開連接。

二、keepalive如何工作?

在此描述中,我們稱使用存活選項的那一段為伺服器,另一端為客戶端。也可以在客戶端設置該選項,且沒有不允許這樣做的理由,但通常設置在伺服器。如果連接兩端都需要探測對方是否消失,那麼就可以在兩端同時設置(比如NFS)。

若在一個給定連接上,兩小時之內無任何活動,伺服器便向客戶端發送一個探測段。(我們將在下面的例子中看到探測段的樣子。)客戶端主機必須是下列四種狀態之一:

1) 客戶端主機依舊活躍(up)運行,並且從伺服器可到達。從客戶端TCP的正常響應,伺服器知道對方仍然活躍。伺服器的TCP為接下來的兩小時複位存活定時器,如果在這兩個小時到期之前,連接上發生應用程序的通信,則定時器重新為往下的兩小時複位,並且接著交換數據。

2) 客戶端已經崩潰,或者已經關閉(down),或者正在重啟過程中。在這兩種情況下,它的TCP都不會響應。伺服器沒有收到對其發出探測的響應,並且在75秒之後超時。伺服器將總共發送10個這樣的探測,每個探測75秒。如果沒有收到一個響應,它就認為客戶端主機已經關閉並終止連接。

3) 客戶端曾經崩潰,但已經重啟。這種情況下,伺服器將會收到對其存活探測的響應,但該響應是一個複位,從而引起伺服器對連接的終止。

4) 客戶端主機活躍運行,但從伺服器不可到達。這與狀態2類似,因為TCP無法區別它們兩個。它所能表明的僅是未收到對其探測的回復。

伺服器不必擔心客戶端主機被關閉然後重啟的情況(這裡指的是操作員執行的正常關閉,而不是主機的崩潰)。當系統被操作員關閉時,所有的應用程序進程(也就是客戶端進程)都將被終止,客戶端TCP會在連接上發送一個FIN。收到這個FIN後,伺服器TCP向伺服器進程報告一個文件結束,以允許伺服器檢測這種狀態。

在第一種狀態下,伺服器應用程序不知道存活探測是否發生。凡事都是由TCP層處理的,存活探測對應用程序透明,直到後面2,3,4三種狀態發生。在這三種狀態下,通過伺服器的TCP,返回給伺服器應用程序錯誤信息。(通常伺服器向網路發出一個讀請求,等待客戶端的數據。如果存活特徵返回一個錯誤信息,則將該信息作為讀操作的返回值返回給伺服器。)在狀態2,錯誤信息類似於「連接超時」。狀態3則為「連接被對方複位」。第四種狀態看起來像連接超時,或者根據是否收到與該連接相關的ICMP錯誤信息,而可能返回其它的錯誤信息。

具體來說:

在TCP協議的機制裡面,本身的心跳包機制,也就是TCP協議中的SO_KEEPALIVE,系統默認是設置2小時的心跳頻率。需要用要用setsockopt將SOL_SOCKET.SO_KEEPALIVE設置為1才是打開,並且可以設置三個參數tcp_keepalive_time/tcp_keepalive_probes/tcp_keepalive_intvl,

分別表示連接閑置多久開始發keepalive的ACK包、發幾個ACK包不回復才當對方死了、兩個ACK包之間間隔多長。

TCP協議會向對方發一個帶有ACK標誌的空數據包(KeepAlive探針),對方在收到ACK包以後,如果連接一切正常,應該回復一個ACK;如果連接出現錯誤了(例如對方重啟了,連接狀態丟失),則應當回復一個RST;如果對方沒有回復,伺服器每隔多少時間再發ACK,如果連續多個包都被無視了,說明連接被斷開了。


「心跳檢測包」是屬於TCP協議底層的檢測機制,上位機軟體只是解析顯示網口的有用數據包,收到心跳包報文屬於TCP協議層的數據,一般軟體不會將它直接在應用層顯示出來,所以看不到。乙太網中的「心跳包」可以通過「乙太網抓包軟體」分析TCP/IP協議層的數據流看到。報文名稱」TCP Keep-Alive」。

一些比較可靠的乙太網轉串口模塊,都有心跳包的檢測,比如致遠電子的ZNE-100TL模塊,配置「心跳包檢測」間隔時間設為「10」秒,使用一款」wireshark」的抓包軟體來實際查看下TCP/IP協議層「心跳包」數據。

看了上面的內容,使用TCP自己的keep-alive機制,也是可以實現連接維持,通過TCP傳輸層的心跳包探知兩端TCP連接的正確性,從而得知應用層的情況(TCP在,應用一定在,TCP不在了,應用一定不在了),但是這不是最優選擇!

那麼既然有TCP的心跳機制,我們為什麼還要在應用層實現自己的心跳檢測機制呢?
評論中 @Raynor 所說:
tcpip詳解卷1有網路異常中斷的3種情況,比如os回收埠的時候發送的rst包,如果os掛了就不會發這個消息了。 另外如果網路異常,有可能收到路由器返回的icmp主機不可達消息從而關閉連接。 keepalive之所以不靠譜,是因為需要從socket error獲知連接斷開,而且是被動斷開。 而應用層自己實現的heartbeat可以自主檢測超時,更方便修改超時時間和斷開前處理。
以及@李樂所說:
keepalive設計初衷清除和回收死亡時間長的連接,不適合實時性高的場合,而且它會先要求連接一定時間內沒有活動,周期長,這樣其實已經斷開很長一段時間,沒有及時性。而且keepalive好像還不能主動通知應用層,需要主動調用api去檢測異常。

二:使用自己應用層的心跳包,上述方法用於正常情況下的TCP連接維護,

場景舉例如下:在遊戲伺服器當中,內存中維護著眾多玩家的在線數據,以方便調用,比如玩家的英雄隊伍信息,玩家的世界位置信息,在玩家下線的時候,伺服器必須知道並且清除掉數據(不然就會出現一個已經下線的玩家出現在世界上),在上述舉例中,在伺服器端收到TCP連接中斷的消息後,調用close,期間可以通過socket得到玩家socket數據,從而獲得user信息(如果提前根據ip或者mac做了記錄)從而清除數據。

但是如果不是正常的玩家下線,TCP的四次握手沒有成功,比如網路直接中斷,client端的TCP協議的fin包沒有發出去,服務端就不能及時探知玩家下線,如果依賴上面講的TCP自己的keep alive探測機制,時間較長,做不到及時處理下線玩家,並且性能不佳,因為TCP/IP的設計者本身就不支持TCP的心跳,因為這可能因為中間網路的短暫中斷導致兩端良好的TCP連接斷開。所以一般不啟用TCP的心跳機制,那我們怎麼處理這些異常下線呢?如果不處理,服務端將一直維護著TCP死連接,導致網路資源(一台伺服器可以支持的TCP連接有限)和內存資源(內存中可能維護著該玩家的數據)的佔用,所以就要用到應用層的心跳包了

下面解釋下應用層的心跳包:


心跳包,通常是客戶端每隔一小段時間向伺服器發送的一個數據包,通知伺服器自己仍然在線,伺服器與客戶端之間每隔一段時間 進行一次交互,來判斷這個鏈接是否有效,並傳輸一些可能有必要的數據。通常是在建立了一個TCP的socket連接後無法保證這個連接是否持續有效,這時候兩邊應用會通過定時發送心跳包來保證連接是有效的。因按照一定的時間間隔發送,類似於心跳,所以叫做心跳包。事實上為了保持長連接(長連接指的是建立一次TCP連接之後,就認為連接有效,利用這個連接去不斷傳輸數據,不斷開TCP連接),至於包的內容,是沒有特別規定的,不過一般都是很小的包,或者只是包含包頭的一個空包。

那麼心跳包的意義就在於方便的在服務端管理客戶端的在線情況,並且可以防止TCP的死連接問題,避免出現長時間不在線的死鏈接仍然出現在服務端的管理任務中。

再舉下面一個例子說明下為什麼TCP自身的四次握手斷開機制不能完全保證應用程序探知連接斷開從而避免死連接。

(1)做一個遊戲客戶端和伺服器端的測試,手動強制關閉客戶端進程,然後查看伺服器的情況,結果往往是,伺服器收到了客戶端關閉的事件。其實, 這樣會忽略一個問題,沒有觸發異常中斷事件,比如網路中斷。


(2)手動關閉客戶端進程,事實上並不能測試出想要的結果,因為進程是在應用層的,所以,這種測試方法不能保證網路驅動層也不發送數據報文給伺服器。經過測試會發現,當應用層強制結束進程時,對於TCP連接,驅動層會發送reset數據包!而伺服器收到這個數據包就可以正常關閉了!


(3)那麼,如果網路異常甚至直接拔掉網線呢,伺服器收不到這個數據包,就會導致死連接存在!


(4)所以,心跳包是必要的,或者使用TCP協議本身的Keep-alive來設置(但是keep-alive不夠好)

我們不能誤解TCP連接如同一條繩子,一方斷開了,另外一方必然會知道的。事實上TCP連接,這個「面向連接」的物理連接並不存在,它只是抽象出來的概念,是一個虛擬的連接,對於物理層,對於網線、光纖而言,不存在連接不連接的概念,因為,對它們而言,無非就是一些電流脈衝而已,具體就是一個一個的IP報文。


TCP的連接,不過是通過ACK、SEQ這些機制來模擬實現的,具體比如差錯重傳,擁塞控制

那麼心跳包的檢測發送處理對伺服器資源的耗費怎麼判斷?

這個要看心跳包發送的頻率,我們可以自行設置

另外這裡有個常式,模擬了socket心跳包的C語言實現:

Socket心跳包異常檢測的C語言實現,伺服器與客戶端代碼案例


兄弟,你理解錯拉。
所謂的長連接,就是你建立Tcp連接的時候,如果你長時間沒有數據往來的話,會被運營商網路主動斷掉的。
舉個通俗點的例子來說,現在手機上的聊天APP,為什麼會及時收到消息呢,就是因為保持了長連接,而長連接就是每隔一定時間發一個數據包,俗稱心跳包,告訴運營商伺服器我還在線上,不要把我斷掉。為什麼運營商伺服器會斷掉長時間沒有數據往來的連接呢,這很簡單,因為用戶太多,要為每個用戶都保持一個連接是不太現實的,所以就會檢測長時間無數據往來的連接。
而手機聊天App,為了不讓被斷掉,所以要每隔一段時間(這個時間要小於伺服器檢測間隔時間),主動發送數據,也就是心跳報,告訴它我還在線上,不要把我斷掉。這個心跳報沒什麼數據,僅僅是告訴它為了讓它保持這個連接不被斷開。


一句話給你講明白:正如TCP層需要自己的心跳一樣,應用層也需要自己的心跳,心跳的本質不是探測下層服務是否存活,而是探測對端同層服務是否存活。
我再講一遍:應用層心跳是為了檢測對端應用層是否存活(當然也順便檢測了所有下層的存活)


雙端連接過程中,如果任意一方意外崩潰、當機、網線斷開或路由器故障等"非正常連接",另一方無法得知tcp連接已經失效。
解決方案就是實現心跳機制 (應用層,靈活,開發者自己控制檢測)。
tcp協議棧的keepalive根據需求場合去取捨(不可控,有限制)。

補充:
舉個例子:連接丟失,tcp不會通知應用程序,client斷線,server的tcp連接不會檢測到斷線,而是一直連接中,server還要去維護client的連接,執行邏輯xxxxxx
心跳包就是及時檢測對方是否連接,檢查斷線的一種機制。心跳包協議數據內容完全是開發者自己掌控(比如client和server可以同步數據)。


回錯題目了,編輯掉。。。無視我


在如下情況下,即:
1,伺服器不主動給客戶端發包。
2,客戶端非正常關閉連接(比如宕機,斷網)。
情況下,伺服器端將一直維護tcp連接的狀態,而與之相關的系統資源(tcp埠,內存)將一直被佔用。
所以需要伺服器每隔一段時間主動給客戶端發個包看看對方是否還在~
另外,tcp的keepalive的最小檢測時長是2小時,對於很多應用而言太長了~再加上心跳又很容易實現,所以一般也就順手做了。


題主需要依次理解以下異常情況對正在相互進行TCP通信的兩個應用程序的影響,就明白為什麼在應用層依然需要維持定時心跳協議。

1 對端程序close socket
2 本機程序close socket
3 對端主機正常關機
4 對端進程被系統kill
5 對端主機意外崩潰關機,比如電源停電
6 對端主機重啟
7 本機與對端網線突然被拔掉
8 本機與對端中間路由器崩潰
9 對端進程陷入死鎖/阻塞等故障或者bug
10 對端主機負載過高 cpu使用率100%

如果開啟KEEPALIVE,以上這些可能又會怎麼樣?


應用層的心跳和tcp自帶的心跳不是一回事

tcp自帶的參數keepalive是檢測死連接的
具體:
設置keepalive後
tcp會在空閑時給對方發送數據
如果對方回復ack,證明連接正常
如果對方回復rst,說明連接退出
如果對方回復fin,說明連接崩潰
如果對方未回應,繼續發送直到超時。

應用層心跳keepalive是自己設定的,比如http是短連接,維持連接就定時發送心跳包


tcp 協議的連接狀態其實可以看成一個狀態機,當客戶端和服務端建立連接後,客戶端和服務端的狀態就變成established,僅此而已。不去主動改變狀態,則這個以established 狀態是保持著的。這意味即使服務端突然消失了,客戶端的狀態依然是established。而通過定時心跳,客戶端可以知道服務端不見了,從而嘗試重連或者釋放資源。


短鏈接的話可以設計專門的心跳包定時傳輸。

長連接的話沒必要自行實現,可以用tcp協議自身的心跳(keep-alive)。

長連接為什麼還需要心跳?連接可能因為以下情形被斷開:關閉連接,關閉程序(友好的和異常的),關閉計算機(友好的和異常的),網路硬體故障(拔網線)。其中拔網線和停電關機,是沒有機會斷開連接的,這時候就要靠心跳了。


即使tcp連接沒有問題,也不意味著應用層服務正常。好比兩個人打電話,電話接通,兩邊的人睡著了。


我有一個疑問,非阻塞socket中,都會有循環recv()的操作,可以通過該函數返回值判斷是否掉線,為什麼一定要有心跳包呢? 不解,求大神指點


我遇到一個socket伺服器端和lvs伺服器端的問題,我在不引入lvs時候,c#編寫的iocp模型,可以穩定運行,可是放加入lvs上就非常容易異常退出,不執行任何的捕獲異常的代碼,很是奇怪,有沒有哪位大神給指導下解決方向


只要兩端地址、埠沒變,空閑/中斷再長時間都能恢復,但問題是:
1 萬惡的NAT 連接空閑時間過長時 地址、埠被回收
2 移動互聯網 終端接入點改變導致地址、埠變化
此外,KeepAlive包的默認間隔長達2小時(RFC1122),基本沒卵用……


通常情況下是為了防止連接被NAT禁掉。


首先自我檢討下,剛幾位大神提醒得對,我表達得不是很好啊!我更正下回答,本人技術有限,歡迎大家批評指正!

可以防假死,現在很多路由器都會對閑置的網路連接進行管理的!導致伺服器上的臨時生成的與client通訊的埠可能會發生改變!但是該client卻是連接的!那麼就有可能找不到client端,從而不能正確通信!而實際上客戶端確實是連接的!這就是一種所謂的假死!我們公司的wifi智能插座的伺服器,是用tcp/ip通訊的!我記得我在linux伺服器上用命令抓包時,我深有體會啊!初出茅廬,不知說得對不對,走了好多坑坑路!


你一定沒有遇到過一個連接還在,但是收發消息包都失效的情況吧,當你的伺服器上被這些連接所佔據,就知道一個邏輯層的心跳是多麼的必要了。
外網的情況,簡直就是神鬼莫測……


補充一下,也要考慮使用多層代理的情況,在處於不活動時,連接池出於效率會釋放掉當前不活動鏈接,騰出資源給新的請求者。


tcp是長連接?好像沒有這說法吧。如果不用tcp的keepalive也不在應用層實現心跳,在沒有數據交換的情況下,即使中間網路斷了或一端崩潰了,另一端也無從得知,還是一直維護著tcp的狀態。


推薦閱讀:

基於UDP實現的可靠傳輸協議(比如uTP),與TCP協議相比有什麼優缺點?
為什麼多 TCP 連接分塊下載比單連接下載快?
為什麼區域網的IP普遍是192.168開頭?

TAG:伺服器 | 計算機網路 | 遊戲伺服器 | TCP | TCP服務 |