1. 程式人生 > 其它 >為什麼 TCP 建立連線需要三次握手

為什麼 TCP 建立連線需要三次握手

​TCP 協議是我們幾乎每天都會接觸到的網路協議,絕大多數網路連線的建立都是基於 TCP 協議的,學過計算機網路或者對 TCP 協議稍有了解的人都知道 —— 使用 TCP 協議建立連線需要經過三次握手(three-way handshake)。

如果讓我們簡單說說 TCP 建立連線的過程,相信很多準備過面試的人都會非常瞭解,但是一旦想要深究"為什麼 TCP 建立連線需要三次握手?",作者相信大多數人都沒有辦法回答這個問題或者會給出錯誤的答案,這邊文章就會討論究竟為什麼我們需要三次握手才能建立 TCP 連線?

概述

首先我們都知道握手是為了建立連線,那麼為什麼必須得三次握手,兩次握手行不行?

Client ------SYN-----> Server
Client <---ACK/SYN---- Server
Client ------ACK-----> Server

為什麼不只是這個?

Client ------SYN-----> Server

Client <-----ACK------ Server

所以我們需要將握手分解為它真正在做什麼。

TCP 是一種雙向通訊協議,這意味著任何一端都應該能夠可靠地傳送資料。雙方都需要建立一個序列號,雙方都需要確認對方的序列號。

那麼client要與server建立TCP連線,需要通過握手確認這四件事情

1.server需要確認它可以從client接收資料包;

2.client需要確認它可以從server接收資料包;

3.client需要確認一件事:server可以從client接收資料包;

4.server需要確認一件事:client可以從server接收資料包。

我們再回到“那麼為什麼必須得三次握手,兩次握手行不行?”這個問題。

Client ------SYN-----> Server,事情1 得到確認。

Client <---ACK/SYN---- Server,事情2 和 3 得到確認。

因此,需要第三次握手來確認事情4,即

Client ------ACK-----> Server

到這裡,我們已經知道了為什麼建立TCP連線需要三次握手。

但是我們從TCP連線是什麼的角度去審視這個問題的話,又該如何解釋?我們先參考

ThereliabilityandflowcontrolmechanismsdescribedaboverequirethatTCPsinitializeandmaintaincertainstatusinformationforeachdatastream.Thecombination ofthisinformation,including sockets,sequencenumbers,andwindowsizes,iscalledaconnection.

文件中非常清楚地定義了 TCP 中的連線是什麼,我們簡單總結一下:用於保證可靠性和流控制機制的資訊,包括 Socket、序列號以及視窗大小叫做連線。

所以,建立 TCP 連線就是通訊的雙方需要對上述的三種資訊達成共識,連線中的一對 Socket 是由網際網路地址標誌符和埠組成的,視窗大小主要用來做流控制,最後的序列號是用來追蹤通訊發起方傳送的資料包序號,接收方可以通過序列號向傳送方確認某個資料包的成功接收。

到這裡,我們將原有的問題轉換成了“為什麼需要通過三次握手才可以初始化 Sockets、視窗大小和初始序列號?“,那麼接下來我們就開始對這個細化的問題進行分析並尋找解釋。

設計

所以接下來我們主要討論以下幾個方面,進行解釋“為什麼需要通過三次握手才可以初始化 Sockets、視窗大小和初始序列號?“

1.通過三次握手才能阻止重複歷史連線的初始化;

2.通過三次握手才能對通訊雙方的初始序列號進行初始化;

3.討論其他次數握手建立連線的可能性。

阻止重複歷史連線的初始化

The principlereasonforthethree-wayhandshakeistopreventoldduplicateconnectioninitiationsfromcausingconfusion.

文件中指出了 TCP 連線使用三次握手的首要原因 —— 為了阻止歷史的重複連線初始化造成的混亂問題,防止使用 TCP 協議通訊的雙方建立了錯誤的連線。

在網路狀況差或者複雜時,如果一個已經發送的連線請求在超時時間內沒有收到確認,傳送方連續傳送多次建立連線的請求。想象一下,如果TCP建立連線僅有兩次握手,那麼雙方會建立錯誤的連線,因為接收方接收到連線請求時並不清楚這是由於網路擁塞而早已過期的連線。

