TCP建立與終止

TCP是一個面向連接的協議。無論哪一方向另一方發送數據之前,都必須在雙方之間建立一條連接。這種兩端間連接的建立與無連接協議UDP不同,UDP向另一端發送數據報時,無需任何預告的握手。


1.建立連接的協議(3次握手)

1)請求端發送一個SYN段指明客戶端打算連接的服務器端口,以及初始序列號。

2)服務器發回包含服務器的初始序號的SYN報文段作爲應答。同時將確認序號設置爲客戶的ISN加1以對客戶的SYN報文段進行確認。一個SYN將佔用一個序號。

3)客戶端將確認序號設置爲服務器的ISN加1以對服務器的SYN報文段進行確認

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



2.連接終止協議

建立一個連接需要3次握手,爲什麼終止一個連接需要4次握手?這由TCP的半關閉造成的。既然一個TCP連接是全雙工(數據在兩個方向能同時傳遞),因此每個方向必須單獨地進行關閉。這原則就是當一方完成它的數據發送後就能發送一個FIN來終止這個方向數據。當一端收到一個FIN,它必須通知應用層另一端已經終止了那個方向的數據傳送。

收到FIN包意味着在這一方向上沒有數據流動(接收)。一個TCP連接在收到一個FIN後仍能發送數據。


3.最大報文段長度

最大報文段長度(MSS)表示TCP傳往另一端的最大數據長度(不包括20字節的IP首部和TCP首部)。當一個連接建立時,連接的雙方都要通告各自的MSS,MSS只能出現在SYN報文段中,這是一個“協商”選項,並不是強制性的。如果一方不接收來自另一方的MSS值,則MSS默認值爲536字節。

MSS值一般爲鏈路層的MTU減去20字節的IP首部和20字節的TCP首部. MSS = MTU - 40Byte

一般來說,如果沒有分段發生,MSS還是越大越好。報文段越大允許每個報文段傳送的數據就越多,相對IP和TCP首部有更高的網絡利用率。

MSS讓主機限制另一端發送數據報的長度。加上主機也能控制它發送數據報的長度,這將使以較小MTU連接到網絡上的主機避免分段。


4.TCP半關閉

TCP提供了連接的一端在結束它的發送後還能接收來自另一端數據的能力。這就是所謂的半關閉。

當客戶端發送FIN到服務端,服務端返回這個FIN的ACK後,並沒有向客戶端發送FIN報文段,這段時間內就叫半關閉狀態。此時客戶端不能向服務端發送數據,但可以接收服務端發送給它的數據。(書中有一個半關閉使用的例子)

沒有半關閉,需要其它的一些技術讓客戶端通知服務器,客戶端已經完成了它的數據傳送,但仍要接收來自服務器的數據。使用兩個TCP連接也可以作爲一個選擇,但使用半關閉的單連接更好。


5.TCP狀態變遷圖

5.1 這是一個看起來比較複雜的狀態遷移圖,因爲它包含了兩個部分---服務器的狀態遷移和客戶端的狀態變遷。



TCP正常連接建立和終止所對應的狀態,這11個狀態中較重要的部分,在以後的博文再進行講解。


5.2 2MSL等待狀態

TIME_WAIT狀態也稱爲2MSL狀態。每個具體TCP實現必須選擇一個報文段最大生存時間MSL,它是任何報文段被丟棄前在網絡內的最長時間,而這個時間是有限的。

當TCP執行主動關閉,併發回最後一個ACK,該連接必須在TIME_WAIT狀態停留2倍的MSL時間。這樣可以讓TCP再次發送最後的ACK以防止這個ACK丟失(另一端超時並重發最後的FIN)。

插口對(socket pair):(包括服務端ip,port和客戶端ip,port的四元組)唯一確定每個TCP連接。

這種2MSL等待的另一個結果是這個TCP連接等待期間,定義這個連接的插口不能再被使用,這個連接只能在2MSL結束才能再被使用。

