1. 程式人生 > >詳解TCP連線的“三次握手”與“四次揮手”(上)

詳解TCP連線的“三次握手”與“四次揮手”(上)

一、TCP connection




客戶端與伺服器之間資料的傳送和返回的過程當中需要建立一個叫TCP connection的東西;
由於TCP不存在連線的概念,只存在請求和響應,請求和響應都是資料包,它們之間都是經過由TCP建立的一個從客戶端發起,伺服器接收的類似連線的通道,這個連線可以一直保持,http請求是在這個連線的基礎上傳送的;

在一個TCP連線上是可以傳送多個http請求的,不同的版本這個模式不一樣。

在HTTP/1.0中這個TCP連線是在http請求建立的時候同步建立的,http請求傳送到伺服器端,伺服器端響應了之後,這個TCP連線就關閉了;
HTTP/1.1中可以以某種方式宣告這個連線一直保持,一個請求傳輸完之後,另一個請求可以接著傳輸。這樣的好處是:在建立一個TCP連線的過程中需要“三次握手”的消耗,“三次握手”代表有三次網路傳輸。

如果TCP連線保持,第二個請求傳送就沒有這“三次握手”的消耗。HTTP/2中同一個TCP連線裡還可以併發地傳輸http請求。

二、TCP報文格式簡介



其中比較重要的欄位有:

(1)序號(sequence number):Seq序號,佔32位,用來標識從TCP源端向目的端傳送的位元組流,發起方傳送資料時對此進行標記。

(2)確認號(acknowledgement number):Ack序號,佔32位,只有ACK標誌位為1時,確認序號欄位才有效,Ack=Seq+1。

(3)標誌位(Flags):共6個,即URG、ACK、PSH、RST、SYN、FIN等,具體含義如下:

  • URG:緊急指標(urgent pointer)有效。
  • ACK:確認序號有效。
  • PSH:接收方應該儘快將這個報文交給應用層。
  • RST:重置連線。
  • SYN:發起一個新連線。
  • FIN:釋放一個連線。

需要注意的是:

  • 不要將確認序號Ack與標誌位中的ACK搞混了。
  • 確認方Ack=發起方Req+1,兩端配對。

三、TCP的三次握手(Three-Way Handshake)


1.”三次握手”的詳解


所謂的三次握手即TCP連線的建立。這個連線必須是一方主動開啟,另一方被動開啟的。
以下為客戶端主動發起連線的圖解:

握手之前主動開啟連線的客戶端結束CLOSED階段,被動開啟的伺服器端也結束CLOSED階段,並進入LISTEN階段。

(1)首先客戶端向伺服器端傳送一段TCP報文,其中:

標記位為SYN,表示“請求建立新連線”;
序號為Seq=X(X一般為1);
隨後客戶端進入SYN-SENT階段。

(2)伺服器端接收到來自客戶端的TCP報文之後,結束LISTEN階段。並返回一段TCP報文,其中:
標誌位為SYN和ACK,表示“確認客戶端的報文Seq序號有效,伺服器能正常接收客戶端傳送的資料,並同意建立新連線”(即告訴客戶端,伺服器收到了你的資料);
序號為Seq=y;
確認號為Ack=x+1,表示收到客戶端的序號Seq並將其值加1作為自己確認號Ack的值;隨後伺服器端進入SYN-RCVD階段。

(3)客戶端接收到來自伺服器端的確認收到資料的TCP報文之後,明確了從客戶端到伺服器的資料傳輸是正常的,結束SYN-SENT階段。並返回最後一段TCP報文。其中:
標誌位為ACK,表示“確認收到伺服器端同意連線的訊號”(即告訴伺服器,我知道你收到我發的資料了);
序號為Seq=x+1,表示收到伺服器端的確認號Ack,並將其值作為自己的序號值;
確認號為Ack=y+1,表示收到伺服器端序號Seq,並將其值加1作為自己的確認號Ack的值;
隨後客戶端進入ESTAB-LISHED階段。

伺服器收到來自客戶端的“確認收到伺服器資料”的TCP報文之後,明確了從伺服器到客戶端的資料傳輸是正常的。結束SYN-SENT階段,進入ESTAB-LISHED階段。

客戶端與伺服器端傳輸的TCP報文中雙方的確認號Ack和序號Seq的值,都是在彼此Ack和Seq值的基礎上進行計算的,這樣做保證了資料傳輸的連貫性。一旦出現某一方發出的TCP報文丟失,便無法繼續建立連線。

此後客戶端和伺服器端進行正常的資料傳輸。這就是“三次握手”的過程。

2.“三次握手”的動態過程


3.“三次握手”的通俗理解



