三次握手+四次揮手,一文搞定所有

TCP 三次握手和四次揮手的問題在面試中是最為常見的考點之一。很多讀者都知道三次和四次,但是如果問深入一點,他們往往都無法作出準確回答。本文就來詳解 TCP 連接的三次握手與四次揮手。

三次握手+四次揮手,一文搞定所有


圖片來自 Pexels

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 報文格式簡介

三次握手+四次揮手,一文搞定所有

其中比較重要的字段有:

  • 序號(sequence number):Seq 序號,佔 32 位,用來標識從 TCP 源端向目的端發送的字節流,發起方發送數據時對此進行標記。
  • 確認號(acknowledgement number):Ack 序號,佔 32 位,只有 ACK 標誌位為 1 時,確認序號字段才有效,Ack=Seq+1。
  • 標誌位(Flags):共 6 個,即 URG、ACK、PSH、RST、SYN、FIN 等。

六個標誌位具體含義如下:

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

需要注意的是:

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

TCP 的三次握手

“三次握手”的詳解

所謂的三次握手即 TCP 連接的建立。這個連接必須是一方主動打開,另一方被動打開的。

以下為客戶端主動發起連接的圖解:

三次握手+四次揮手,一文搞定所有

握手之前主動打開連接的客戶端結束 CLOSED 階段,被動打開的服務器端也結束 CLOSED 階段,並進入 LISTEN 階段,隨後開始“三次握手”。

①首先客戶端向服務器端發送一段 TCP 報文。

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

②服務器端接收到來自客戶端的 TCP 報文之後,結束 LISTEN 階段。並返回一段 TCP 報文。

其中:標誌位為 SYN 和 ACK,表示“確認客戶端的報文 Seq 序號有效,服務器能正常接收客戶端發送的數據,並同意創建新連接”(即告訴客戶端,服務器收到了你的數據)。

序號為 Seq=y;確認號為 Ack=x+1,表示收到客戶端的序號 Seq 並將其值加 1 作為自己確認號 Ack 的值;隨後服務器端進入 SYN-RCVD 階段。

③客戶端接收到來自服務器端的確認收到數據的 TCP 報文之後,明確了從客戶端到服務器的數據傳輸是正常的,結束 SYN-SENT 階段。並返回最後一段 TCP 報文。

其中:標誌位為 ACK,表示“確認收到服務器端同意連接的信號”(即告訴服務器,我知道你收到我發的數據了)。

序號為 Seq=x+1,表示收到服務器端的確認號 Ack,並將其值作為自己的序號值。

確認號為 Ack=y+1,表示收到服務器端序號 Seq,並將其值加 1 作為自己的確認號 Ack 的值;隨後客戶端進入 ESTABLISHED 階段。

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

在客戶端與服務器端傳輸的 TCP 報文中,雙方的確認號 Ack 和序號 Seq 的值,都是在彼此 Ack 和 Seq 值的基礎上進行計算的,這樣做保證了 TCP 報文傳輸的連貫性。

一旦出現某一方發出的 TCP 報文丟失,便無法繼續"握手",以此確保了"三次握手"的順利完成。

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

“三次握手”的動態過程

三次握手+四次揮手,一文搞定所有

“三次握手”的通俗理解

三次握手+四次揮手,一文搞定所有

舉個栗子:把客戶端比作男孩,服務器比作女孩。

用他們的交往來說明“三次握手”過程:

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

寫完信之後,女孩也焦急地等待,因為不知道回信能否能順利傳達給男孩。

  • 男孩收到回信之後很開心,因為發出的情書女孩收到了,並且從回信中知道了女孩喜歡自己,並且願意和自己交往。

然後男孩又寫了一封信告訴女孩:你的心意和信我都收到了,謝謝你,還有我愛你!

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

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

為什麼要進行第三次握手

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

由於網絡傳輸是有延時的(要通過網絡光纖和各種中間代理服務器),在傳輸的過程中,比如客戶端發起了 SYN=1 創建連接的請求(第一次握手)。

如果服務器端就直接創建了這個連接並返回包含 SYN、ACK 和 Seq 等內容的數據包給客戶端,這個數據包因為網絡傳輸的原因丟失了,丟失之後客戶端就一直沒有接收到服務器返回的數據包。

客戶端可能設置了一個超時時間,時間到了就關閉了連接創建的請求。