在連接處於2MSL等待時,任何遲到的報文段將被丟棄,因爲處於連接的插口不能被再用,因此當要建立一個有效的連接時,來自該連接的一個較早替身的遲到報文段作爲新連接的一部分很有可能被曲解。

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

然而,對於服務器就存在問題,因爲服務器使用熟知端口。如果我們終止一個已經建立連接的服務器程序,並試圖立即重啓這個服務器程序,這個熟知端口不能賦值給服務器程序,因爲該端口是處於2MSL連接的一部分。在重啓服務器程序前,它需要1~4分鐘。(如果我們一直試圖重新啓動服務器程序,並測試它直到成功所需的時間,我們就能估算出2MSL值)

TCP實現加強了該限制,默認在2MSL等待狀態下,插口中使用的本地端口也不能使用,但通過SO_REUSEADDR參數可以重用該端口。

注意:2MSL狀態時該插口對不能使用是針對於主動關閉一方。 假如服務端主動關閉,然後用SO_REUSEADDR重用該端口,如果直接用2MSL的插口對連接客戶端將不會成功,但如果客戶端使用插口對中的端口來連接服務端,那麼將是成功的,雖然該插口對還處於2MSL狀態。即允許一個連接到達仍處於2MSL狀態的連接。


5.3 平靜等待時間

對於來自某個連接的較早替身的遲到報文段,2MSL等待可防止將它解釋成使用相同插口對的新連接的一部分。但這只有在2MSL等待連接中的主機處於正常工作狀態時纔有效。

如果使用2MSL等待的主機出現故障並在MSL內重啓,並立即使用處於2MSL的插口對來建立一個新的連接,從而故障前發出而遲到的報文段會被錯誤地當作屬於重啓後新連接的報文段。

爲了防止這種情況,RFC793提出TCP在重啓後的MSL秒內不能建立任何連接,這就是平靜等待時間(quiet time)。

只有極少的實現版準遵守這一原則,因爲大多數主機重啓時間的時間都比MSL秒要長。

5.3 FIN_WAIT_2狀態

主動關閉方已經發出了FIN,並且被動關閉方也已對它進行ACK確認,那麼主動方進入FIN_WAIT_2狀態。只有當被動方的進程完成這個關閉(發出FIN包),主動方發出ack,主動方纔從FIN_WAIT_2進入TIME_WATI狀態。

如果被動方一直不發出FIN包,這意味着主動和被動方分別永遠保持FIN_WAIT_2和CLOSE_WAIT狀態,並一直保持這個狀態直到應用層決定進行關閉。(許多伯克利實現採用以下方法來防止這種FIN_WAIT_2狀態的無限等待,如果執行主動關閉的應用層將進行全關閉,而不是半關閉,表示它不想接收數據,就設置一個定時器,如果這個連接空閒超過10分鐘75秒,那麼TCP將進入CLOSED狀態)


6.復位報文段

連接不存在的端口:

一般來說,無論何時一個報文段發往基準的連接出現錯誤,TCP都會發出一個復位報文段。產生復位的一種常見情況是當連接請求到達時,目的端口沒有進程正在監聽,例如當一個數據報達到目的端口時,該端口沒在使用,它將產生一個端口不可達的信息,而TCP則使用復位。

異常終止一個連接:

終止連接有2個方式,其中一個是正常方式:一方發送FIN,也稱爲有序釋放,正常情況下沒有任何數據丟失。但也有可能發送一個復位報文段而不是FIN來中途釋放一個連接,稱爲異常釋放。

異常終止一個連接對應用程序來說有2個優點:

1)丟棄任何待發數據並立即發送復位報文段

2)RST的接收方會區分另一端執行的是異常關閉還是正常關閉。

需要注意的是RST報文段不會導致另一端產生任何響應,另一端根本不進行確認。收到RST的一方將終止連接,並通知應用層連接復位。

檢測半打開連接:

