1. 程式人生 > >計算機網路之TCP連線的建立和終止

計算機網路之TCP連線的建立和終止

TCP提供一種面向連線的、可靠的基於流的服務。

面向連線:在彼此交換資料之前必須先建立一個TCP連線,雙方互相確認,僅有兩方彼此通訊。

可靠:資料被分割成TCP認為最適合傳送的資料塊;TCP發出一個段後啟動一個定時器,超時重傳;TCP收到另一端的資料後,將回復一個確認;TCP將保持它首部和資料的校驗和,以檢測資料在傳輸過程中的任何變化;必要時TCP將對收到的資料進行重新排序;TCP還能提供流量控制。

基於流的服務:應用程式對資料的傳送和接受是沒有邊界限制的,傳送段指定的寫操作和接受段執行的讀操作的次數之間沒有任何的數量關係。

提供全雙工通訊。資料在兩個方向上獨立的進行傳輸,連線的每一端必須保持每個方向上的輸出資料序號。

TCP首部

這裡寫圖片描述

通常是20個位元組。

傳送端和目的端的埠號:用於標識發端和收端的應用程序。一個IP地址和一個埠號稱為一個socket,插口對(包含源端IP、源埠、目的IP、目的埠)可唯一確定一個TCP連線。

32位序號:用來標識從TCP發端向TCP收端傳送的資料位元組流,表示在這個報文段中第一個資料位元組,TCP用序號對每個位元組進行計數。用來解決網路包亂序問題。

32位確認序號:希望對端傳送的資料位元組,只有ACK標誌位為1時,確認序號欄位才有效。確認方ack=傳送方seq+1。用來解決不丟包問題。

4位首部長度:TCP首部除固定長度外,還有可選項,因此TCP首部長度是可變的。UDP就沒有,UDP的長度是固定的。

標誌位元位:

URG:緊急指標有效
ACK:確認序號有效
PSH:接收方應該儘快將這個報文段交給應用層
RST:重置連線
SYN:同步序號用來發起一個連線
FIN:發端完成傳送任務,釋放連線

視窗大小:這個值是接受端正期望接收的位元組,16位所以視窗大小最大為65535位元組。著名的滑動視窗,解決流量控制。

校驗和:這是一個強制性欄位,一定是由發端計算和儲存,並由收端進行驗證。

緊急指標:只有當URG為1時才有效,是傳送端向另一端傳送緊急資料的一種方式。

問題一:為什麼TCP首部中要把埠號放入最開始的4個位元組?

答:在ICMP的差錯報文中,要包含IP首部後面的8個位元組的內容,而這裡面有TCP首部中的源埠號和目的埠號。當TCP收到ICMP差錯報文時需要用這兩個埠來確定是哪條連接出了差錯。

問題二:TCP提供了一種位元組流服務,而收發雙方都不保持記錄的邊界,應用程式應該如何提供他們自己的記錄標識呢?

答:很多應用使用回車和換行來標記每個應用記錄的結束,另外可以在記錄前加上記錄的位元組計數等等手段。

TCP連線的建立

正常情況下

建立連線協議

(1)客戶端將SYN標誌置為1,傳送一個SYN段指明客戶打算連線的伺服器的埠,以及客戶的初始序號ISN。客戶端進入SYN_SENT狀態

(2)服務端發回伺服器的初始序號的SYN報文段作為應答。同時將確認序號設定為客戶的ISN+1,對客戶的SYN報文段進行確認。一個SYN佔用一個序號,服務端進入SYN_RCVD狀態

(3)客戶將確認序號設定為伺服器的ISN+1以對伺服器的SYN報文段進行確認。

初始序號是如何確定的?

當一端傳送一個SYN報文段時,為連線選擇一個初始序號。

那麼初始序號是怎麼來的呢?ISN隨時間而變化,因此每個連線都將有不同的ISN。ISN可以看作是一個32位的計數器,每4ms加1。

TCP連線的終止

終止一個連線需要四次握手,這是有TCP的半關閉造成的。TCP連線是全雙工的,因此每個方向都必須單獨地進行關閉。
(1)首先進行關閉的一方傳送一個FIN執行主動關閉,進入FIN_WAIT_1狀態,另一端執行被動關閉。

(2)收到FIN後,伺服器發回一個ACK,確認序號為收到的序號+1(一個FIN佔用一個序號),伺服器端進入CLOSE_WAIT狀態。

(3)TCP伺服器嚮應用程式傳送一個檔案結束符,接著這個伺服器程式就關閉它的連線,導致它的TCP端傳送一個FIN,伺服器端進入LAST_ACK狀態。

(4)收到FIN後,客戶端進入TIME_WAIT狀態,接著客戶必須發回一個確認,並將確認序號設定為收到的序號加1,伺服器端進入CLOSE狀態。

問題一:為什麼建立連線是三次握手,而關閉連線是四次揮手呢?

因為服務端在LISTEN狀態下,收到建立連線請求的SYN報文後,把ACK和SYN放到一個報文裡傳送給客戶端了。

而關閉連線時,當收到對方的FIN報文時,僅僅表示對方不再發送資料了但是還能接受資料,己方也未必全部資料都發送給對方了,所以己方可以立即close,也可以傳送一些資料給對方後,在發FIN來關閉連線。因此己方ACK和FIN一般都會分開發送。

問題二:為什麼伺服器不將對客戶FIN的ACK與自己的FIN合併,從而將報文段數減少為3個?

首先,伺服器對客戶的FIN確認一般不會被延遲,而是在FIN到達後立即傳送。應用程序需要一些時間來接受EOF,告訴它的TCP關閉它這一端的連線。

第二,伺服器收到客戶的FIN後,並不一定要關閉它這一端的連線。

最大報文段長度(MSS)

表示TCP傳往另一端的最大塊資料的長度,當一個連線建立時,連線的雙方都要通告各自的MSS。MSS選項只能出現在SYN報文段中。

如果一方不接收來自另一方的MSS值,則MSS就定為預設值536位元組(這個預設值允許20位元組的IP首部和20位元組的TCP首部以適合576位元組IP資料報)。

當TCP傳送一個SYN時,或者是因為一個本地應用程序想發起一個連線,或者是因為另一端的主機收到了一個連線請求,它能將MSS值設定為外出介面上的MTU長度減去固定的IP首部和TCP首部長度。對於一個乙太網,MSS值可達1460位元組。如果目的IP地址為“非本地的(nonlocal)”,MSS通常的預設值為536。

MSS讓主機限制另一端傳送資料包的長度,加上主機也能控制它傳送資料報的長度,這將使以較小的MTU連線到一個網路上的主機避免分段。

TCP的半關閉

何為半關閉? TCP提供了連線的一端在結束它的傳送後還能接收來自另一端資料的能力,這就是所謂的半關閉。

只有少數的應用程式使用它,如果應用程式不呼叫close而呼叫shutdown,且第二個引數值為1,則插口的API支援半關閉。

為什麼要有半關閉?

有些情況下,需要一些技術讓客戶通知伺服器,客戶端已經完成了它的資料傳送,但仍要接受來自伺服器的資料。

TCP的狀態變遷圖

這裡寫圖片描述

  1. 粗的實線箭頭表示正常的客戶端狀態變遷;細的虛線箭頭表示正常的伺服器狀態變遷。
    1. 兩個導致進入ESTABLISH-ED狀態的變遷對應開啟一個連線,而兩個導致從ESTABLISHED狀態離開的變遷主動開啟對應關閉一個連線。
  2. 左下角4個狀態放在一個虛線框內,並標為“主動關閉”。其他兩個狀態(CLOSE_WAIT和LAST_ACK)也用虛線框住,並標為“被動關閉”。

TIME_WAIT狀態

也稱為2MSL等待狀態。每個具體TCP實現必須選擇一個報文段最大生存時間MSL(Maximum Segment Lifetime),它是任何報文段被丟棄前在網路內的最長時間。

對一個具體實現所給定的MSL值,處理的原則是:當TCP執行一個主動關閉,併發回最後一個ACK,該連線必須在TIME_WAIT狀態停留的時間為2倍的MSL。目的:可讓TCP再次傳送最後的ACK以防這個ACK丟失(另一端超時重發最後的FIN,一來一回正好2個MSL);有足夠時間讓這個連線不會跟後面的連線混在一起。這個TCP連線在2MSL等待期間,定義這個連線的插口不能再被利用,這個連線只能在2MSL結束後才能被再次使用。

一個插口對(本地IP、本地埠、遠端IP、遠端埠)在它處於2MSL等待時,將不能再被使用。

