爲什麼TCP建立連接協議是三次握手,而關閉連接卻是四次揮手呢?

看到了一道面試題:“爲什麼TCP建立連接協議是三次握手,而關閉連接卻是四次握手呢?爲什麼不能用兩次握手進行連接?”,想想最近也到金三銀四了,所以就查閱了相關資料,整理出來了這篇文章,希望對你們有所幫助。

TCP 連接

我們先來補一下基礎什麼是 TCP 協議?傳輸控制協議( Transmission Control Protocol, TCP )是種面向連接、確保數據在端到端間可靠傳輸的協議。面向連接是插在發送數據前,需要先建立一條虛擬的鏈路,然後讓數據在這條鏈路上“流動”完成傳輸。

TCP 是可靠的,會盡自己最大的努力去完成數據傳輸,TCP 協議比較複雜,可以看下面這張 TCP 協議的報文頭圖片:

TCP 協議報文頭格式

內容非常的豐富,跟我們今天要討論的連接協議相關的就是中間那六個狀態位: URG、ACK、PSH、RST、SYN、FIN ,都置爲 1 表示有效,在這六個當中,我們主要關注重點關注 ACK、SYN、FIN 這三個。下面解釋一下這三個狀態位:

ACK:用於對收到的數據進行確認,所確認的數據由確認序列號表示。

SYN:用作建立連接時的同步信號

FIN:表示後面沒有數據需要發送,通常意昧着所建立的連接需要關閉了。

好了,到這裏,TCP 的基礎知識我們就知道了,下面我們就來看看爲什麼
是三次握手,而不是四次或者兩次
,爲了讓你更好的理解,我把知乎上一個高贊特別形象的比喻放在這裏,希望對你有所幫助。

兩次和四次都會出現問題,三次就剛剛好,希望這張圖能夠讓你更好的理解爲什麼是三次握手。

我們已經知道了 TCP 協議是三次握手,爲什麼是三次握手呢?我們先來看看下面這張 TCP 協議建立連接的時序圖。

TCP 協議三次握手時序圖

總體來說就是呼叫、應答、迴應,我們來詳細的介紹每一步:

  • 第一步: A 機器向 B機器發出一個數據包並將 SYN 設置爲 1 ,表示希望建立連接。這個包中的序列號假設是 X。
  • 第二步: B 機器收到 A機器發過來的數據包後,通過 SYN 得知這是一個建立連接的請求,於是發送一個響應包並將 SYN 、ACK 標記都置爲 1。假設這個包中的序列號是 y ,而確認序列號必須是 x+l ,表示收到了 發過來的 SYN,TCP 中, SYN 被當作數據部分的一個字節。
  • 第三步: A 收到 的響應包後需進行確認,確認包中將 ACK ,並將確認序列號設置爲 y+ ,表示收到了來自B 的 SYN

經過這三步之後,兩臺服務器就建立連接了,可以進行通信數據傳輸了。爲什麼要三次握手呢?主要是爲了信息對等和防止出現請求超時導致髒連接。

第一是爲了保證兩臺機器信息對等,確保兩臺機器都沒有什麼問題:

信息對稱

只有三次握手之後才能夠保證兩臺服務器都完全沒有問題,各自具備發報和收報能力。

第二是防止出現請求超時導致髒連接,看下面這張圖:

防止出現請求超時導致髒連接

爲什麼會出現髒連接?因爲TTL 網絡報文的生存時間往往都會超 TCP 請求超時時間,如果兩次握手就可以創建連接 ,傳輸數據並釋放連接後,第一個超時的連接請求才到達 B 機器的話,B 機器會以爲是 A 創建新連接的請求,然後確認同意創建連接。因爲 A 機器的狀態不是 SYl_SENT ,所以直接丟棄了 B 的確認數據 ,以致最後只是 B 機器單方面創建連接完畢。

三次握手就可以解決這個問題,因爲需要 A 服務器確認了才真正的建立了連接。

TCP 四次揮手

上面介紹了 TCP 協議連接,有連接就有斷開,相對於三次連接,斷開卻需要四次揮手,怎麼理解呢?先看下面這個場景:

A:B 啊,我不想玩了。

B:哦,你不想玩了啊,我知道了。

這個時候,還只是 A 不想玩了,也即 A 不會再發送數據,但是 B 能不能在 ACK 的時候,直接關閉呢?當然不可以了,很有可能 A 是發完了最後的數據就準備不玩了,但是 B 還沒做完自己的事情,還是可以發送數據的,所以稱爲半關閉的狀態

這個時候 A 可以選擇不再接收數據了,也可以選擇最後再接收一段數據,等待 B 也主動關閉。

B:A 啊,好吧,我也不玩了,拜拜。

A:好的,拜拜。

這就是一個完整的關閉連接,在這個關閉的過程中,一共說了四句話,我們也稱之爲四次揮手。跟建立連接一樣,斷開時也是用狀態來表示,下面是斷開的時序圖:

狀態時序圖

我們結合上面的時序圖和場景再來分析一下 TCP 斷開過程。

當 A 說“不玩了”,A 就進入 FIN_WAIT_1 的狀態,B 收到“A 不玩”的消息後,發送知道了,B 就進入 CLOSE_WAIT 的狀態。

A 收到“B 說知道了”,就進入 FIN_WAIT_2 的狀態,如果這個時候 B 直接跑路,則 A 將永遠在這個狀態。雖然 TCP 協議裏面並沒有對這個狀態的處理,但是 Linux 有,可以調整 tcp_fin_timeout 這個參數,設置一個超時時間,最後 A 也會關閉的。

如果 B 沒有跑路,發送了“B 也不玩了”的請求到達 A 時,A 發送“知道 B 也不玩了”的 ACK 後,從 FIN_WAIT_2 狀態結束,按說 A 可以跑路了,但是最後的這個 ACK 萬一 B 收不到呢?則 B 會重新發一個“B 不玩了”,這個時候 A 已經跑路了的話,B 就再也收不到 ACK 了,因而 TCP 協議要求 A 最後等待一段時間 TIME_WAIT,這個時間要足夠長,長到如果 B 沒收到 ACK 的話,“B 說不玩了”會重發的,A 會重新發一個 ACK 並且足夠時間到達 B。

要求 A 等待 TIME_WAIT還有一個原因就是防止產生混亂,A 直接關閉了,但是這個時候 B是不知道的,可能在 A 關閉之前 B還發送了很多數據包,如果這時候 A 的端口被一個新的應用佔用了的話,那麼新的應用就會接收到上個連接中 B發送過來的數據包,這樣就混亂了,雖然這個數據包是無效的,但是等待 TIME_WAIT 可以是一個雙保險,因而也需要等足夠長的時間,等到原來 B 發送的所有的包都死翹翹,再空出端口來。

以上就是 TCP 協議三次握手,四次揮手的原因,希望這篇文章對您的學習或者工作有所幫助,如果您覺得文章不錯,還請您幫忙點個贊和轉發,謝謝。

最後

目前互聯網上很多大佬都有 TCP 協議相關文章,如有雷同,請多多包涵了。原創不易,碼字不易,還希望大家多多支持。若文中有所錯誤之處,還望提出,謝謝。

歡迎掃碼關注微信公衆號:「平頭哥的技術博文」,和平頭哥一起學習,一起進步。

平頭哥的技術博文

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章