TCP協議詳解(三) TCP狀態轉移

2021-08-19 15:53:26 字數 3383 閱讀 1804

tcp連線的任意一端在任一時刻都處於某種狀態,當前狀態可以通過netstat命令檢視。下圖為完整的tcp狀態轉移圖,它描繪了所有的tcp狀態以及可能的狀態轉換。

圖中的粗虛線表示典型的伺服器端連線的狀態轉移;粗實線表示典型的客戶端連線的狀態轉移。closed是乙個假想的起始點,並不是乙個實際狀態。

這裡首先討論伺服器的典型狀態轉移過程,此時說的連線狀態都是指的該連線的伺服器端的狀態。

伺服器通過listen系統呼叫進入listen狀態,被動等待客戶端連線,因此執行的是所謂的被動開啟。伺服器一旦監聽到某個連線請求(收到同步報文段),就將該連線放入核心等待佇列,並向客戶端傳送帶syn標誌的確認報文段。此時該連線處於syn_rcvd狀態。如果伺服器成功地接收到客戶端傳送回地確認報文段,則該連線轉移到established狀態。established狀態是連線雙方能夠進行雙向資料傳輸的狀態。

當客戶端主動關閉連線時(通過close或shutdown系統呼叫向伺服器傳送結束報文段),伺服器通過返回確認報文段使連線進入close_wait狀態。這個狀態的含義很明確:等待伺服器應用程式關閉連線。通常伺服器檢測到客戶端關閉連線後,也會立即向客戶端傳送乙個結束報文段來關閉連線,此時連線進入last_ack狀態,以等待客戶端的最後一次確認,一旦確認完成,連線就徹底關閉了。

下面討論客戶端的典型狀態轉移過程,此時說的連線都是指該連線的客戶端的狀態。

客戶端通過connect系統呼叫主動與伺服器建立連線。connect系統呼叫首先給伺服器傳送乙個同步報文段,使連線轉移到syn_sent狀態。此後,connect系統呼叫可能因為如下兩個原因失敗返回:

connect呼叫失敗將使連線立即返回到初始的closed狀態。如果客戶端成功收到伺服器的同步報文段和確認,則connect呼叫成功返回,連線轉移至established狀態。

當客戶端執行主動關閉時,它將向伺服器傳送乙個結束報文段,同時連線進入fin_wait_1狀態。若此時客戶端收到伺服器專門用於確認目的的確認報文段,則連線轉移至fin_wait_2狀態。當客戶端處於fin_wait_2狀態時,伺服器處於close_wait狀態,這一對狀態是可能發生半關閉的狀態。此時如果伺服器也關閉連線(傳送結束報文段),則客戶端將給予確認並進入time_wait狀態。

圖3-8還給出了客戶端從fin_wait_1狀態直接進入time_wait狀態的一條線路(不經過fin_wait_2狀態),前提是處於fin_wait_1狀態的伺服器直接受到帶確認資訊的結束報文段(而不是先收到確認報文段,再收到結束報文段)。

前面說過,處於fin_wait_2狀態的客戶端需要等待伺服器傳送結束報文段,才能轉移至time_wait狀態,否則它將一直停留再這個狀態。如果不是為了在半關閉狀態下繼續接收資料,連線長時間地停留在fin_wait_2狀態並無益處。連線停留在fin_wait_2狀態的情況可能發生在:客戶端執行半關閉後,未等伺服器關閉連線就強行退出了。此時客戶端連線由核心來接管,可稱之為故而連線(和孤兒程序類似)。linux為了防止孤兒連線長時間存留在核心中,定義了兩個核心變數:/proc//proc/sys/net/ipv4/tcp_max_orphans和/proc/sys/net/ipv4/tcp_fin_timeout。前者指定核心能接管的孤兒連線數目,後者指定孤兒連線在核心生存的時間。

下面為客戶端和伺服器的狀態轉移如圖:

上圖中,客戶端連線在接收到伺服器的結束報文段(tcp報文段6)後,並未直接進入cloesed狀態,而是轉移到time_wait

狀態。在這個狀態,客戶端連線要等待一段長為2msl(maximum segment life,報文最大生存時間)的時間,才能完全關閉。msl是tcp報文段在網路中的最大生存時間,標準文件rfc1122的建議值是2min。

time_wait狀態存在的原因有兩點:

第乙個原因很好理解。假設上圖中用於確認報文段6的tcp報文段7丟失,那麼伺服器將重發結束報文段。因此客戶端需要停留在某個狀態以處理重複收到的結束報文段(即向伺服器傳送確認報文段)。否則,客戶端將以復位報文段來回應伺服器,伺服器則認為這是乙個錯誤,因為它期望的是乙個像tcp報文段7那樣的確認報文段。

在linux系統上,乙個tcp埠不能被同時開啟多次(兩次及以上)。當乙個tcp連線處於time_wait狀態時,我們將無法立即使用該連線占用著的埠來建立乙個新連線。反過來思考,如果不存在time_wait狀態,則應用程式能夠立即建立乙個和剛關閉的連線相似的連線(具有相同ip位址和埠號)。這個新的、和原來相似的連線被稱為連線的化身(incarnation)。新的化身可能接收到屬於原來的連線的、攜帶應用程式資料的tcp報文段(就是遲到的報文段),這顯然是不應該發生的。這就是time_wait狀態存在的第二個原因。

另外,由於網路上tcp報文段的最大生存時間是msl,所以堅持2msl時間的time_wait狀態能夠確保網路上兩個傳輸方向上尚未被收到的、遲到的tcp報文段都已經消失(被中專路由器丟棄)。因此,乙個連線的新的化身可以在2msl時間之後安全的建立,而絕對不會接收到屬於原來連線的應用程式資料,這就是time_wait狀態要持續2msl時間的原因。

考慮乙個例子:在測試機子上ernest-laptop上以客戶端方式執行nc(用於建立網路連線的工具) 命令,登陸本機的web服務,且明確指定客戶端使用12345埠與伺服器通訊。然後ctrl+c終止客戶端程式,接著又立即重啟nc程式,以完全相同的方式再次連線本機的web服務,如下:

$ nc -p 12345 192.168.1.108 80  

ctrl+c #中斷客戶端程式

$ nc -p 12345 192.168.1.108 80 #重啟客戶端

nc: bind failed: address already in use #輸出顯示連線失敗,因為12345埠仍被占用

$ netstat -nat #用netstat命令檢視連線狀態

tcp 0 0 192.168.1.108:12345 192.168.1.108:80 time_wait

這裡使用netstat命令檢視連線的狀態。其輸出顯示,客戶端程式被中斷後,連線進入time_wait狀態,12345埠仍被占用,所以客戶端重啟失效。

對客戶端程式來說,我們通常不用擔心上面描述的重啟問題。因為客戶端一般使用系統自動分配的臨時埠號來建立連線,而由於隨機性,臨時埠號一般和程式上一次使用的埠號(還處於time_wait狀態的哪個連線使用的埠號)不同,所以客戶端程式一般可以立即重啟。

但如果是伺服器主動關閉連線後異常終止,則因為它總是使用同乙個知名服務埠號,所以連線的time_wait狀態將導致它不能立即重啟。但是可以通過socket選項so_reuseaddr來強制程序立即使用處於time_wait狀態的連線占用的埠。

復位報文段

TCP狀態轉移詳解

在看tcp狀態轉移圖之前,我們先來看一下三次握手和四次揮手 畫了乙個比較簡陋的圖 1 建立連線協議 三次握手 1 客戶端傳送乙個帶syn標誌的tcp報文到伺服器。這是三次握手過程中的報文1。2 伺服器端回應客戶端的,這是三次握手中的第2個報文,這個報文同時帶ack標誌和syn標誌。因此它表示對剛才客...

TCP狀態轉移

tcp狀態轉移,一共存在11個狀態,請看下圖 1.closed 起始點,在超時或者連線關閉時候進入此狀態。2.listen svr端在等待連線過來時候的狀態,svr端為此要呼叫socket,bind,listen函式,就能進入此狀態。此稱為應用程式被動開啟 等待客戶端來連線 3.syn sent 客...

TCP狀態轉移

被動開啟 active open 客戶端通過connect發起主動開啟。客戶端通過connect連線伺服器,客戶端tcp將傳送乙個syn包,告訴伺服器客戶端將在待建立連線傳送資料的初始序列號。客戶端 closed syn sent 伺服器端必須ack客戶端syn,同時傳送乙個syn,告訴客戶端,伺服...