23.1介紹
在一個空閑的(idle)TCP連接上,沒有任何的數據流,許多TCP/IP的初學者都對此感到驚奇。也就是說,如果TCP連接兩端沒有任何一個進程在向對方發送數據,那么在這兩個TCP模塊之間沒有任何的數據交換。你可能在其它的網絡協議中發現有輪詢(polling),但在TCP中它不存在。言外之意就是我們只要啟動一個客戶端進程,同服務器建立了TCP連接,不管你離開幾小時,幾天,幾星期或是幾個月,連接依舊存在。中間的路由器可能崩潰或者重啟,電話線可能go down或者back up,只要連接兩端的主機沒有重啟,連接依舊保持建立。
這就可以認為不管是客戶端的還是服務器端的應用程序都沒有應用程序級(application-level)的定時器來探測連接的不活動狀態(inactivity),從而引起任何一個應用程序的終止。回憶在10.7結束,BGP每隔30秒就向對方發送一個應用程序探測。這是一個應用程序定時器(application timer),與TCP存活定時器不同。
然而有的時候,服務器需要知道客戶端主機是否已崩潰并且關閉,或者崩潰但重啟。許多實現提供了存活定時器來完成這個任務。
存活(keepalive)并不是TCP規范的一部分。在Host Requirements RFC羅列有不使用它的三個理由:(1)在短暫的故障期間,它們可能引起一個良好連接(good connection)被釋放(dropped),(2)它們消費了不必要的寬帶,(3)在以數據包計費的互聯網上它們(額外)花費金錢。然而,在許多的實現中提供了存活定時器。
存活定時器是一個包含爭議的特征。許多人認為,即使需要這個特征,這種對對方的輪詢也應該由應用程序來完成,而不是由TCP中實現。一些人對這個話題表現了極大的熱情,甚至達到宗教般的狂熱。
如果兩個終端系統之間的某個中間網絡上有連接的暫時中斷,那么存活選項(option)就能夠引起兩個進程間一個良好連接的終止。例如,如果正好在某個中間路由器崩潰、重啟的時候發送存活探測,TCP就將會認為客戶端主機已經崩潰,但事實并非如此。
一些服務器應用程序可能代表客戶端占用資源,它們需要知道客戶端主機是否崩潰。存活定時器可以為這些應用程序提供探測服務。Telnet服務器和Rlogin服務器的許多版本都默認提供存活選項。
個人計算機用戶使用TCP/IP協議通過Telnet登錄一臺主機,這是能夠說明需要使用存活定時器的一個常用例子。如果某個用戶在使用結束時只是關掉了電源,而沒有注銷(log off),那么他就留下了一個半打開(half-open)的連接。在圖18.16,我們看到如何在一個半打開連接上通過發送數據,得到一個復位(reset)返回,但那是在客戶端,是由客戶端發送的數據。如果客戶端消失,留給了服務器端半打開的連接,并且服務器又在等待客戶端的數據,那么等待將永遠持續下去。存活特征的目的就是在服務器端檢測這種半打開連接。
23.2 描述
在此描述中,我們稱使用存活選項的那一段為服務器,另一端為客戶端。也可以在客戶端設置該選項,且沒有不允許這樣做的理由,但通常設置在服務器。如果連接兩端都需要探測對方是否消失,那么就可以在兩端同時設置(比如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錯誤信息,而可能返回其它的錯誤信息。
22.3 存活范例
檢查上一部分講述的狀態2,3,4,使用存活選項觀察包的交換。
對方崩潰
我們來看服務器主機崩潰而沒有重啟的情況。用下列步驟來模擬:
- 在客戶端(在主機bsdi運行sock程序)和主機svr4上的標準echo服務器之間建立連接。客戶端用-K選項提供存活功能。
- 觀察客戶端TCP每隔兩小時發送的存活包和服務器TCP對它們的應答
- 從服務器斷開以太網線路,并保持斷開直到這個例子結束。這使得客戶端認為服務器已經崩潰
- 我們期望客戶端在斷言連接死亡之前,看到它發送的10個存活探測(每個間隔75秒)。
這是客戶端的交互式輸出:
bsdi % sock -K svr4 echo -K是存活選項
hello, world 開始時輸入此行以驗證連接活躍
hello, world 并看到echo
4小時后斷開以太網線路
read error: Connection timed out 發生在啟動后約6小時又11分
圖23.1顯示了tcpdump的輸出(已經刪除了連接建立過程和通告的窗口)
圖23.1 判斷主機是否崩潰的存活包
行1,2,3從客戶端發送字符串”hello,world”到服務器,并且得到返回。第一個存活探測發生在2個小時(7200秒)之后(行4)。在行6的TCP段被發送之前,我們首先看到了一個ARP請求和一個ARP回復。行6的存活探測引起了另一端的響應(行7)。兩個小時后,相同的順序包被交換(行8-11)。
如果我們注意觀察存活探測(行6和行10)中的所有域,就會發現它的順序號小于下一個將要發送的順序號(本例中是當它應該是14時,這個順序號域出現的是13),但因為在該段中沒有數據,所以tcpdump沒有打印順序號域(對于空段,tcpdump只在設置有SYN,FIN或RST標志的時候才打印順序號。)正是這個不正確的順序號強迫服務器的TCP對存活探測做出響應。該響應告知客戶端服務器期望的下一順序號(14)。
然后我們斷開線路,等待2小時后下一個探測的失敗。當下一個探測發生時,注意我們在線路上看不到TCP段,這是由于主機對ARP請求沒有響應。在放棄連接之前,我們仍然能夠看到客戶端每隔75秒發送了10個探測。從交互式腳本,我們看到由TCP返還給客戶端進程的錯誤碼被轉換成“連接超時”,事實如此。
對方崩潰并重啟
在這個例子中,我們將會看到客戶端崩潰和重啟的情況。初始的情況和上次相同,但在驗證連接活躍之后,我們將服務器從以太網斷開,重新啟動,然后重新連接到以太網。我們期望下一個存活探測能使服務器產生一個復位,因為服務器現在對于這個連接一無所知。這是交互會話:
bsdi % sock -K svr4 echo -K是存活選項
hi, there 驗證連接活躍
hi, there 這是對方的echo
以太網斷開,服務器重啟
read error: Connection reset by peer圖23.2
顯示了tcpdump
輸出(已經刪除了連接建立過程和通告的窗口)。
圖23.2 當對方主機崩潰且重啟時的存活例子
我們建立了一個連接,并且從客戶端向服務器發送了9個字節的數據(行1-3)。兩個小時后客戶端發送了第一個存活探測,得到的響應是服務器的一個復位。客戶端應用程序打印了我們看得懂的錯誤信息“連接被對方復位”。
另一端不可到達
在這個例子中,客戶端沒有崩潰,但是在存活探測被發送的10分鐘內不可到達。中間的路由器可能崩潰了,或者電話線暫時處于無序狀態,或者發生了其它類似情況。
為模擬這個例子,我們將建立一個通過撥號SLIP鏈路從主機slip到vangogh.cs.berkeley.edu的TCP連接,然后斷掉鏈路。首先,交互式輸出如下:
bsdi % sock -K vangogh.cs.berkeley.edu echo
testing
testing
在某個時候撥號SLIP鏈路被斷開 read error: No route to host
圖23.3顯示了在路由器bsdi收集的tcpdump輸出(已經刪除了連接建立過程和通告的窗口)。
圖23.3當對方不可到達時的存活例子
我們如先前一樣地開始分析這個例子:行1-3驗證了連接是活躍的。兩小時后的第一個存活探測正常(行4和行5),但在下一個兩小時的存活探測發出之前,我們斷開了路由器sun和netb的SLIP連接。(參考相冊的拓撲圖)
第六行的存活探測引發了來自路由器sun網絡不可到達的ICMP。對于路由器slip上的接收TCP來說,這是一個軟件錯誤。它記錄收到了一個ICMP錯誤,但是錯誤的接收并不引起終止這個連接。在發出主機放棄之前,另外8個存活探測間隔75秒相繼被發出。這一次返回給應用程序的錯誤產生了不同的信息“沒有達到主機的路由”。
23.4 小結(略)