?? 盡人事,聽天命,博主東南大學研究生在讀,熱愛健身和籃球,正在為兩年后的秋招準備中,樂于分享技術相關的所見所得,關注公眾號 @ 飛天小牛肉,第一時間獲取文章更新,成長的路上我們一起進步
?? 本文已收錄于 CS-Wiki(Gitee 官方推薦專案,現已 0.9k star),致力打造完善的后端知識體系,在技術的路上少走彎路,歡迎各位小伙伴前來交流學習
0. 前言
在面試中,計算機網路的 TCP 三次握手和四次揮手是很常見的問題,但是在實際面試中,面試官會更愿意聽到怎樣的回答呢?詳細程度是怎樣的?
越簡單常見的問題越不可小覷,萬丈高樓平地起,把簡單的問題深入化,才更能拉開與競爭者的距離,掌握了本文講的全部知識點,關于 TCP 三次握手和四次揮手基本就 OK 了 ??
1. TCP 和 UDP
講解 TCP 三次握手和四次握手之前,我們先了解一下 TCP 和 UDP 這兩個重量級的傳輸層協議,
?? 用戶資料報協議 UDP(User Datagram Protocol):
-
UDP 在傳送資料之前不需要先建立連接,遠程主機在收到 UDP 報文后,不需要給出任何確認,
-
雖然 UDP 不提供可靠交付,但在某些情況下 UDP 確是一種最有效的作業方式(一般用于即時通信),比如: QQ 語音、 QQ 視頻 、直播等等
?? 傳輸控制協議 TCP(Transmission Control Protocol):
-
TCP 提供面向連接的服務,在傳送資料之前必須先建立連接,資料傳送結束后要釋放連接,
-
TCP 不提供廣播或多播服務,由于 TCP 要提供可靠的,面向連接的傳輸服務(TCP的可靠體現在TCP在傳遞資料之前,會有三次握手來建立連接,而且在資料傳遞時,有確認、視窗、重傳、流量控制、擁塞控制機制,在資料傳完后,還會四次揮手斷開連接用來節約系統資源),這不僅使協議資料單元的首部增大很多,還要占用許多處理機資源,
-
TCP 一般用于檔案傳輸、發送和接收郵件、遠程登錄等場景,
2. TCP 報文段首部格式
TCP 報文段的具體格式大家可以不必都記住,但是其中的幾個控制位與我們接下來要講的三次握手和四次揮手息息相關,大家一定要牢記,