再重新發出創建連接的請求,而服務器端是不知道的,如果沒有第三次握手告訴服務器端客戶端收的到服務器端傳輸的數據的話,服務器端是不知道客戶端有沒有接收到服務器端返回的信息的。

這個過程可理解為:

三次握手+四次揮手,一文搞定所有

這樣沒有給服務器端一個創建還是關閉連接端口的請求,服務器端的端口就一直開著,等到客戶端因超時重新發出請求時,服務器就會重新開啟一個端口連接。

那麼服務器端上沒有接收到請求數據的上一個端口就一直開著,長此以往,這樣的端口多了,就會造成服務器端開銷的嚴重浪費。

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

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

也可以這樣理解:“第三次握手”是客戶端向服務器端發送數據,這個數據就是要告訴服務器,客戶端有沒有收到服務器“第二次握手”時傳過去的數據。

若發送的這個數據是“收到了”的信息,接收後服務器就正常建立 TCP 連接,否則建立 TCP 連接失敗,服務器關閉連接端口。由此減少服務器開銷和接收到失效請求發生的錯誤。

抓包驗證

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

三次握手+四次揮手,一文搞定所有

圖中顯示的就是完整的 TCP 連接的”三次握手”過程。在 52528→80 中,52528 是本地(客戶端)端口,80 是服務器的端口。80 端口和 52528 端口之間的三次來回就是"三次握手"過程。

注意到“第一次握手”客戶端發送的 TCP 報文中以[SYN]作為標誌位,並且客戶端序號 Seq=0。

接下來”第二次握手”服務器返回的 TCP 報文中以[SYN,ACK]作為標誌位;並且服務器端序號 Seq=0;確認號 Ack=1(“第一次握手”中客戶端序號 Seq 的值+1)。

最後”第三次握手”客戶端再向服務器端發送的 TCP 報文中以[ACK]作為標誌位;其中客戶端序號 Seq=1(“第二次握手”中服務器端確認號 Ack 的值);確認號 Ack=1(“第二次握手”中服務器端序號 Seq 的值 +1)。

這就完成了”三次握手”的過程,符合前面分析的結果。

TCP 的四次揮手

對於"三次握手"我們耳熟能詳,因為其相對的簡單。但是,我們卻不常聽見“四次揮手”,就算聽過也未必能詳細地說明白它的具體過程。下面就為大家詳盡,直觀,完整地介紹“四次揮手”的過程。

“四次揮手”的詳解

所謂的四次揮手即 TCP 連接的釋放(解除)。連接的釋放必須是一方主動釋放,另一方被動釋放。

以下為客戶端主動發起釋放連接的圖解:

三次握手+四次揮手,一文搞定所有

揮手之前主動釋放連接的客戶端結束 ESTABLISHED 階段。隨後開始“四次揮手”。

①首先客戶端想要釋放連接,向服務器端發送一段 TCP 報文。

其中:標記位為 FIN,表示“請求釋放連接“;序號為 Seq=U。

隨後客戶端進入 FIN-WAIT-1 階段,即半關閉階段。並且停止在客戶端到服務器端方向上發送數據,但是客戶端仍然能接收從服務器端傳輸過來的數據。

注意:這裡不發送的是正常連接時傳輸的數據(非確認報文),而不是一切數據,所以客戶端仍然能發送 ACK 確認報文。

②服務器端接收到從客戶端發出的 TCP 報文之後,確認了客戶端想要釋放連接,隨後服務器端結束 ESTABLISHED 階段,進入 CLOSE-WAIT 階段(半關閉狀態)並返回一段 TCP 報文。

其中:標記位為 ACK,表示“接收到客戶端發送的釋放連接的請求”。

序號為 Seq=V,確認號為 Ack=U+1,表示是在收到客戶端報文的基礎上,將其序號 Seq 值加 1 作為本段報文確認號 Ack 的值;隨後服務器端開始準備釋放服務器端到客戶端方向上的連接。

客戶端收到從服務器端發出的 TCP 報文之後,確認了服務器收到了客戶端發出的釋放連接請求,隨後客戶端結束 FIN-WAIT-1 階段,進入 FIN-WAIT-2 階段。

前"兩次揮手"既讓服務器端知道了客戶端想要釋放連接,也讓客戶端知道了服務器端了解了自己想要釋放連接的請求。於是,可以確認關閉客戶端到服務器端方向上的連接了。