如果一方已經關閉或異常終止連接而另一方卻還不知道,我們將這樣的TCP連接稱爲半打開的(Half-Open)。任何一端的主機異常都可能導致發生這種情況。只要不打算在半打開連接上傳輸數據,仍處於連接狀態的一方就不會檢測另一方已經出現異常。

半打開連接的另一個常見原因是當客戶主機突然掉電而不是正常的結束客戶應用程序後再關機。此時如果服務器又重新啓動,它將丟失復位前連接的所有信息,因此它不知道數據報文段中提到的連接。此時TCP處理的原則是接收方以復位作爲應答。


7.同時打開

兩個應用程序同時彼此執行主動打開的情況是可能的,TCP特意設計可以處理同時打開,對於同時打開它僅建立一條連接而不是兩條連接。

同時打開時,兩端幾乎同時發送SYN,並進入SYN_SENT狀態。當每一端收到SYN時,狀態變爲SYN_RCVD,同時它們都再發SYN並對收到的SYN進行確認。當雙方都收到SYN相應的ACK時,狀態都變遷爲ESTABLISH。

一個同時打開的連接需要交換4個報文段,比正常的三次握手多1個,如下圖所示:



7.同時關閉

雙方同時執行主動關閉是可能的,TCP協議也允許這樣的同時關閉。

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

同時關閉與正常關閉使用的段交換數據相同。



8.TCP服務器的設計


8.1 TCP服務器端口號:

TCP服務器對於每一個連接都需要建立一個獨立的進程(或者是輕量級的,線程),來保證對話的獨立性。所以TCP服務器是併發的。

當一個服務器進程接受一個來自客戶端的請求時是如何處理端口的?如果多個連接請求幾乎同時到達會發生什麼情況?

1)首先使用neststat -a -n可以查看網絡中的所有主機端,假定端口號23爲監聽端口,在沒有任何請求連接時輸入命令可以看到23端口處於LISTEN監聽狀態。(Foreign Address爲*.*表示還不知道遠端IP地址和端口號,因爲該端還處於LISTEN狀態,正等待連接請求的到達)

2)從客戶端發起一個服務端23端口的連接,使用命令可以看到處於LISTEN狀態的服務器進程仍然存在(23端口),用於接收其它的連接請求。當傳入的連接請求到達並被接收時,系統內核中的TCP模塊就創建一個處於ESTABLISHED狀態的端點(23端口)。另外,處於ESTABLISHED狀態的連接端口不會變化,也是23,與處於LISTEN狀態的進程相同。

3)在使用端口23的進程中,只有處於LISTEN的進程能夠接收新的連接請求。處於ESTABLISHED的進程將不能接收SYN報文段,而處於LISTEN的進程將不能接收數據報文段。


8.2 TCP處理呼入連接請求規則:

(1)正等待連接的一端有一個固定長度的連接隊列,該隊列中的連接已經完成3次握手,但還沒有被應用層接收。
(2)應用層指定這個連接隊列的最大長度,這個值通常叫做積壓值(backlog),取值範圍爲0至5的整數。
(3)當一個請求連接到達(SYN),TCP根據連接隊列中的連接數確認是否接收這個連接,但這時的最大排隊連接數並不等於積壓值。
(4)如果連接隊列中的連接數少於最大排隊的連接數,TCP將確認建立連接。在客戶端主動連接成功而服務端應用層還沒接收這個連接時,客戶端發送的數據將保存在服務端的TCP緩存隊列。
(5)如果連接隊列沒有空間,TCP將丟棄收到的SYN請求,不發回任何報文(包括RST),客戶端將超時重傳SYN請求,等待連接隊列有空間。

TCP服務器無法使客戶端的主動打開失效。因爲服務器接收到請求時,TCP的三次握手已經完成。所以對於限定遠程IP地址的服務器,必須在客戶端三次握手建立連接後才能判斷是否合法。

發佈了10 篇原創文章 · 獲贊 12 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章