首部固定部分各欄位意義如下:
-
1 - 源埠和目的埠:各占 2 個位元組,分別寫入源埠和目的埠,IP 地址 + 埠號就可以確定一個行程地址
-
2 - 序號/序列號(Sequense Number,SN):在一個 TCP 連接中傳送的位元組流中的每一個位元組都按順序編號,該欄位表示本報文段所發送的資料的第一個位元組的序號,初始序號稱為 Init Sequense Number, ISN(序號/序列號這個欄位很重要,大家留個印象,下文會詳細講解)
例如,一報文段的序號是 101,共有 100 位元組的資料,這就表明:本報文段的資料的第一個位元組的序號是 101,最后一個位元組的序號是 200,顯然,下一個報文段的資料序號應當從 201 開始,即下一個報文段的序號欄位值應為 201,
-
3 - 確認號 ack:期望收到對方下一個報文段的第一個資料位元組的序號,若確認號為 N,則表明:到序號 N-1 為止的所有資料都已正確收到,
-
4 - 資料偏移(首部長度):它指出 TCP 報文段的資料起始處距離 TCP 報文段的起始處有多遠,這個欄位實際上是指出TCP報文段的首部長度,
-
5 - 保留:占 6 位,應置為 0,保留為今后使用,
? 大家看上圖,保留位的右邊還有 6 個控制位(重要),這是TCP 用來說明該報文段性質的:
-
緊急位 URG:當 URG = 1 時,表明此報文段中有緊急資料,是高優先級的資料,應盡快發送,不用在快取中排隊,該控制位需配合緊急指標使用(緊急指標指出本報文段中緊急資料的位元組數)
舉個例子:我們需要取消一個已經發送了很長程式的運行,因此用戶從鍵盤發出中斷命令,如果不使用緊急資料,那么這個指令將存盤在接收 TCP 的快取末尾,只有在所有的資料被處理完畢后這兩個字符才被交付接收方的應用行程,這樣做就無法實作立即中斷,
-
確認 ACK:僅當 ACK = 1 時確認號欄位才有效,當 ACK = 0 時確認號無效,TCP 規定,在連接建立后所有傳送的報文段都必須把 ACK 置為 1,
-
推送 PSH:當兩個應用行程進行互動式的通信時,有時在一端的應用行程希望在鍵入一個命令后立即就能收到對方的回應,在這種情況下,TCP 就可以使用推送(push)操作,這時,發送方 TCP 把 PSH 置為 1,并立即創建一個報文段發送出去,接收方 TCP 收到 PSH = 1 的報文段,就盡快地交付接收應用行程,而不用等到整個快取都填滿了后再向上交付,
-
復位 RST:當 RST = 1 時,表明 TCP 連接中出現了嚴重錯誤(如由于主機崩潰或其他原因),必須釋放連接,然后再重新建立傳輸連接,
-
同步 SYN:SYN = 1 表示這是一個連接請求或連接接受報文,
當 SYN = 1 而 ACK = 0 時,表明這是一個連接請求報文段,對方若同意建立連接,則應在回應的報文段中使 SYN = 1 且 ACK = 1,
-
終止 FIN:用來釋放一個連接,當 FIN = 1時,表明此報文段的發送發的資料已發送完畢,并要求釋放運輸連接,
3. TCP 三次握手建立連接
① 三次握手程序詳解
三次握手的原文是 three-way handshake,整個名詞的可以翻譯為:需要三個步驟才能建立握手/連接的機制,當然,三次握手也可以叫 three-message handshake,通過三條訊息來建立的握手/連接,
進行三次握手的主要作用就是為了確認雙方的接收能力和發送能力是否正常、指定自己的 初始化序列號(Init Sequense Number, ISN) 為后面的可靠性傳輸做準備,
三次握手程序如下圖:

回顧一下圖中字符的含義:
-
SYN:連接請求/接收 報文段 -
seq:發送的第一個位元組的序號 -
ACK:確認報文段 -
ack:確認號,希望收到的下一個資料的第一個位元組的序號
剛開始客戶端處于 Closed 的狀態,而服務端處于 Listen 狀態:
CLOSED:沒有任何連接狀態
LISTEN:偵聽來自遠方 TCP 埠的連接請求
1)第一次握手:客戶端向服務端發送一個 SYN 報文(SYN = 1),并指明客戶端的初始化序列號 ISN(x),即圖中的 seq = x,表示本報文段所發送的資料的第一個位元組的序號,此時客戶端處于 SYN_Send 狀態,
SYN-SENT:在發送連接請求后等待匹配的連接請求
2)第二次握手:服務器收到客戶端的 SYN 報文之后,會發送 SYN 報文作為應答(SYN = 1),并且指定自己的初始化序列號 ISN(y),即圖中的 seq = y,同時會把客戶端的 ISN + 1 作為確認號 ack 的值,表示已經收到了客戶端發來的的 SYN 報文,希望收到的下一個資料的第一個位元組的序號是 x + 1,此時服務器處于 SYN_REVD 的狀態,
SYN-RECEIVED:在收到和發送一個連接請求后等待對連接請求的確認
3)第三次握手:客戶端收到服務器端回應的 SYN 報文之后,會發送一個 ACK 報文,也是一樣把服務器的 ISN + 1 作為 ack 的值,表示已經收到了服務端發來的的 SYN 報文,希望收到的下一個資料的第一個位元組的序號是 y + 1,并指明此時客戶端的序列號 seq = x + 1(初始為 seq = x,所以第二個報文段要 +1),此時客戶端處于 Establised 狀態,
服務器收到 ACK 報文之后,也處于 Establised 狀態,至此,雙方建立起了 TCP 連接,
ESTABLISHED:代表一個打開的連接,資料可以傳送給用戶
② 為什么要三次握手
三次握手的目的是建立可靠的通信信道,說到通訊,簡單來說就是資料的發送與接收,而三次握手最主要的目的就是雙方確認自己與對方的發送與接收是正常的,
只有經過三次握手才能確認雙發的收發功能都正常,缺一不可:
-
第一次握手(客戶端發送 SYN 報文給服務器,服務器接收該報文):客戶端什么都不能確認;服務器確認了對方發送正常,自己接收正常
-
第二次握手(服務器回應 SYN 報文給客戶端,客戶端接收該報文):
客戶端確認了:自己發送、接收正常,對方發送、接收正常;
服務器確認了:對方發送正常,自己接收正常
-
第三次握手(客戶端發送 ACK 報文給服務器):
客戶端確認了:自己發送、接收正常,對方發送、接收正常;
服務器確認了:自己發送、接收正常,對方發送、接收正常
③ ISN (Initial Sequence Number) 是固定的嗎
三次握手的其中一個重要功能是客戶端和服務端交換 ISN(Initial Sequence Number),以便讓對方知道接下來接收資料的時候如何按序列號組裝資料,
當一端為建立連接而發送它的 SYN 時,它會為連接選擇一個初始序號,ISN 隨時間而變化,因此每個連接都將具有不同的 ISN,如果 ISN 是固定的,攻擊者很容易猜出后續的確認號,因此 ISN 是動態生成的,
④ 三次握手程序中可以攜帶資料嗎
第三次握手的時候,是可以攜帶資料的,但是,第一次、第二次握手絕對不可以攜帶資料
假如第一次握手可以攜帶資料的話,如果有人要惡意攻擊服務器,那他每次都在第一次握手中的 SYN 報文中放入大量的資料,然后瘋狂重復發 SYN 報文的話(因為攻擊者根本就不用管服務器的接收、發送能力是否正常,它就是要攻擊你),這會讓服務器花費很多時間、記憶體空間來接收這些報文,
? 簡單的記憶就是,請求連接/接收 即 SYN = 1 的時候不能攜帶資料
而對于第三次的話,此時客戶端已經處于 ESTABLISHED 狀態,對于客戶端來說,他已經建立起連接了,并且也已經知道服務器的接收、發送能力是正常的了,所以當然能正常發送/攜帶資料了,
⑤ 半連接佇列
服務器第一次收到客戶端的 SYN 之后,就會處于 SYN_RCVD 狀態,此時雙方還沒有完全建立其連接,服務器會把這種狀態下的請求連接放在一個佇列里,我們把這種佇列稱之為半連接佇列,
當然還有一個全連接佇列,完成三次握手后建立起的連接就會放在全連接佇列中,如果佇列滿了就有可能會出現丟包現象,
⑥ SYN 洪泛攻擊
SYN 攻擊就是 Client 在短時間內偽造大量不存在的 IP 地址,并向 Server 不斷地發送 SYN 包,Server 則回復確認包,并等待 Client 確認,由于源地址不存在,因此 Server 需要不斷重發直至超時,這些偽造的 SYN 包將長時間占用半連接佇列,導致正常的 SYN 請求因為佇列滿而被丟棄,從而引起網路擁塞甚至系統癱瘓,
⑦ 如果第三次握手丟失了,客戶端服務端會如何處理
服務器發送完 SYN-ACK 包,如果未收到客戶端回應的確認包,也即第三次握手丟失,那么服務器就會進行首次重傳,若等待一段時間仍未收到客戶確認包,就進行第二次重傳,如果重傳次數超過系統規定的最大重傳次數,則系統將該連接資訊從半連接佇列中洗掉,
注意,每次重傳等待的時間不一定相同,一般會是指數增長,例如間隔時間為 1s,2s,4s,8s…
4. TCP 四次揮手釋放連接
① 四次揮手程序詳解
建立一個 TCP 連接需要三次握手,而終止一個 TCP 連接要經過四次揮手(也有將四次揮手叫做四次握手的),這是由于 TCP 的半關閉(half-close)特性造成的,TCP 提供了連接的一端在結束它的發送后還能接收來自另一端資料的能力,
TCP 連接的釋放需要發送四個包(執行四個步驟),因此稱為四次揮手(Four-way handshake),客戶端或服務端均可主動發起揮手動作,