所以,TCP 選擇使用三次握手來建立連線並在連線引入了 RST 這一控制訊息,接收方當收到請求時會將傳送方發來的 SEQ+1 傳送給對方,這時由傳送方來判斷當前連線是否是歷史連線:

1.如果當前連線是歷史連線,即 SEQ 過期或者超時,那麼傳送方就會直接傳送 RST 控制訊息中止這一次連線;

2.如果當前連線不是歷史連線,那麼傳送方就會發送 ACK 控制訊息,通訊雙方就會成功建立連線。

使用三次握手和RST控制訊息將是否建立連線的最終控制權交給了傳送方,因為只有傳送方有足夠的上下文來判斷當前連線是否是錯誤的或者過期的,這也是使用三次握手建立TCP連線的最主要原因。

對通訊雙方的初始序列號進行初始化

另一個使用三次握手的重要的原因就是通訊雙方都需要獲得一個用於傳送資訊的初始化序列號,作為一個可靠的傳輸層協議,TCP 需要在不穩定的網路環境中構建一個可靠的傳輸層,網路的不確定性可能會導致資料包的缺失和順序顛倒等問題,常見的問題可能包括:

1.資料包被髮送方多次傳送造成資料的重複;

2.資料包在傳輸的過程中被路由或者其他節點丟失;

3.資料包到達接收方可能無法按照發送順序。

為了解決上述這些可能存在的問題,TCP 協議要求傳送方在資料包中加入“序列號“欄位,有了資料包對應的序列號,我們就可以:

1.接收方可以通過序列號對重複的資料包進行去重;

2.傳送方會在對應資料包未被 ACK 時進行重複傳送;

3.接收方可以根據資料包的序列號對它們進行重新排序。

序列號在 TCP 連線中有著非常重要的作用,初始序列號作為 TCP 連線的一部分也需要在三次握手期間進行初始化,由於 TCP 連線通訊的雙方都需要獲得初始序列號,所以它們其實需要向對方傳送 SYN 控制訊息並攜帶自己期望的初始化序列號 SEQ,對方在收到 SYN 訊息之後會通過 ACK 控制訊息以及 SEQ+1 來進行確認。

通訊次數

當我們討論 TCP 建立連線需要的通訊次數時,我們經常會執著於“為什麼通訊三次才可以建立連線,兩次行不行?”,我們不會討論“四次、五次握手行不行?”。增加 TCP 連線通訊次數的問題往往沒有討論的必要性,我們追求的其實是用更少的通訊次數(理論上的邊界)完成資訊的交換,也就是為什麼一再強調使用“兩次握手“沒有辦法建立 TCP 連線,使用”三次握手“是建立連線所需要的最小次數。

總結

我們重新回到在文章開頭提的問題,為什麼建立TCP連線需要三次握手?經過分析,答案非常清晰了。

1.防止使用 TCP 協議通訊的雙方建立了錯誤的連線;

2.減少通訊雙方不必要的資源消耗;

3.幫助通訊雙方獲取初始化序列號,它們能夠保證資料包傳輸的不重不丟,還能保證它們的傳輸順序。

不使用兩次握手和四次握手的原因是:

1.兩次握手,無法避免歷史錯誤連線的初始化,浪費接收方的資源;

2.四次握手,TCP 通訊協議的設計可以讓我們同時傳遞 ACK 和 SYN 兩個控制資訊,減少了通訊次數,所以不需要使用更多的通訊次數傳輸相同的資訊。

參考資料

[^1]Why do we need a 3-way handshake? Why not just 2-way?

https://networkengineering.stackexchange.com/questions/24068/why-do-we-need-a-3-way-handshake-why-not-just-2-way

[^2]rfc793

https://datatracker.ietf.org/doc/html/rfc793

[^3]為什麼建立TCP連線需要三次握手

https://draveness.me/whys-the-design-tcp-three-way-handshake

[^4]計算機網路-傳輸層

https://github.com/CyC2018/CS-Notes/blob/master/notes