原文出自:http://www.cnblogs.com/youxin/p/4056041.html
一篇文章:css
23.1介紹html
在一個空閒的(idle)TCP鏈接上,沒有任何的數據流,許多TCP/IP的初學者都對此感到驚奇。也就是說,若是TCP鏈接兩端沒有任何一個進程在向對方發送數據,那麼在這兩個TCP模塊之間沒有任何的數據交換。你可能在其它的網絡協議中發現有輪詢(polling),但在TCP中它不存在。言外之意就是咱們只要啓動一個客戶端進程,同服務器創建了TCP鏈接,無論你離開幾小時,幾天,幾星期或是幾個月,鏈接依舊存在。中間的路由器可能崩潰或者重啓,電話線可能go down或者back up,只要鏈接兩端的主機沒有重啓,鏈接依舊保持創建。linux
這就能夠認爲無論是客戶端的仍是服務器端的應用程序都沒有應用程序級(application-level)的定時器來探測鏈接的不活動狀態(inactivity),從而引發任何一個應用程序的終止。回憶在10.7結束,BGP每隔30秒就向對方發送一個應用程序探測。這是一個應用程序定時器(application timer),與TCP存活定時器不一樣。web
然而有的時候,服務器須要知道客戶端主機是否已崩潰而且關閉,或者崩潰但重啓。許多實現提供了存活定時器來完成這個任務。shell
存活(keepalive)並非TCP規範的一部分。在Host Requirements RFC羅列有不使用它的三個理由:(1)在短暫的故障期間,它們可能引發一個良好鏈接(good connection)被釋放(dropped),(2)它們消費了沒必要要的寬帶,(3)在以數據包計費的互聯網上它們(額外)花費金錢。然而,在許多的實現中提供了存活定時器。編程
存活定時器是一個包含爭議的特徵。許多人認爲,即便須要這個特徵,這種對對方的輪詢也應該由應用程序來完成,而不是由TCP中實現。一些人對這個話題表現了極大的熱情,甚至達到宗教般的狂熱。服務器
若是兩個終端系統之間的某個中間網絡上有鏈接的暫時中斷,那麼存活選項(option)就可以引發兩個進程間一個良好鏈接的終止。例如,若是正好在某個中間路由器崩潰、重啓的時候發送存活探測,TCP就將會認爲客戶端主機已經崩潰,但事實並不是如此。網絡
一些服務器應用程序可能表明客戶端佔用資源,它們須要知道客戶端主機是否崩潰。存活定時器能夠爲這些應用程序提供探測服務。Telnet服務器和Rlogin服務器的許多版本都默認提供存活選項。app
我的計算機用戶使用TCP/IP協議經過Telnet登陸一臺主機,這是可以說明須要使用存活定時器的一個經常使用例子。若是某個用戶在使用結束時只是關掉了電源,而沒有註銷(log off),那麼他就留下了一個半打開(half-open)的鏈接。在圖18.16,咱們看到如何在一個半打開鏈接上經過發送數據,獲得一個復位(reset)返回,但那是在客戶端,是由客戶端發送的數據。若是客戶端消失,留給了服務器端半打開的鏈接,而且服務器又在等待客戶端的數據,那麼等待將永遠持續下去。存活特徵的目的就是在服務器端檢測這種半打開鏈接。socket
/* Net check Make sure you have not used OUT OF BAND DATA AND YOU CAN use OOB */ int netcheck(int fd) { int buf_size = 1024; char buf[buf_size]; //clear OOB DATA recv(fd, buf, buf_size); if(send(fd, (void *)"\0", 1, MSG_OOB) < 0 ) { fprintf(stderr, "Connection[%d] send OOB failed, %s", fd, strerror(errno)); return -1; } return 0; }
/* Setting SO_TCP KEEPALIVE */ //int keep_alive = 1;//設定KeepAlive //int keep_idle = 1;//開始首次KeepAlive探測前的TCP空閉時間 //int keep_interval = 1;//兩次KeepAlive探測間的時間間隔 //int keep_count = 3;//斷定斷開前的KeepAlive探測次數 void set_keepalive(int fd, int keep_alive, int keep_idle, int keep_interval, int keep_count) { int opt = 1; if(keep_alive) { if(setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keep_alive, sizeof(keep_alive)) == -1) { fprintf(stderr, "setsockopt SOL_SOCKET::SO_KEEPALIVE failed, %s\n",strerror(errno)); } if(setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (void *)&keep_idle,sizeof(keep_idle)) == -1) { fprintf(stderr, "setsockopt SOL_TCP::TCP_KEEPIDLE failed, %s\n", strerror(errno)); } if(setsockopt(fd,SOL_TCP,TCP_KEEPINTVL, (void *)&keep_interval, sizeof(keep_interval)) == -1) { fprintf(stderr, "setsockopt SOL_tcp::TCP_KEEPINTVL failed, %s\n", strerror(errno)); } if(setsockopt(fd,SOL_TCP,TCP_KEEPCNT, (void *)&keep_count,sizeof(keep_count)) == -1) { fprintf(stderr, "setsockopt SOL_TCP::TCP_KEEPCNT failed, %s\n", strerror(errno)); } } }
這周在上班的路上看了本書《Effective TCP/IP Programming》,如下是一些讀書筆記。順帶推薦一下這本書,寫的很棒,適用於像我這樣常常要寫一些有必定質量的網絡編程,但又沒時間啃那些講解TCPIP協議大部頭書的人。
不少人都知道TCP並不會去主動檢測鏈接的丟失,這意味着,若是雙方不產生交互,那麼若是網絡斷了或者有一方機器崩潰,另一方將永遠不知道鏈接已經不可用了。檢測鏈接是否丟失的方法大體有兩種:keepalive和heart-beat。
Keepalive是不少的TCP實現提供的一種機制,它容許鏈接在空閒的時候雙方會發送一些特殊的數據段,並經過響應與否來判斷鏈接是否還存活着(所謂keep~~alive)。我曾經寫過一篇關於keepalive的blog ,但後來我也發現,其實keepalive在實際的應用中並不常見。爲什麼如此?這得歸結於keepalive設計的初衷。Keepalive適用於清除死亡時間比較長的鏈接。
好比這樣的場景:一個用戶建立tcp鏈接訪問了一個web服務器,當用戶完成他執行的操做後,很粗暴的直接撥了網線。這種狀況下,這個tcp鏈接已經斷開了,可是web服務器並不知道,它會依然守護着這個鏈接。若是web server設置了keepalive,那麼它就可以在用戶斷開網線的大概幾個小時之後,確認這個鏈接已經中斷,而後丟棄此鏈接,回收資源。
採用keepalive,它會先要求此鏈接必定時間沒有活動(通常是幾個小時),而後發出數據段,通過屢次嘗試後(每次嘗試之間也有時間間隔),若是仍沒有響應,則判斷鏈接中斷。可想而知,整個週期須要很長的時間。
因此,如前面的場景那樣,須要一種方法可以清除和回收那些在系統不知情的狀況下死去了好久的鏈接,keepalive是很是好的選擇。
可是,在大部分狀況下,特別是分佈式環境中,咱們須要的是一個可以快速或者實時監控鏈接狀態的機制,這裏,heart-beat纔是更加合適的方案。
Heart-beat(心跳),按個人理解,它的原理和keepalive很是相似,都是發送一個信號給對方,若是屢次發送都沒有響應的話,則判斷鏈接中斷。它們的不一樣點在於,keepalive是tcp實現中內建的機制,是在建立tcp鏈接時經過設置參數啓動keepalive機制;而heart-beat則須要在tcp之上的應用層實現。一個簡單的heart-beat實現通常測試鏈接是否中斷採用的時間間隔都比較短,能夠很快的決定鏈接是否中斷。而且,因爲是在應用層實現,由於能夠自行決定當判斷鏈接中斷後應該採起的行爲,而keepalive在判斷鏈接失敗後只會將鏈接丟棄。 關於heart-beat,一個很是有趣的問題是,應該在傳輸真正數據的鏈接中發送「心跳」信號,仍是能夠專門建立一個發送「心跳」信號的鏈接。好比說,A,B兩臺機器之間經過鏈接m來傳輸數據,如今爲了可以檢測A,B之間的鏈接狀態,咱們是應該在鏈接m中傳輸「心跳」信號,仍是建立新的鏈接n來專門傳輸「心跳」呢?我我的認爲二者皆可。若是擔憂的是端到端的鏈接狀態,那麼就直接在該條鏈接中實現「心跳」。但不少時候,關注的是網絡情況和兩臺主機間的鏈接狀態,這種狀況下, 建立專門的「心跳」鏈接也何嘗不可。