回顧一下上圖中符號的意思:
-
FIN:連接終止位 -
seq:發送的第一個位元組的序號 -
ACK:確認報文段 -
ack:確認號,希望收到的下一個資料的第一個位元組的序號
剛開始雙方都處于ESTABLISHED 狀態,假設是客戶端先發起關閉請求,四次揮手的程序如下:
1)第一次揮手:客戶端發送一個 FIN 報文(請求連接終止:FIN = 1),報文中會指定一個序列號 seq = u,并停止再發送資料,主動關閉 TCP 連接,此時客戶端處于 FIN_WAIT1 狀態,等待服務端的確認,
FIN-WAIT-1- 等待遠程TCP的連接中斷請求,或先前的連接中斷請求的確認;
2)第二次揮手:服務端收到 FIN 之后,會發送 ACK 報文,且把客戶端的序號值 +1 作為 ACK 報文的序列號值,表明已經收到客戶端的報文了,此時服務端處于 CLOSE_WAIT 狀態,
CLOSE-WAIT- 等待從本地用戶發來的連接中斷請求;
此時的 TCP 處于半關閉狀態,客戶端到服務端的連接釋放,客戶端收到服務端的確認后,進入FIN_WAIT2(終止等待 2)狀態,等待服務端發出的連接釋放報文段,
FIN-WAIT-2- 從遠程TCP等待連接中斷請求;
3)第三次揮手:如果服務端也想斷開連接了(沒有要向客戶端發出的資料),和客戶端的第一次揮手一樣,發送 FIN 報文,且指定一個序列號,此時服務端處于 LAST_ACK 的狀態,等待客戶端的確認,
LAST-ACK- 等待原來發向遠程TCP的連接中斷請求的確認;
4)第四次揮手:客戶端收到 FIN 之后,一樣發送一個 ACK 報文作為應答(ack = w+1),且把服務端的序列值 +1 作為自己 ACK 報文的序號值(seq=u+1),此時客戶端處于 TIME_WAIT (時間等待)狀態,
TIME-WAIT- 等待足夠的時間以確保遠程TCP接收到連接中斷請求的確認;
?? 注意 !!!這個時候由服務端到客戶端的 TCP 連接并未釋放掉,需要經過時間等待計時器設定的時間 2MSL(一個報文的來回時間) 后才會進入 CLOSED 狀態(這樣做的目的是確保服務端收到自己的 ACK 報文,如果服務端在規定時間內沒有收到客戶端發來的 ACK 報文的話,服務端會重新發送 FIN 報文給客戶端,客戶端再次收到 FIN 報文之后,就知道之前的 ACK 報文丟失了,然后再次發送 ACK 報文給服務端),服務端收到 ACK 報文之后,就關閉連接了,處于 CLOSED 狀態,
② 為什么要四次揮手
由于 TCP 的半關閉(half-close)特性,TCP 提供了連接的一端在結束它的發送后還能接收來自另一端資料的能力,
任何一方都可以在資料傳送結束后發出連接釋放的通知,待對方確認后進入半關閉狀態,當另一方也沒有資料再發送的時候,則發出連接釋放通知,對方確認后就完全關閉了TCP連接,
通俗的來說,兩次握手就可以釋放一端到另一端的 TCP 連接,完全釋放連接一共需要四次握手,
舉個例子:A 和 B 打電話,通話即將結束后,A 說 “我沒啥要說的了”,B 回答 “我知道了”,于是 A 向 B 的連接釋放了,但是 B 可能還會有要說的話,于是 B 可能又巴拉巴拉說了一通,最后 B 說“我說完了”,A 回答“知道了”,于是 B 向 A 的連接釋放了,這樣整個通話就結束了,
?? 關注公眾號 | 飛天小牛肉,即時獲取更新
-
博主東南大學研究生在讀,利用課余時間運營一個公眾號『 飛天小牛肉 』,2020/12/29 日開通,專注分享計算機基礎(資料結構 + 演算法 + 計算機網路 + 資料庫 + 作業系統 + Linux)、Java 基礎和面試指南的相關原創技術好文,本公眾號的目的就是讓大家可以快速掌握重點知識,有的放矢,希望大家多多支持哦,和小牛肉一起成長 ??
-
并推薦個人維護的開源教程類專案: CS-Wiki(Gitee 推薦專案,現已 0.9k star), 致力打造完善的后端知識體系,在技術的路上少走彎路,歡迎各位小伙伴前來交流學習 ~ ??
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/260856.html
標籤:Java