③服務器端自從發出 ACK 確認報文之後,經過 CLOSED-WAIT 階段,做好了釋放服務器端到客戶端方向上的連接準備,再次向客戶端發出一段 TCP 報文。

其中:標記位為 FIN,ACK,表示“已經準備好釋放連接了”。注意:這裡的 ACK 並不是確認收到服務器端報文的確認報文。

序號為 Seq=W,確認號為 Ack=U+1,表示是在收到客戶端報文的基礎上,將其序號 Seq 值加 1 作為本段報文確認號 Ack 的值。

隨後服務器端結束 CLOSE-WAIT 階段,進入 LAST-ACK 階段。並且停止在服務器端到客戶端的方向上發送數據,但是服務器端仍然能夠接收從客戶端傳輸過來的數據。

④客戶端收到從服務器端發出的 TCP 報文,確認了服務器端已做好釋放連接的準備,結束 FIN-WAIT-2 階段,進入 TIME-WAIT 階段,並向服務器端發送一段報文。

其中:標記位為 ACK,表示“接收到服務器準備好釋放連接的信號”。

序號為 Seq=u+1;表示是在收到了服務器端報文的基礎上,將其確認號 Ack 值作為本段報文序號的值。

確認號為 Ack=w+1;表示是在收到了服務器端報文的基礎上,將其序號 Seq 值作為本段報文確認號的值。隨後客戶端開始在 TIME-WAIT 階段等待 2MSL。

服務器端收到從客戶端發出的 TCP 報文之後結束 LAST-ACK 階段,進入 CLOSED 階段。由此正式確認關閉服務器端到客戶端方向上的連接。

客戶端等待完 2MSL 之後,結束 TIME-WAIT 階段,進入 CLOSED 階段,由此完成“四次揮手”。

後“兩次揮手”既讓客戶端知道了服務器端準備好釋放連接了,也讓服務器端知道了客戶端了解了自己準備好釋放連接了。

於是,可以確認關閉服務器端到客戶端方向上的連接了,由此完成“四次揮手”。

與“三次揮手”一樣,在客戶端與服務器端傳輸的 TCP 報文中,雙方的確認號 Ack 和序號 Seq 的值,都是在彼此 Ack 和 Seq 值的基礎上進行計算的。

這樣保證了 TCP 報文傳輸的連貫性,一旦出現某一方發出的 TCP 報文丟失,便無法繼續"揮手",以此確保了"四次揮手"的順利完成。

“四次揮手”的動態過程

三次握手+四次揮手,一文搞定所有

“四次揮手”的通俗理解

三次握手+四次揮手,一文搞定所有

舉個栗子:把客戶端比作男孩,服務器比作女孩。

通過他們的分手來說明“四次揮手”過程:

  • "第一次揮手":日久見人心,男孩發現女孩變成了自己討厭的樣子,忍無可忍,於是決定分手,隨即寫了一封信告訴女孩。
  • “第二次揮手”:女孩收到信之後,知道了男孩要和自己分手,怒火中燒,心中暗罵:你算什麼東西,當初你可不是這個樣子的!於是立馬給男孩寫了一封回信:分手就分手,給我點時間,我要把你的東西整理好,全部還給你!

男孩收到女孩的第一封信之後,明白了女孩知道自己要和她分手。隨後等待女孩把自己的東西收拾好。

  • “第三次揮手”:過了幾天,女孩把男孩送的東西都整理好了,於是再次寫信給男孩:你的東西我整理好了,快把它們拿走,從此你我恩斷義絕!
  • “第四次揮手”:男孩收到女孩第二封信之後,知道了女孩收拾好東西了,可以正式分手了,於是再次寫信告訴女孩:我知道了,這就去拿回來!

這裡雙方都有各自的堅持:

  • 女孩自發出第二封信開始,限定一天內收不到男孩回信,就會再發一封信催促男孩來取東西!
  • 男孩自發出第二封信開始,限定兩天內沒有再次收到女孩的信就認為,女孩收到了自己的第二封信;若兩天內再次收到女孩的來信,就認為自己的第二封信女孩沒收到,需要再寫一封信,再等兩天…..

倘若雙方信都能正常收到,最少只用四封信就能徹底分手!這就是“四次揮手”。

