文源網絡,僅供學習之用,若有侵權請聯繫刪除。
關於TCP的文章,網上有不少,講「三次握手」和「四次揮手」的文章。那些文章都很好的詮釋了其原理,可是有些話語都乾澀難懂,本身得反覆的揣摩!java
正好,今天在與朋友交流的時候,又看到有人在問TCP「三次握手」的問題:程序員
其實,已經知道朋友確定就能很快理解這句話的意思,可是還在摸索中的朋友呢?面試
下面未來講講,這TCP的「三次握手」和「四次揮手」究竟是怎麼一回事。spring
在好久好久之前.....設計模式
面試官:說說什麼是TCP?網絡
程序員:傳輸協議數據結構
面試官:UDP也是,那爲何不用UDP呢?框架
程序員:由於UDP把數據傳輸過去就無論了,就假定接收方必定能接收到,惋惜真實的網絡環境不會如此單純,甚至是複雜、殘酷的。學習
在網絡環境中無時無刻出現着丟包、阻塞、亂序的狀況,一個不當心,數據可能就傳輸不到目的地了,這個時候就只能輪到TCP了spa
面試官:爲何TCP就可靠了呢?
程序員:就是由於它的「三次握手」和「四次揮手」哇!
面試官(邪魅一笑):那你來說講吧!
程序員:那您且細聽分說
這個「三次握手」就是通訊創建連接的一個過程,看下圖
通俗點講就是:
客戶端:你好,我是客戶端
服務端:你好,客戶端,我是服務端
客戶端:你好,服務端
也常常把這個過程叫作「請求->響應->響應的響應」
面試官:打斷一下,問個問題,爲何不是兩次或者四次,非得是三次呢?
程序員:嗯,不錯,是個稱職的面試官!
試想一下,客戶端發送一個鏈接,因爲丟包、超時,或是服務端根本就不想創建鏈接,那這個時候客戶端怎麼辦?
客戶端並不能知道結果,就只有重複的發送鏈接請求,雖然這個時候服務端接收到了,但客戶端仍不知道這事,只能繼續發請求。
此時此刻,服務端接收了客戶端的請求,固然知道了客戶端的存在,若是服務端不一樣意創建鏈接,客戶端重試幾回後就會放棄,沒問題。
若是服務端願意創建鏈接呢?這個時候就會發送響應給客戶端(好了,你別叫喚了,客戶端我知道你了)
到了這一步,就已經進行了兩次握手!理論上,這個時候已經能夠進行鏈接了,可是問題來了:
由於對於服務端來講,這個響應的過程也可能會丟包,不必定會達到客戶端,甚至客戶端徹底掛了都是可能的。
對於另外的狀況,在第一次握手的時候,因爲網絡問題,客戶端向服務端發送了多個創建鏈接的請求,假設其中某一個創建成功,作了簡單的通訊,而後結束了鏈接,原本很正常,可是另一些請求因爲網絡的延遲又到了服務端,服務端會認爲這是一個正常的請求,接下來又創建鏈接。
所以兩次握手會有着種種問題。
爲了保證服務端的響應能正確接收,客戶端仍是須要告訴給服務端,我收到了服務端的響應,也就是給服務端一個「響應的響應」,這樣就是三次握手了。
面試官:要是這個「響應的響應」要是也出現了丟包等問題沒有送到呢?
程序員:你存心找麻煩的吧?那照你這樣說你就是手握爛了,也沒辦法保證消息100%可靠。三次握手,保證服務端、客戶端雙方都確認了對方存在就足夠了。
面試官:嗯,不錯,來講說「四次揮手」又是什麼吧!
程序員:創建完鏈接後就能夠傳輸數據了,傳輸完成後則須要四次揮手來告別,過程看下圖
面試官:這你TMD沒有上一張清晰啊!
程序員:哎呀~面試時間有限,將就着看看啦!
通俗來說就是:
客戶端:服務端啊,我要和你拜拜了
服務端:好的,我知道了
這個時候,客戶端說了拜拜,也不會向服務端發送數據了,這個時候服務端是否能立刻關閉呢?
不能夠!由於服務端可能還沒處理完相應的事情,因此仍是要發送數據的,這個時候的狀態爲等待關閉的狀態,當處理完後須要繼續下面步驟。
服務端:客戶端,我也要和你說拜拜了
客戶端:拜拜,滾吧
其實在這裏也會存在一些問題,當客戶端說「拜拜」,服務端說「知道了」,這兩次是沒什麼問題的。
可是當客戶端說「拜拜」以後就直接奪門而出,這樣就出問題了,由於服務端還沒說「知道了」,就算已經說了「知道了」,客戶端也可能已經走了。
這個時候爲了解決這些問題,TCP協議還有幾個狀態來處理這些問題,就如上面看的狀態圖。
當客戶端說「拜拜」,就進入 FIN_WAIT_1 的狀態,服務端收到「拜拜」的消息後,發送「知道了」,而後就進入 CLOSE_WAIT 的狀態。
當客戶端接收到「知道了」,進入FIN_WAIT_2,過一段時間,客戶端也接收到了「客戶端,我也要和你說拜拜了」,客戶端就應該發送「拜拜,滾吧」,說完這句話已經就能夠走了。
可是客戶端發送的消息沒發送成功,服務端只能重複發送「我也要和你說拜拜」,惋惜的客戶端已經走了,永遠不會給你響應。
因爲存在這種問題,TCP協議要求客戶端在離開以前先等待一會會,這個等待的時間叫TIME_WAIT,這個TIME_WAIT還有另一個做用,若是客戶端說完「拜拜」以後還須要等待一段時間,若是不等待就會直接釋放端口。
在前面一次鏈接客戶端說完「拜拜」就走了,服務端不知道,不停的回覆「知道了」,這時候一個新的客戶端鏈接了,這新客戶端接收到了「知道了」這是否是就特別混亂了?
因此這TIME_WAIT就特別重要了,它能夠保證讓遲來的TCP報文段有足夠的時間被識別和丟棄。鏈接結束了,網絡中的延遲報文也應該被丟棄掉,以避免影響馬上創建的新鏈接。
我將面試題和答案都整理成了PDF文檔,還有一套學習資料,涵蓋Java虛擬機、spring框架、Java線程、數據結構、設計模式等等,但不只限於此。關注公衆號【java圈子】獲取資料,還有優質文章每日送達。