在連線處於2MSL等待時,任何遲到的報文段將被丟棄,因為處於2MSL等待的、由該插口對定義的連線在這段時間內不能被再用。客戶執行主動關閉並進入TIME_WAIT是正常的,伺服器通常執行被動關閉,不會進入TIME_WAIT狀態。因為伺服器使用熟知埠,所以如果我們終止一個已經建立連線的伺服器程式,並試圖重啟這個伺服器程式,伺服器程式將不能把它的這個熟知埠賦值給它的端點。在重啟伺服器程式前,需要在1~4分鐘。

需要注意:一個連線只有經過了已建立狀態才能進入TIME_WAIT狀態。

問題一:處於TIME_WAIT狀態的主機收到使其進入此狀態的重複的FIN時所發生的情況?

重複的FIN會得到確認,2MSL定時器重新開始

問題二:處於TIME_WAIT狀態的主機收到一個RST時所發生的情況?

這會引起過早地終止,這叫做TIME_WAIT斷開。會存在潛在的問題,RFC提出簡單的修改是TIME_WAIT狀態時忽略RST段。

FIN_WAIT_2狀態

我們發出了FIN,並且另一端也對它進行了確認,這時就處於FIN_WAIT_2狀態。如果這一端不實行半關閉,那麼只有當另一端的程序完成這個關閉,這端才會從FIN_WAIT_2狀態進入TIME_WAIT狀態。

這意味著,我們這端可能永遠保持FIN_WAIT_2狀態,另一端也將處於CLOSE_WAIT狀態,並一直保持直到應用層決定進行關閉。為了防止這種在這個狀態無限的等待,設定了一個定時器,如果這個連線空閒10分鐘75秒,TCP將進入CLOSED狀態。

復位報文段

RST位元是用於“復位”的。一般情況下,無論何時一個報文段發往基準的連線(由一個插口對指明的連線)出現錯誤,TCP都會發出一個復位報文段。下面列幾個常見情況:

  1. 到不存在的埠的連線請求
    當連線請求到達時,目的埠沒有程序在監聽。UDP產生一個ICMP埠不可達的資訊,而TCP則使用復位。

  2. 異常終止一個連線
    終止一個連線的正常方式是一方傳送FIN,也有可能傳送一個復位報文段而不是FIN來中途釋放一個連線,這稱為異常連線。
    異常終止一個連線對應用程式來說有兩個優點:(1)丟棄任何待發資料並立即傳送復位報文段;(2)RST的接收方會區分另一端執行的是異常關閉還是正常關閉,應用程式使用的API必須提供產生異常關閉而不是正常關閉的手段。

    RST報文段中包含一個序號和確認序號,另一端根本不會進行確認,將終止該連線,並通知應用層進行連線復位。如下圖:收到RST時它產生一個差錯:連線被對方復位。

    這裡寫圖片描述

  3. 檢測半開啟連線
    如果一方已經關閉或異常終止連線而另一方卻還不知道,我們將這樣的TCP連線稱為半開啟的。
    任何一端的主機異常都可能導致這種情況,只要不打算在半開啟連線上傳輸資料,仍處於連線狀態的一方就不會檢測到另一方已經出現異常。

    舉個例子,當伺服器主機出現異常,伺服器主機重啟後,將丟失復位前連線的所有資訊,因此它不知道資料報文段中提到的連線,它會發送復位作為應答。

同時開啟

兩個應用程式同時彼此執行主動開啟的情況也是有可能的。每一方必須傳送一個SYN,且這些SYN必須傳遞給對方,這需要每一方使用一個對方熟知的埠作為本地埠。例如主機A中的一個應用程式使用本地埠7777,並與主機B的埠8888執行主動開啟。主機B中的應用程式則使用本地埠8888,並與主機A的埠7777執行主動開啟。
TCP特意設計可以處理同時開啟,對於同時開啟它僅建立一條連線而不是兩條連線。

這裡寫圖片描述

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

可以看到一個同時開啟的連線需要交換4個報文段,比正常的三次握手多一個。

同時關閉

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

雙方各發送一個FIN,兩個FIN經過網路傳送後分別到達另一端,收到FIN後,狀態由FIN_WAIT_1變遷到CLOSING,併發送最後的ACK,當收到ACK時,狀態變化為TIME_WAIT。

這裡寫圖片描述

同時關閉與正常關閉使用的段交換數目相同,都是四次。

下一篇:計算機網路之TCP的超時和重傳