【俗話說】換個角度理解TCP的三次握手和四次揮手

SH的全棧筆記發表於2020-03-03

PS:通俗一點的解釋都會在引用塊中

Nothing is true, Everything is permitted.

0. 什麼是TCP

TCP,全稱Transmission Control Protocol,是一種面向連線、可靠的、基於位元組流的單播協議。與我們常說的TCP/IP協議不同,TCP/IP是一個協議族,涉及到OSI模型中的網路層、應用層和應用層。而我們要聊的TCP就是在傳輸層的協議,現在應用的特別廣泛的HTTP請求,就是基於TCP的。

1. 三次握手

所謂面向連線很好理解,就像我們要對遠端伺服器發出一個指令,首先我們需要登入上去。這個登入就是一個連線的過程。

在做資料交換之前,通訊雙方必須在彼此建立一條連線。也就是通訊雙方都維護了一份對方的資訊,比如IP地址和埠號。說到建立連線,就不得不提到經典的三次握手和四次揮手。

1.1 為什麼不兩次握手

三次握手讓通訊雙方都明確有一個連線正在建立,也為了確保客戶端和伺服器同時具有傳送接收的能力。而兩次握手做不到這一點。我們現在從另外一個角度來看一下三次握手,那就是為什麼要三次握手?我兩次握手它不香嗎?讓我們用一段對話來模擬如果真的採用兩次握手,會帶來什麼問題。

朋友:喂,喂?聽得到嗎

你:聽得到…你聲音能不能小點

這就是兩次握手。

按照人的邏輯來說,這已經是一次正常的對話了是吧,下一步難道不是建立連線嗎?說下一步之前,需要先了解做三次握手的目的是什麼。三次握手讓通訊雙方都明確有一個連線正在建立,也為了確保客戶端和伺服器同時具有傳送接收的能力。

我們來分析一下上面的那段對話。

朋友問你能不能聽到,說明朋友具有傳送能力;你聽到了朋友的問題,說明你具有接收能力

如果只有兩次握手,問題在哪兒呢?

站在朋友的角度,他知道你同時具有傳送接收能力

但站在你(伺服器)的角度,你只知道朋友具有傳送能力,因為你不知道你發的聲音能不能小點,他到底有沒有收到

伺服器不清楚客戶端是否有接收能力的情況下,就算資料包真的發出去了,但無法知道客戶端是否收到了資料。這樣的就是不可靠的連線了。

而且,真實的網路傳輸中,出現網路延遲是常有的事,如果客戶端傳送了請求建立連線的資料包,由於網路延遲,資料包沒有到達,客戶端又發了一次,伺服器收到之後建立了連線。

但是當前的連線關閉後,由於網路延遲的沒有到達的包到了伺服器,伺服器又建立了連線,但是此時客戶端已經斷開了,這樣就白白浪費了伺服器的資源。

如果覺得上面的例子還是不能讓你理解, 為什麼兩次握手不行。請看下面這個終極例子。

朋友:快借我點錢,XX寶賬號123XXXXXXXX

你:好的, 你的帳號是123XXXXXXXX嗎

。。。。。。(無應答)

你的內心:??????

如果你是被借錢的那個,你敢把錢轉過去嗎?

0341fda0555dc92b70a2ea4874115d5b
0341fda0555dc92b70a2ea4874115d5b

簡單總結一下兩次握手所帶來的問題:不可靠,還會造成網路資源的浪費。

1.2 三次握手的過程

上面我們討論了為什麼要三次握手,接下來我們用幾個專業術語來解釋一下三次握手的過程。

  • 伺服器開始監聽某個埠,此時伺服器進入了LISTEN狀態

  • 客戶端最初是CLOSED狀態,然後向伺服器傳送一個SYN標誌位的資料包,主動發起連線。客戶端變成SYN-SENT狀態

  • 伺服器接收到客戶端的SYN資料包,通過標誌位知道了客戶端想要建立連線。於是回了客戶端一個SYN和ACK,表示收到了請求。伺服器的自身狀態變為了SYN-RCVD

  • 客戶端收到了伺服器的ACK,表示伺服器知道了客戶端想要建立連線。然後客戶端再給伺服器回了一個ACK表示自己收到了(或者說能夠收到)伺服器的訊息,傳送完這個ACK後,客戶端的狀態變成了ESTABLISH

  • 伺服器收到了客戶端的ACK,伺服器的狀態也變成ESTABLISH

2. 四次揮手

2.1 模擬四次揮手

老規矩,還是讓我們用一段對話來模擬TCP的四次揮手。

場景,你跟你的朋友們正在外面high

你:你們繼續玩,我就先走了,明天還要上班(第一次)

老鐵:(老鐵看到你在跟他說話且從你說的話中知道你要走了,老鐵也用肢體語言告訴你他知道你要走了)(第二次)

老鐵:那好吧, 路上注意安全哈 (第三次)

你:好的,下次再約 (第四次)

這就是通俗版本的四次揮手的解釋,下面從專業的角度來看看。

2.2 四次揮手的過程

