換個角度理解TCP的三次握手和四次揮手

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嗎

。。。。。。(無應答)

你的內心:??????

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

換個角度理解TCP的三次握手和四次揮手

0341fda0555dc92b70a2ea4874115d5b

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

1.2 三次握手的過程

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

  • 服務器開始監聽某個端口,此時服務器進入了LISTEN狀態
  • 客戶端最初是CLOSED狀態,然後向服務器發送一個SYN標誌位的數據包,主動發起連接。客戶端變成SYN-SENT狀態
  • 服務器接收到客戶端的SYN數據包,通過標誌位知道了客戶端想要建立連接。於是回了客戶端一個SYN和ACK,表示收到了請求。服務器的自身狀態變為了SYN-RCVD
  • 客戶端收到了服務器的ACK,表示服務器知道了客戶端想要建立連接。然後客戶端再給服務器回了一個ACK表示自己收到了(或者說能夠收到)服務器的消息,發送完這個ACK後,客戶端的狀態變成了ESTABLISH
  • 服務器收到了客戶端的ACK,服務器的狀態也變成ESTABLISH
換個角度理解TCP的三次握手和四次揮手

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 對比分析

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


換個角度理解TCP的三次握手和四次揮手

2.4 為什麼要等待

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

保證服務器收到ACK

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

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

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

防止失效的數據包

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

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

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

3. 結尾

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

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


作者:SH的全棧筆記
鏈接:https://juejin.im/post/5e5e33e4f265da573c0c8114


分享到:


相關文章: