TCP學習總結(二):連接的建立與終止

1、建立連接之三次握手

TCP是一個面向連接的協議。無論哪一方向另一方發送數據之前,都必須現在雙方之間建立一條連接建立一條TCP連接分爲三步:

  1. 請求端(通常稱爲客戶端)發送一個SYN段指明客戶打算連接的服務器的端口,以及初始序號(ISN,在這個例子中爲1415531521)。這個SYN段爲報文段1。
  2. 服務器發回包含服務器的初始序號的SYN報文段(報文段2)作爲應答。同時,將確認序號設置爲客戶的ISN加1以對客戶的SYN報文段進行確認。一個SYN將佔用一個序號。
  3. 客戶必須將確認序號設置爲服務器的ISN加1以對服務器的SYN報文段進行確認(報文段3)。

這三個報文段完成連接的建立。這個過程也稱爲三次握手(three-way handshake)。
1.jpg
                 圖1:建立連接與終止連接

發送第一個SYN的一端將執行主動打開(active open)。接受這個SYN併發回下一個SYN的另一端執行被動打開(passive open)(後續章節將介紹雙方如何都執行主動打開)。

當一端爲建立連接而發送它的SYN時,它爲連接選擇一個初始序號。ISN隨時間而變化,因此每個連接都具有不同的ISN。RFC793指出ISN可以看作是一個32比特的計數器,每4ms加1。這樣選擇序號的目的在於放置在網絡中被延遲的分組在以後又被傳送,而導致某個連接的一方對它作錯誤的解釋。

    如何進行序號選擇?在4.4BSD中,系統初始化時初始的發送序號被初始化爲1。這種方法違背了Host Requirement RFC(在這個代碼中的一個註釋確認這是一個錯誤)。這個變量每0.5秒增加64000,並每隔9.5小時又回到0(對應這個計數器每8ms加1,而不是每4ms加1)。另外,每次建立一個連接後,這個變量將增加64000。

2、斷開連接之四次揮手

建立一個連接需要三次握手,而斷開一個連接時要經過4次揮手。這由TCP的半連接(half-close)造成的。既然一個TCP連接是全雙工的(即數據在兩個方向上能同時傳遞),因此每個方向必須單獨的進行關閉。原則就是當一方完成它的數據發送任務之後就能發送一個FIN來終止這個方向上的連接。當一端收到一個FIN時,它必須通知應用層另一端已經終止了那個方向上的數據傳送。發送FIN通常是應用層進行關閉的結果。

收到一個FIN只意味着在這一方向上沒有數據流動。一個TCP連接在收到一個FIN後仍能發送數據。而這對利用半關閉的應用來說是可能的,儘管在實際應用中只有很少的TCP應用程序這樣做,正常關閉過程如圖1所示。

首先進行關閉的一方(即發送第一個FIN)將執行主動關閉,而另一方(收到這個FIN)將執行被動關閉。通常一方完成主動關閉而另一方完成被動關閉,後面我們將看到雙方如何都執行主動關閉。

圖1中的報文段4發起終止連接,它由客戶端關閉連接時發出。當服務器收到這個FIN時,它發回一個ACK,確認序號爲收到的序號加1(報文段5)。和SYN一樣,一個FIN將佔用一個序號。同時,TCP服務器還向應用程序(即丟棄服務器)傳送一個文件結束符。接着這個服務器程序就關閉它的連接,導致它的TCP端發送一個FIN(報文段6),客戶必須發回一個確認,並將確認序號設置爲收到序號加1(報文段7)。
2.jpg
           圖2:連接終止期間報文段的正常交換

圖2顯示了終止一個連接的典型握手順序。我們省略了序號。在這個圖中發送FIN將導致應用程序關閉它們的連接,這些FIN的ACK都是由TCP軟件自動產生的。

3、最大報文段長度

最大報文段長度(MSS)表示TCP傳往另一端的最大塊數據的長度。當一個連接建立時,連接的雙方都要通告各自的MSS。我們已經見過MSS都是1024,這導致IP數據報通常是40字節長:20字節的TCP首部和20字節的IP首部。當建立一個連接時,每一方都有用於通告它期望接收的MSS選項(MSS選項只能出現在SYN報文段中)。如果一方不接收來自另一方的MSS值,則MSS就定爲默認值536字節(這個默認值允許20字節的IP首部和20字節的TCP首部以適合576字節的IP數據報)。

一般來說,如果沒有分段發生,MSS還是越大越好(這也並不總是正確),報文段越大允許每個報文段傳送的數據就越多,相對IP和TCP首部有更高的網絡利用率。當TCP發送一個SYN時,或者是因爲一個本地應用進程想發起一個連接,或者是因爲另一端的主機收到了一個連接請求,它能將MSS值設置爲外出接口上的MTU長度減去固定的IP首部和TCP首部長度。對一個以太網,MSS值可達1460字節。

4、TCP的半關閉

TCP提供了連接的一端在結束它的發送後還能接收來自另一端數據的能力,這就是所謂的半關閉。正如我們上一篇提到的只有很少的應用程序使用它。

爲了使用這個特性,編程接口必須爲應用程序提供一種方式來說明“我已經完成了數據傳送,因此發送一個文件結束(FIN)給另一端,但我還想接收另一端發來的數據,直到它給我發來文件結束(FIN)”。
3.jpg
                 圖3:TCP半關閉的例子
圖3顯示了一個半關閉的典型例子。讓左方的客戶端開始半關閉,當然也可以由另一端開始。開始的兩個報文段和圖2是相同的:初始端發出的FIN,接着是另一端對這個FIN的ACK報文段。但後面就和圖2不同,因爲接收半關閉的一方仍能發送數據。我們只顯示一個數據報文段和一個ACK報文段,但實際可能發送了許多數據報文段。當收到半關閉的一端在完成它的數據傳送後,將發送一個FIN關閉這個方向的連接,這將傳送一個文件結束符給發起這個半關閉的應用進程。當對第二個FIN進行確認後,這個連接並徹底關閉了。

5、TCP的狀態變遷圖

我們已經介紹了有關發起和終止TCP連接的規則,這些規則都能從圖4所示的狀態變遷圖中得出。
4.jpg
                 圖4:TCP的狀態變遷圖
在這個圖中要注意的第一點是一個狀態變遷的子集是“典型的”。我們用粗的實線箭頭表示正常的客戶端狀態變遷,用粗的虛線箭頭表示正常的服務器狀態變遷。

第二點是兩個導致進入ESTABLISHED狀態的變遷對應打開一個連接,而兩個導致從ESTABLISHED狀態離開的變遷對應關閉一個連接。ESTABLISHED狀態是連接雙方能夠進行雙向數據傳輸的狀態。

在圖4中左下角4個狀態放在一個虛線框內,並標爲“主動關閉”,其他兩個狀態(CLOSE_WAIT和LAST_ACK)也用虛線框住,並 標爲“被動關閉”。只有當SYN_RCVD狀態是從LISTEN狀態(正常情況)進入,而不是從SYN_SEND(同時打開)進入時,從SYN_RCVD回到LISTEN的狀態變遷纔是有效的。這意味着如果我們執行被動關閉(進入LISTEN),收到一個SYN,發送一個帶ACK的SYN(進入SYN_RCVD),然後收到一個RST,而不是一個ACK,便又回到LISTEN狀態並等待另一個連接請求的到來。

5.jpg
           圖5:TCP正常連接建立和終止所對應的狀態

圖5顯示了在正常的TCP連接的建立和終止過程中,客戶與服務器所經歷的不同狀態

6、2MSL等待狀態

TIME_WAIT狀態也稱爲2MSL等待狀態。每個具體的TCP實現必須選擇一個報文段最大生存時間MSL(Maximum Segment Lifetime)。它是任何報文段被丟棄前在網絡內的最長時間。我們知道這個時間是有限的,因爲TCP報文段以IP數據報在網絡內傳輸,而IP數據報則有限制其生存時間的TTL字段。

RFC793指出MSL爲2分鐘。然而,實現中通常值是30秒,1分鐘或者2分鐘。對IP數據報TTL的限制是基於跳數,而不是定時器。對一個具體實現所給定的MSL值,處理原則是:當TCP執行一個主動關閉,併發回最後一個ACK,該連接必須在TIME_WAIT狀態停留的時間爲2倍的MSL,這樣可以讓TCP再次發送最後的ACK以防止這個ACK丟失(另一端超時並重發最後的FIN)。這種2MSL等待的另一個結果是這個TCP連接在2MSL等待期間,定義這個連接的插口(客戶的IP地址和端口號,服務器的IP地址和端口號)不能再被使用。這個連接的插口只能在2MSL結束後才能再被使用。大多數TCP實現強加了更爲嚴格的限制,在2MSL等待期間,插口中使用的本地端口在默認情況下不能再被使用。

在連接處於2MSL等待時,任何遲到的報文將被丟棄。因爲處於2MSL等待的、由該插口對(socket pair)定義的連接在這段時間內不能被再用,因此當要建立一個有效的連接時,來自該連接的一個較早替身( incarnation)的遲到報文作爲新連接的一部分不可能不被曲解(一個連接由一個插口對來定義。一個連接的新的實例(instance)稱爲該連接的替身)。

我們說圖4中客戶執行主動關閉並進入TIME_WAIT是正常的。服務器通常執行被動關閉,不會進入TIME_WAIT狀態。這暗示如果我們終止一個客戶程序,並立即重新啓動這個客戶程序,則這個新客戶程序將不能重用相同的本地端口。這不會帶來什麼問題,因爲客戶使用本地端口,而並不關心這個端口號是什麼。

然而,對於服務器,情況就有所不同,因爲服務器使用熟知端口。如果我們終止一個已經建立連接的服務器程序,並試圖立即重新啓動這個服務器程序,服務器程序將不能把它的這個熟知端口賦值給它的端點,因爲那個端口是處於2MSL連接的一部分。在重新啓動服務器程序前,它需要在1 ~ 4分鐘。

對於來自某個連接的較早替身的遲到報文段,2MSL等待可防止將它解釋成使用相同插口對的新連接的一部分。但這只有在處於2MSL等待連接中的主機處於正常工作狀態時纔有效。如果使用處於2MSL等待端口的主機出現故障,它會在MSL秒內重新啓動,並立即使用故障前仍處於2MSL的插口對來建立一個新的連接,如果是這樣,在故障前從這個連接發出而遲到的報文段會被錯誤地當作屬於重啓後新連接的報文段。無論如何選擇重啓後新連接的初始序號,都會發生這種情況。

爲了防止這種情況, RFC 793指出TCP在重啓動後的MSL秒內不能建立任何連接。這就稱爲平靜時間 (quiet time)。 只有極少的實現版遵守這一原則,因爲大多數主機重啓動的時間都比MSL秒要長。

7、同時打開和同時關閉

兩個應用程序同時彼此執行主動打開的情況是可能的,儘管發生的可能性極小。每一方必須發送一個SYN,且這些SYN必須傳遞給對方。這需要每一方使用一個對方熟知的端口作爲本地端口。這又稱爲同時打開( simultaneous open)。

TCP是特意設計爲了可以處理同時打開,對於同時打開它僅建立一條連接而不是兩條連接。當出現同時打開的情況時,狀態變遷與圖5所示的不同。兩端幾乎在同時發送SYN,並進入SYN_SEND狀態。當每一端收到SYN時,狀態變爲SYN_RCVD(如圖4),同時它們都再發SYN並對收到的SYN進行確認。當雙方都收到SYN及相應的ACK時,狀態都變遷爲ESTABLISHED。圖6顯示了這些狀態變遷過程。

6.jpg
           圖6:同時打開期間報文段的交換

一個同時打開的連接需要交換4個報文段,比正常的3次握手多一個。此外,要注意的是我們沒有將任何一端稱爲客戶或服務器,因爲每一端既是客戶又是服務器。

我們在以前討論過一方(通常但不總是客戶方)發送第一個FIN執行主動關閉。雙方都執行主動關閉也是可能的, TCP協議也允許這樣的同時關閉( simultaneous close)。

在圖4中,當應用層發出關閉命令時,兩端均從ESTABLISHED變爲FIN_WAIT_1。這將導致雙方各發送一個FIN,兩個FIN經過網絡傳送後分別到達另一端。收到FIN後,狀態由FIN_WAIT_1變遷到CLOSING,併發送最後的ACK。當收到最後的ACK時,狀態變化爲TIME_WAIT。圖7總結了這些狀態的變化。

7.jpg
           圖7:同時關閉期間報文段的交換

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章