我們來看一下完整的流程。

  • 最初,客戶端和伺服器都處於ESTABLISH狀態

  • 客戶端想要斷開連線,便主動向伺服器傳送標誌位為FIN的資料包。傳送之後客戶端的狀態變為FIN-WAIT-1,同時客戶端也變成了半關閉狀態,即無法向伺服器傳送資料包了,只能接收來自伺服器的資料

  • 伺服器收到客戶端的FIN資料包,狀態變為CLOSE-WAIT,並回給客戶端一個表示確認的資料包ACK

  • 客戶端收到了ACK之後,狀態變為FIN-WAIT-2

  • 然後,伺服器向客戶端傳送FIN資料包,伺服器狀態變為LAST-ACK

  • 客戶端收到FIN資料包,客戶端狀態變為TIME-WAIT。然後回一個確認資料包ACK給伺服器

  • 然後客戶端等待2MSL,如果在這段時間內,沒有收到伺服器重發的訊息,說明伺服器收到了ACK

  • 四次揮手到此結束,連線斷開

我們再來模擬一次剛剛的場景。

場景,你跟你的朋友們正在外面high

你:你們繼續玩,我就先走了,明天還要上班(第一次)

老鐵:(老鐵喝high了,反射弧無限延長)

你肯定得再說一次啊,給朋友說你要走了,於是你又說了一次。

你:你們繼續玩,我就先走了,明天還要上班(第一次)

老鐵:(老鐵喝high了,反射弧無限延長)

。。。。。。

如此反覆

實際情況是,如果是兩次揮手,也就是把伺服器給客戶端的ACK和FIN合併為同一個,如果此時網路出現了延遲,站在客戶端的角度來看,客戶端會認為剛剛傳送的FIN報文並沒有到達伺服器,於是會在再重新傳送一次。如果延遲的時間較長,那麼客戶端將會一直重新傳送FIN的TCP報文。

2.3 對比分析

結合抽象和具體的四次揮手,其實就很好理解了,我們用一個表格來總結一下。

描述狀態 實際情況
你和你的朋友在外面high 客戶端和伺服器建立了連線
你和朋友說你要走了 客戶端主動向伺服器傳送FIN,客戶端狀態變為FIN-WAIT-1
你的朋友聽到了並理解了你要說的話,並通過肢體語言反饋給你他知道了 伺服器收到FIN資料包,並回了一個ACK,伺服器的狀態變為CLOSE-WAIT。客戶端收到ACK之後變為FIN-WAIT-2
你的朋友說“那好吧, 路上注意安全哈” 伺服器向客戶端傳送FIN包,伺服器變為LAST-CHECK。
你說“好的,下次再約” 客戶端收到FIN包後狀態變為TIME-WAIT。並回一個ACK給伺服器。
你遲疑了一下,你的朋友並沒有挽留你 客戶端等待2MSL,如果沒有收到伺服器的重發訊息,則說明伺服器收到了ACK。
你離開了和朋友的聚會 四次揮手結束,連線斷開

2.4 為什麼要等待

MSL,即Maximun Segment LifeTime,報文最大生存時間。為什麼在TIME-WAIT之後還需要等待2MSL呢?主要是兩個原因,讓我們結合例子來理解一下。

保證伺服器收到ACK

假設你說了“好的,下次再約”。由於大家都在high,聲音太大了。導致你的朋友沒有聽到你說的“好的,下次再約”這句話,然後你轉頭就走了。

如果你站在你朋友的角度,肯定會心裡很不爽,好心提醒你,連句道別的話都沒有?

這種情況就是伺服器並沒有收到客戶端收到的ACK,站在伺服器的角度,伺服器並不知道客戶端收到了自己發的FIN包。也就不會斷開連線,但是客戶端已經單方面的斷開連線了。又造成了伺服器的資源浪費,伺服器也無法進入正常的關閉連線狀態。

防止失效的資料包

同樣,你說了”好的, 下次再約“後,你沒有確認你的朋友是否聽到了,扭頭就走。你的朋友也喝多了,此時心裡很不爽,罵了一句傻¥。

這句話剛好被路過、站到了你剛剛站的位置上的哥們接住了,以為在說他,心裡就很不爽,提著拳頭就把你的朋友揍了一頓。

這種情況是指,客戶端沒有等待2MSL就直接斷開,但是伺服器此時仍然有些資料包需要傳送,或者已經發了出去。但是資料包到了後,此時的埠已經被新的連線佔用了,老的TCP報文就會與新連線的TCP報文衝突、混淆。

3. 結尾

後面如果我有時間,會繼續嘗試把枯燥的理論抽象成生活中一些簡單的現象並且與專業的知識結合起來的文章風格,來幫助那些看理論知識很吃力的人。其實只要理解了整個思路,是不需要去死記硬背的。

如果文章中有不對的地方,還望各位大佬不吝賜教。

如果你覺得這篇文章對你有幫助,還麻煩點個贊關個注分個享留個言

也可以微信搜尋公眾號【SH的全棧筆記】,當然也可以直接掃描二維碼關注

拜了個拜

【俗話說】換個角度理解TCP的三次握手和四次揮手

相關文章