為啥握手是三次,揮手卻要四次

TCP 建立連接時之所以只需要"三次握手",是因為在第二次"握手"過程中,服務器端發送給客戶端的 TCP 報文是以 SYN 與 ACK 作為標誌位的。

SYN 是請求連接標誌,表示服務器端同意建立連接;ACK 是確認報文,表示告訴客戶端,服務器端收到了它的請求報文。

即 SYN 建立連接報文與 ACK 確認接收報文是在同一次"握手"當中傳輸的,所以"三次握手"不多也不少,正好讓雙方明確彼此信息互通。

TCP 釋放連接時之所以需要“四次揮手”,是因為 FIN 釋放連接報文與 ACK 確認接收報文是分別由第二次和第三次"握手"傳輸的。

為何建立連接時一起傳輸,釋放連接時卻要分開傳輸?

  • 建立連接時,被動方服務器端結束 CLOSED 階段進入“握手”階段並不需要任何準備,可以直接返回 SYN 和 ACK 報文,開始建立連接。
  • 釋放連接時,被動方服務器,突然收到主動方客戶端釋放連接的請求時並不能立即釋放連接。

因為還有必要的數據需要處理,所以服務器先返回 ACK 確認收到報文,經過 CLOSE-WAIT 階段準備好釋放連接之後,才能返回 FIN 釋放連接報文。

所以是“三次握手”,“四次揮手”。

為啥客戶端在TIME-WAIT階段要等2MSL

為的是確認服務器端是否收到客戶端發出的 ACK 確認報文,當客戶端發出最後的 ACK 確認報文時,並不能確定服務器端能夠收到該段報文。

所以客戶端在發送完 ACK 確認報文之後,會設置一個時長為 2MSL 的計時器。

MSL 指的是 Maximum Segment Lifetime:一段 TCP 報文在傳輸過程中的最大生命週期。

2MSL 即是服務器端發出為 FIN 報文和客戶端發出的 ACK 確認報文所能保持有效的最大時長。

服務器端在 1MSL 內沒有收到客戶端發出的 ACK 確認報文,就會再次向客戶端發出 FIN 報文:

  • 如果客戶端在 2MSL 內,再次收到了來自服務器端的 FIN 報文,說明服務器端由於各種原因沒有接收到客戶端發出的 ACK 確認報文。

客戶端再次向服務器端發出 ACK 確認報文,計時器重置,重新開始 2MSL 的計時。

  • 否則客戶端在 2MSL 內沒有再次收到來自服務器端的 FIN 報文,說明服務器端正常接收了 ACK 確認報文,客戶端可以進入 CLOSED 階段,完成“四次揮手”。

所以,客戶端要經歷時長為 2SML 的 TIME-WAIT 階段;這也是為什麼客戶端比服務器端晚進入 CLOSED 階段的原因。

抓包驗證

三次握手+四次揮手,一文搞定所有

圖中顯示的就是完整的 TCP 連接釋放的”四次揮手”過程。在 80→55389 中,假設 80 是本地(客戶端)端口,55389 是服務器端口。

80 端口與 55389 之間的四次來回就是"四次揮手"過程:

  • “第一次揮手”客戶端發送的 FIN 請求釋放連接報文以[FIN,ACK]作為標誌位,其中報文序號 Seq=2445;確認號 Ack=558。注意:這裡與“第三次握手”的 ACK 並不是表示確認的 ACK 報文。
  • “第二次揮手”服務器端返回的 ACK 確認報文以[ACK]作為標誌位;其中報文序號 Seq=558;確認號 Ack=2246。
  • “第三次揮手”服務器端繼續返回的 FIN 同意釋放連接報文以[FIN,ACK]作為標誌位;其中報文序號 Seq=558;確認號 Ack=2246。
  • “第四次揮手”客戶端發出的 ACK 確認接收報文以[ACK]作為標誌位;其中報文序號 Seq=2446;確認號 Ack=559。

後一次“揮手”傳輸報文中的序號 Seq 值等於前一次"握手"傳輸報文中的確認號 Ack 值。

後一次“揮手”傳輸報文中的確認號 Ack 值等於前一次"握手"傳輸報文中的序號 Seq 值。

故這是連續的“四次揮手”過程,與前面的分析相符。


分享到:


相關文章: