23.1介紹
在一個(gè)空閑的(idle)TCP連接上,沒(méi)有任何的數(shù)據(jù)流,許多TCP/IP的初學(xué)者都對(duì)此感到驚奇。也就是說(shuō),如果TCP連接兩端沒(méi)有任何一個(gè)進(jìn)程在向?qū)Ψ桨l(fā)送數(shù)據(jù),那么在這兩個(gè)TCP模塊之間沒(méi)有任何的數(shù)據(jù)交換。你可能在其它的網(wǎng)絡(luò)協(xié)議中發(fā)現(xiàn)有輪詢(xún)(polling),但在TCP中它不存在。言外之意就是我們只要啟動(dòng)一個(gè)客戶(hù)端進(jìn)程,同服務(wù)器建立了TCP連接,不管你離開(kāi)幾小時(shí),幾天,幾星期或是幾個(gè)月,連接依舊存在。中間的路由器可能崩潰或者重啟,電話線可能go down或者back up,只要連接兩端的主機(jī)沒(méi)有重啟,連接依舊保持建立。
這就可以認(rèn)為不管是客戶(hù)端的還是服務(wù)器端的應(yīng)用程序都沒(méi)有應(yīng)用程序級(jí)(application-level)的定時(shí)器來(lái)探測(cè)連接的不活動(dòng)狀態(tài)(inactivity),從而引起任何一個(gè)應(yīng)用程序的終止。回憶在10.7結(jié)束,BGP每隔30秒就向?qū)Ψ桨l(fā)送一個(gè)應(yīng)用程序探測(cè)。這是一個(gè)應(yīng)用程序定時(shí)器(application timer),與TCP存活定時(shí)器不同。
然而有的時(shí)候,服務(wù)器需要知道客戶(hù)端主機(jī)是否已崩潰并且關(guān)閉,或者崩潰但重啟。許多實(shí)現(xiàn)提供了存活定時(shí)器來(lái)完成這個(gè)任務(wù)。
存活(keepalive)并不是TCP規(guī)范的一部分。在Host Requirements RFC羅列有不使用它的三個(gè)理由:(1)在短暫的故障期間,它們可能引起一個(gè)良好連接(good connection)被釋放(dropped),(2)它們消費(fèi)了不必要的寬帶,(3)在以數(shù)據(jù)包計(jì)費(fèi)的互聯(lián)網(wǎng)上它們(額外)花費(fèi)金錢(qián)。然而,在許多的實(shí)現(xiàn)中提供了存活定時(shí)器。
存活定時(shí)器是一個(gè)包含爭(zhēng)議的特征。許多人認(rèn)為,即使需要這個(gè)特征,這種對(duì)對(duì)方的輪詢(xún)也應(yīng)該由應(yīng)用程序來(lái)完成,而不是由TCP中實(shí)現(xiàn)。一些人對(duì)這個(gè)話題表現(xiàn)了極大的熱情,甚至達(dá)到宗教般的狂熱。
如果兩個(gè)終端系統(tǒng)之間的某個(gè)中間網(wǎng)絡(luò)上有連接的暫時(shí)中斷,那么存活選項(xiàng)(option)就能夠引起兩個(gè)進(jìn)程間一個(gè)良好連接的終止。例如,如果正好在某個(gè)中間路由器崩潰、重啟的時(shí)候發(fā)送存活探測(cè),TCP就將會(huì)認(rèn)為客戶(hù)端主機(jī)已經(jīng)崩潰,但事實(shí)并非如此。
一些服務(wù)器應(yīng)用程序可能代表客戶(hù)端占用資源,它們需要知道客戶(hù)端主機(jī)是否崩潰。存活定時(shí)器可以為這些應(yīng)用程序提供探測(cè)服務(wù)。Telnet服務(wù)器和Rlogin服務(wù)器的許多版本都默認(rèn)提供存活選項(xiàng)。
個(gè)人計(jì)算機(jī)用戶(hù)使用TCP/IP協(xié)議通過(guò)Telnet登錄一臺(tái)主機(jī),這是能夠說(shuō)明需要使用存活定時(shí)器的一個(gè)常用例子。如果某個(gè)用戶(hù)在使用結(jié)束時(shí)只是關(guān)掉了電源,而沒(méi)有注銷(xiāo)(log off),那么他就留下了一個(gè)半打開(kāi)(half-open)的連接。在圖18.16,我們看到如何在一個(gè)半打開(kāi)連接上通過(guò)發(fā)送數(shù)據(jù),得到一個(gè)復(fù)位(reset)返回,但那是在客戶(hù)端,是由客戶(hù)端發(fā)送的數(shù)據(jù)。如果客戶(hù)端消失,留給了服務(wù)器端半打開(kāi)的連接,并且服務(wù)器又在等待客戶(hù)端的數(shù)據(jù),那么等待將永遠(yuǎn)持續(xù)下去。存活特征的目的就是在服務(wù)器端檢測(cè)這種半打開(kāi)連接。
23.2 描述
在此描述中,我們稱(chēng)使用存活選項(xiàng)的那一段為服務(wù)器,另一端為客戶(hù)端。也可以在客戶(hù)端設(shè)置該選項(xiàng),且沒(méi)有不允許這樣做的理由,但通常設(shè)置在服務(wù)器。如果連接兩端都需要探測(cè)對(duì)方是否消失,那么就可以在兩端同時(shí)設(shè)置(比如NFS)。
若在一個(gè)給定連接上,兩小時(shí)之內(nèi)無(wú)任何活動(dòng),服務(wù)器便向客戶(hù)端發(fā)送一個(gè)探測(cè)段。(我們將在下面的例子中看到探測(cè)段的樣子。)客戶(hù)端主機(jī)必須是下列四種狀態(tài)之一:
1. 客戶(hù)端主機(jī)依舊活躍(up)運(yùn)行,并且從服務(wù)器可到達(dá)。從客戶(hù)端TCP的正常響應(yīng),服務(wù)器知道對(duì)方仍然活躍。服務(wù)器的TCP為接下來(lái)的兩小時(shí)復(fù)位存活定時(shí)器,如果在這兩個(gè)小時(shí)到期之前,連接上發(fā)生應(yīng)用程序的通信,則定時(shí)器重新為往下的兩小時(shí)復(fù)位,并且接著交換數(shù)據(jù)。
2. 客戶(hù)端已經(jīng)崩潰,或者已經(jīng)關(guān)閉(down),或者正在重啟過(guò)程中。在這兩種情況下,它的TCP都不會(huì)響應(yīng)。服務(wù)器沒(méi)有收到對(duì)其發(fā)出探測(cè)的響應(yīng),并且在75秒之后超時(shí)。服務(wù)器將總共發(fā)送10個(gè)這樣的探測(cè),每個(gè)探測(cè)75秒。如果沒(méi)有收到一個(gè)響應(yīng),它就認(rèn)為客戶(hù)端主機(jī)已經(jīng)關(guān)閉并終止連接。
3. 客戶(hù)端曾經(jīng)崩潰,但已經(jīng)重啟。這種情況下,服務(wù)器將會(huì)收到對(duì)其存活探測(cè)的響應(yīng),但該響應(yīng)是一個(gè)復(fù)位,從而引起服務(wù)器對(duì)連接的終止。
4. 客戶(hù)端主機(jī)活躍運(yùn)行,但從服務(wù)器不可到達(dá)。這與狀態(tài)2類(lèi)似,因?yàn)?/span>TCP無(wú)法區(qū)別它們兩個(gè)。它所能表明的僅是未收到對(duì)其探測(cè)的回復(fù)。
服務(wù)器不必?fù)?dān)心客戶(hù)端主機(jī)被關(guān)閉然后重啟的情況(這里指的是操作員執(zhí)行的正常關(guān)閉,而不是主機(jī)的崩潰)。當(dāng)系統(tǒng)被操作員關(guān)閉時(shí),所有的應(yīng)用程序進(jìn)程(也就是客戶(hù)端進(jìn)程)都將被終止,客戶(hù)端TCP會(huì)在連接上發(fā)送一個(gè)FIN。收到這個(gè)FIN后,服務(wù)器TCP向服務(wù)器進(jìn)程報(bào)告一個(gè)文件結(jié)束,以允許服務(wù)器檢測(cè)這種狀態(tài)。
在第一種狀態(tài)下,服務(wù)器應(yīng)用程序不知道存活探測(cè)是否發(fā)生。凡事都是由TCP層處理的,存活探測(cè)對(duì)應(yīng)用程序透明,直到后面2,3,4三種狀態(tài)發(fā)生。在這三種狀態(tài)下,通過(guò)服務(wù)器的TCP,返回給服務(wù)器應(yīng)用程序錯(cuò)誤信息。(通常服務(wù)器向網(wǎng)絡(luò)發(fā)出一個(gè)讀請(qǐng)求,等待客戶(hù)端的數(shù)據(jù)。如果存活特征返回一個(gè)錯(cuò)誤信息,則將該信息作為讀操作的返回值返回給服務(wù)器。)在狀態(tài)2,錯(cuò)誤信息類(lèi)似于“連接超時(shí)”。狀態(tài)3則為“連接被對(duì)方復(fù)位”。第四種狀態(tài)看起來(lái)像連接超時(shí),或者根據(jù)是否收到與該連接相關(guān)的ICMP錯(cuò)誤信息,而可能返回其它的錯(cuò)誤信息。
22.3 存活范例
檢查上一部分講述的狀態(tài)2,3,4,使用存活選項(xiàng)觀察包的交換。
對(duì)方崩潰
我們來(lái)看服務(wù)器主機(jī)崩潰而沒(méi)有重啟的情況。用下列步驟來(lái)模擬:
- 在客戶(hù)端(在主機(jī)bsdi運(yùn)行sock程序)和主機(jī)svr4上的標(biāo)準(zhǔn)echo服務(wù)器之間建立連接。客戶(hù)端用-K選項(xiàng)提供存活功能。
- 驗(yàn)證數(shù)據(jù)能夠在連接上傳輸
- 觀察客戶(hù)端TCP每隔兩小時(shí)發(fā)送的存活包和服務(wù)器TCP對(duì)它們的應(yīng)答
- 從服務(wù)器斷開(kāi)以太網(wǎng)線路,并保持?jǐn)嚅_(kāi)直到這個(gè)例子結(jié)束。這使得客戶(hù)端認(rèn)為服務(wù)器已經(jīng)崩潰
- 我們期望客戶(hù)端在斷言連接死亡之前,看到它發(fā)送的10個(gè)存活探測(cè)(每個(gè)間隔75秒)。
這是客戶(hù)端的交互式輸出:
bsdi % sock -K svr4 echo -K是存活選項(xiàng)
hello, world 開(kāi)始時(shí)輸入此行以驗(yàn)證連接活躍
hello, world 并看到echo
4小時(shí)后斷開(kāi)以太網(wǎng)線路
read error: Connection timed out 發(fā)生在啟動(dòng)后約6小時(shí)又11分
圖23.1顯示了tcpdump的輸出(已經(jīng)刪除了連接建立過(guò)程和通告的窗口)
圖23.1 判斷主機(jī)是否崩潰的存活包
行1,2,3從客戶(hù)端發(fā)送字符串”hello,world”到服務(wù)器,并且得到返回。第一個(gè)存活探測(cè)發(fā)生在2個(gè)小時(shí)(7200秒)之后(行4)。在行6的TCP段被發(fā)送之前,我們首先看到了一個(gè)ARP請(qǐng)求和一個(gè)ARP回復(fù)。行6的存活探測(cè)引起了另一端的響應(yīng)(行7)。兩個(gè)小時(shí)后,相同的順序包被交換(行8-11)。
如果我們注意觀察存活探測(cè)(行6和行10)中的所有域,就會(huì)發(fā)現(xiàn)它的順序號(hào)小于下一個(gè)將要發(fā)送的順序號(hào)(本例中是當(dāng)它應(yīng)該是14時(shí),這個(gè)順序號(hào)域出現(xiàn)的是13),但因?yàn)樵谠摱沃袥](méi)有數(shù)據(jù),所以tcpdump沒(méi)有打印順序號(hào)域(對(duì)于空段,tcpdump只在設(shè)置有SYN,FIN或RST標(biāo)志的時(shí)候才打印順序號(hào)。)正是這個(gè)不正確的順序號(hào)強(qiáng)迫服務(wù)器的TCP對(duì)存活探測(cè)做出響應(yīng)。該響應(yīng)告知客戶(hù)端服務(wù)器期望的下一順序號(hào)(14)。
然后我們斷開(kāi)線路,等待2小時(shí)后下一個(gè)探測(cè)的失敗。當(dāng)下一個(gè)探測(cè)發(fā)生時(shí),注意我們?cè)诰€路上看不到TCP段,這是由于主機(jī)對(duì)ARP請(qǐng)求沒(méi)有響應(yīng)。在放棄連接之前,我們?nèi)匀荒軌蚩吹娇蛻?hù)端每隔75秒發(fā)送了10個(gè)探測(cè)。從交互式腳本,我們看到由TCP返還給客戶(hù)端進(jìn)程的錯(cuò)誤碼被轉(zhuǎn)換成“連接超時(shí)”,事實(shí)如此。
對(duì)方崩潰并重啟
在這個(gè)例子中,我們將會(huì)看到客戶(hù)端崩潰和重啟的情況。初始的情況和上次相同,但在驗(yàn)證連接活躍之后,我們將服務(wù)器從以太網(wǎng)斷開(kāi),重新啟動(dòng),然后重新連接到以太網(wǎng)。我們期望下一個(gè)存活探測(cè)能使服務(wù)器產(chǎn)生一個(gè)復(fù)位,因?yàn)榉?wù)器現(xiàn)在對(duì)于這個(gè)連接一無(wú)所知。這是交互會(huì)話:
bsdi % sock -K svr4 echo -K是存活選項(xiàng)
hi, there 驗(yàn)證連接活躍
hi, there 這是對(duì)方的echo
以太網(wǎng)斷開(kāi),服務(wù)器重啟
read error: Connection reset by peer圖23.2
顯示了tcpdump
輸出(已經(jīng)刪除了連接建立過(guò)程和通告的窗口)。
圖23.2 當(dāng)對(duì)方主機(jī)崩潰且重啟時(shí)的存活例子
我們建立了一個(gè)連接,并且從客戶(hù)端向服務(wù)器發(fā)送了9個(gè)字節(jié)的數(shù)據(jù)(行1-3)。兩個(gè)小時(shí)后客戶(hù)端發(fā)送了第一個(gè)存活探測(cè),得到的響應(yīng)是服務(wù)器的一個(gè)復(fù)位。客戶(hù)端應(yīng)用程序打印了我們看得懂的錯(cuò)誤信息“連接被對(duì)方復(fù)位”。
另一端不可到達(dá)
在這個(gè)例子中,客戶(hù)端沒(méi)有崩潰,但是在存活探測(cè)被發(fā)送的10分鐘內(nèi)不可到達(dá)。中間的路由器可能崩潰了,或者電話線暫時(shí)處于無(wú)序狀態(tài),或者發(fā)生了其它類(lèi)似情況。
為模擬這個(gè)例子,我們將建立一個(gè)通過(guò)撥號(hào)SLIP鏈路從主機(jī)slip到vangogh.cs.berkeley.edu的TCP連接,然后斷掉鏈路。首先,交互式輸出如下:
bsdi % sock -K vangogh.cs.berkeley.edu echo
testing
testing
在某個(gè)時(shí)候撥號(hào)SLIP鏈路被斷開(kāi) read error: No route to host
圖23.3顯示了在路由器bsdi收集的tcpdump輸出(已經(jīng)刪除了連接建立過(guò)程和通告的窗口)。
圖23.3當(dāng)對(duì)方不可到達(dá)時(shí)的存活例子
我們?nèi)缦惹耙粯拥亻_(kāi)始分析這個(gè)例子:行1-3驗(yàn)證了連接是活躍的。兩小時(shí)后的第一個(gè)存活探測(cè)正常(行4和行5),但在下一個(gè)兩小時(shí)的存活探測(cè)發(fā)出之前,我們斷開(kāi)了路由器sun和netb的SLIP連接。(參考相冊(cè)的拓?fù)鋱D)
第六行的存活探測(cè)引發(fā)了來(lái)自路由器sun網(wǎng)絡(luò)不可到達(dá)的ICMP。對(duì)于路由器slip上的接收TCP來(lái)說(shuō),這是一個(gè)軟件錯(cuò)誤。它記錄收到了一個(gè)ICMP錯(cuò)誤,但是錯(cuò)誤的接收并不引起終止這個(gè)連接。在發(fā)出主機(jī)放棄之前,另外8個(gè)存活探測(cè)間隔75秒相繼被發(fā)出。這一次返回給應(yīng)用程序的錯(cuò)誤產(chǎn)生了不同的信息“沒(méi)有達(dá)到主機(jī)的路由”。
23.4 小結(jié)(略)