TCP的運輸連線管理
TCP是面向連線的通訊,運輸連線是用來傳送TCP報文的,TCP運輸連線的建立和釋放是每一次面向連線的通訊中必不可少的過程。因此,運輸連線有三個階段,即:建立連線,資料傳送,連線釋放,運輸連線的管理就是使運輸連線和釋放都能正常的進行。在TCP連線中主要解決以下問題:
-
要使每一方都能確認對方的存在。
-
要允許雙方協商一些引數(如最大視窗值,是否使用視窗擴大選項和時間戳選項以及服務質量等)。
-
能夠對運輸實體資源進行合理的分配(如快取大小,連線表中的專案等)。
TCP連線的建立採用客戶伺服器方式。主動發起連線建立的應用程式叫做客戶(client),而被動等待連線的應用程式叫做伺服器(server)。
TCP的連線建立
TCP建立連線的過程叫握手,握手需要客戶和伺服器之間交換三個TCP報文段。如下圖5-28。
假定主機A執行的是TCP客戶程式,而B執行的是TCP服務程式,最初兩端的連線處於CLOSE(關閉) 狀態,在本例中A主動開啟連線,B被動開啟連線。
TCP握手過程描述:
- 一開始,B的TCP伺服器程式先建立傳輸控制塊TCB(Transmission Control Block),準備接受客戶程式的連線請求,然後伺服器就處於LISTEN(監聽)狀態,等待客戶的連線請求,如果有,就做出響應。
- A的TCP客戶程式也是首先創立傳輸控制模組TCB,然後在建立連線的時候,向伺服器B傳送連線請求報文段,這時候首部中的同步位SYN=1,同時選擇一個初始序號seq=x。TCP規定:SYN報文段(SYN=1的報文段)不能攜帶資料,但是要消耗掉一個序列號,這時候,TCP客戶端程式進入SYN-SENT同步已傳送狀態。
- B收到連線請求的報文段,如果同意建立連線,則向A傳送確認,在確認報文段SYN位=1,ACK位為1,確認號是ack=x+1,同時也為自己選擇一個初始序號seq=y,注意,這個報文段也不能攜帶資料,但是同樣要消耗一個序列號。這時候TCP伺服器程式處於SYN-RCVD(同步收到)狀態。
- TCP客戶程式收到B的確認後,還要給B給出確認,確認報文段的ACK=1確認號ack=y+1,而自己的序號seq=x+1。TCP的標準規定,ACK報文段可以攜帶資料,但如果不攜帶資料則不消耗序號,這種情況下,下一個資料包文段的序號仍然是seq=x+1,這時候,TCP連線已經建立連線,客戶端A進入ESTABLISHED(已建立連線狀態),
- B收到A的確認報文後,也進入ESTABLISHED狀態。
上面給出的連線過程叫做三次報文握手,注意,在圖中,當B傳送給A的報文段,也可以拆分成兩個報文段,可以先傳送確認報文段(ACK=1,ack=x+1),然後再傳送一個同步報文段(SYN=1,seq=y),這樣的過程就變成了四報文握手,但效果是一樣的。
為什麼A最後還要傳送一次確認呢?這主要是為了防止已失效的連線請求報文突然又傳送到B,因而產生錯誤。
所謂“已失效的連線請求報文段”是這樣產生的。考慮一種正常情況,A 發出連線請求,但因連線請求報文丟失而未收到確認。於是 A 再重傳一次連線請求。後來收到了確認,建立了連線。資料傳輸完畢後,就釋放了連線。A 共傳送了兩個連線請求報文段,其中第一個丟失,第二個到達了 B,沒有“已失效的連線請求報文段”。
現假定出現一種異常情況,即 A 發出的第一個連線請求報文段並沒有丟失,而是在某些網路結點長時間滯留了,以致延誤到連線釋放以後的某個時間才到達 B。本來這是一個早已失效的報文段。但 B 收到此失效的連線請求報文段後,就誤認為是A又發出一次新的連線請求。於是就向 A 發出確認報文段,同意建立連線。假定不採用報文握手,那麼只要 B發出確認,新的連線就建立了。
由於現在 A 並沒有發出建立連線的請求,因此不會理睬 B 的確認,也不會向 B 傳送資料。但 B 卻以為新的運輸連線已經建立了,並一直等待 A 發來資料。B 的許多資源就這樣白白浪費了。
採用三報文握手的辦法,可以防止上述現象的發生。例如在剛才的異常情況下,A 不會
向B的確認發出確認。B 由於收不到確認,就知道A並沒有要求建立連線。
TCP連線釋放
TCP連線釋放比較複雜,我們仍然結合雙方狀態的改變來闡明連線釋放的過程。
資料傳輸結束後,通訊的雙方都可釋放連線。現在 A 和 B 都處於 ESTABLISHED 狀態(圖5-29)。A 的應用程式先向其TCP發出連線釋放報文段,並停止再傳送資料,主動關閉TCP 連線。A 把連線釋放報文段首部的終止控制位 FIN 置 1,其序號 seq = u,它等於前面已傳送過的資料的最後一個位元組的序號加 1。這時 A 進入 FIN-WAIT-1(終止等待 1)狀態,等待 B 的確認。請注意,TCP 規定,FIN 報文段即使不攜帶資料,它也消耗掉一個序號。
-
B伺服器收到連線釋放的報文段後立即發出確認,確認號為ack=u+1,而這個報文段自己的序號為v,等於B前面已傳過的資料的最後一個位元組+1,然後伺服器就進入CLOSE-WAIT(關閉等待)狀態,TCP伺服器程式這時候通知高層應用程式,因而從客戶端A到伺服器端B這個連線就釋放了,這時候的TCP連線窗臺處於半關閉狀態(HALF-CLOSE),A已經沒有資料傳送給B了,但是B要使發資料,A仍然要接受,也就說,從B到A的方向的連線仍未關閉,這個狀態可能還要持續一段時間。 A收到來自B的確認後,就進入FIN-WAIT2(終止等待2)狀態,等待B發出的連線釋放報文段。若B已經沒有必要向A傳送資料了,其應用程式就通知TCP釋放連線。這時候B發出的連線釋放報文段必須使FIN=1。現假設B的序號為w,B還重複上次已經傳送過的確認號ack=u+1,這時候B就進入LAST-ACK(最後確認狀態),等待A的確認。A在收到B的連線釋放的時候,必須對此發出確認,在確認報文段中把ACK=1,確認號ack=w+1,自己的序號為seq=u+1,然後進入到TIME-WAIT(時間等待狀態),注意,現在的TCP並沒有斷開連線,而是必須經過時間等待計時器設定的2MSL(max segment lifetime),A才能進入CLOSED狀態,時間MSL叫做最長報文段壽命,RFC標準建議為2min。為什麼要設立時間等待狀態必須為2MSL呢?
- 為了保證客戶端傳送額最後一個ACK報文段能夠到達B,這個ACK報文段有可能會丟失,因而使用LAST-ACK狀態的B收不到對已傳送FIN+ACK報文的確認,B會超時重傳這個FIN+ACK報文段,而A就能在2MSL時間內收到這個重傳的FIN+ACK報文段,接著A重傳一次確認,重新啟動AMSL計時器。最後知道A和B都進入CLOSED狀態。
- 防止上面提到的已失效的連線請求報文段出現在本連線中,A在發完最後一個ACK報文段的時候,再經過2MSL時間,就可以是本連線持續的時間內產生的所有報文段都從網路中協消失。這樣就可以使下一個新的連線中不會出現這種舊的連線請求文。
除了時間等待計時器之外,還應該設有保活計時器(keep-live timer),有這樣的一個場景:客戶端已經主動與伺服器建立了TCP連線,但是後來客戶端的主機突然出現了故障,顯然,伺服器以後就不能在也接收到客戶端的資料了,因此,伺服器就沒有必要浪費資源繼續與出現故障的客戶端繼續保持相連。這就需要使用保活計時器,伺服器每收到一次客戶端請求的資料,就重新重置保活計數器,時間的設定通常是兩小時,若兩小時內沒有收到客戶端的資料,伺服器就傳送一個探測報文段,以後每隔75s傳送一個,若一連傳送10個探測報文段後仍然沒有相應,伺服器就認為該客戶端出現了故障,就關閉這個連線。
TCP的有限狀態機)
為了更清晰地看出 TCP 連線的各種狀態之間的關係,圖 5-30 給出了 TCP 的有限狀態機。圖中每一個方框即 TCP 可能具有的狀態。每個方框中的大寫英文字串是 TCP 標準所使用的 TCP 連線狀態名。狀態之間的箭頭表示可能發生的狀態變遷。箭頭旁邊的字,表明引起這種變遷的原因,或表明發生狀態變遷後又出現什麼動作。請注意圖中有三種不同的箭頭。粗實線箭頭表示對客戶程式的正常變遷。粗虛線箭頭表示對伺服器程式的正常變遷。另一種細線箭頭表示異常變遷。