舉個栗子:把客戶端比作男孩,伺服器比作女孩。“三次握手”的過程是這樣的:

  • (1)男孩喜歡女孩,於是寫了一封信告訴女孩:我愛你,請和我交往吧!;寫完信之後,男孩焦急地等待,因為不知道信能否順利傳達給女孩。

  • (2)女孩收到男孩的情書後,心花怒放,原來我們是兩情相悅呀!於是給男孩寫了一封回信:我收到你的情書了,也明白了你的心意,其實,我也喜歡你!我願意和你交往!;
    寫完信之後,女孩也焦急地等待,因為不知道回信能否能順利傳達給男孩。

  • (3)男孩收到回信之後很開心,因為發出的情書女孩收到了,並且從回信中知道了女孩喜歡自己,並且願意和自己交往。然後男孩又寫了一封信告訴女孩:你的心意和信我都收到了,謝謝你,還有我愛你!

女孩收到男孩的回信之後,也很開心,因為發出的情書男孩收到了。由此男孩女孩雙方都知道了彼此的心意,之後就快樂地交流起來了~~

這就是通俗版的“三次握手”,期間一共往來了三封信也就是“三次握手”,以此確認兩個方向上的資料傳輸通道是否正常。

4.為什麼要進行第三次握手?


為了防止伺服器端開啟一些無用的連線增加伺服器開銷以及防止已失效的連線請求報文段突然又傳送到了服務端,因而產生錯誤。

由於網路傳輸是有延時的(要通過網路光纖和各種中間代理伺服器),在傳輸的過程中,比如客戶端發起了SYN=1建立連線的請求(第一次握手)。
如果伺服器端就直接建立了這個連線並返回包含SYN、ACK和Seq等內容的資料包給客戶端,這個資料包因為網路傳輸的原因丟失了,丟失之後客戶端就一直沒有接收到伺服器返回的資料包。
客戶端可能設定了一個超時時間,時間到了就關閉了連線建立的請求。再重新發出建立連線的請求,而伺服器端是不知道的,如果沒有第三次握手告訴伺服器端客戶端收的到伺服器端傳輸的資料的話,
伺服器端是不知道客戶端有沒有接收到伺服器端返回的資訊的。

這樣沒有給伺服器端一個建立還是關閉連線埠的請求,伺服器端的埠就一直開著,等著客戶端傳送實際的請求資料,這個時候伺服器端的開銷就浪費了。

還有一種情況是已經失效的客戶端發出的請求資訊,由於某種原因傳輸到了伺服器端,伺服器端以為是客戶端發出的有效請求,接收後產生錯誤。

所以我們需要“第三次握手”來確認這個過程,讓客戶端和伺服器端能夠及時地察覺到因為網路等一些問題導致的連線建立失敗,這樣伺服器端的埠就可以關閉了不用一直等待。

也可以這樣理解:“第三次握手”是客戶端向伺服器端傳送資料,這個資料就是要告訴伺服器,客戶端有沒有收到伺服器“第二次握手”時傳過去的資料。若傳送的這個資料是“收到了”的資訊,接收後伺服器就正常建立TCP連線,否則建立TCP連線失敗,伺服器關閉連線埠。由此減少伺服器開銷和接收到失效請求發生的錯誤。

5.抓包驗證


下面是用抓包工具抓到的一些資料包,可用來分析TCP的三次握手:

圖中顯示的就是完整的TCP連線的”三次握手”過程。在52528 -> 80中,52528是本地(客戶端)埠,80是伺服器的埠。由於存在”三次握手”,電腦(客戶端)和伺服器端有三個來回,
只要找到相同兩個埠之間的來回就可以確認,是客戶端和伺服器端三次握手的過程。

  • 注意到”第一次握手”客戶端傳送的TCP報文中以[SYN]作為標誌位,並且客戶端序號Seq=0;
  • 接下來”第二次握手”伺服器返回的TCP報文中以[SYN,ACK]作為標誌位;並且伺服器端序號Seq=0;確認號Ack=1(“第一次握手”中客戶端序號Seq的值+1);
  • 最後”第三次握手”客戶端再向伺服器端傳送的TCP報文中以[ACK]作為標誌位;其中客戶端序號Seq=1(“第二次握手”中伺服器端確認號Ack的值);確認號Ack=1(“第二次握手”中伺服器端序號Seq的值+1)。
    這就完成了”三次握手”的過程,符合前面分析的結果。

參考資料:
深入淺出圖解【計算機網路】 之 【TCP可靠傳輸的實現: 三次握手+滑動視窗】
使用 WireShark 分析 TCP/IP 三次握手 和 四次揮手