點選上方“芋道原始碼”,選擇“置頂公眾號”
技術文章第一時間送達!
原始碼精品專欄
摘要: 原創出處 https://juejin.im/post/5b29d2c4e51d4558b80b1d8c 「老錢」歡迎轉載,保留摘要,謝謝!
-
TCP 三次握手
-
TCP 資料傳輸
-
TCP 四次揮手
-
總結
TCP三次握手和四次揮手的問題在面試中是最為常見的考點之一。很多讀者都知道三次和四次,但是如果問深入一點,他們往往都無法作出準確回答。
本篇嘗試使用動畫來對這個知識點進行講解,期望讀者們可以更加簡單地地理解TCP互動的本質。
TCP 三次握手
TCP 三次握手就好比兩個人在街上隔著50米看見了對方,但是因為霧霾等原因不能100%確認,所以要透過招手的方式相互確定對方是否認識自己。
張三首先向李四招手(syn),李四看到張三向自己招手後,向對方點了點頭擠出了一個微笑(ack)。張三看到李四微笑後確認了李四成功辨認出了自己(進入estalished狀態)。
但是李四還有點狐疑,向四周看了一看,有沒有可能張三是在看別人呢,他也需要確認一下。所以李四也向張三招了招手(syn),張三看到李四向自己招手後知道對方是在尋求自己的確認,於是也點了點頭擠出了微笑(ack),李四看到對方的微笑後確認了張三就是在向自己打招呼(進入established狀態)。
於是兩人加快步伐,走到了一起,相互擁抱。
我們看到這個過程中一共是四個動作,張三招手–李四點頭微笑–李四招手–張三點頭微笑。其中李四連續進行了2個動作,先是點頭微笑(回覆對方),然後再次招手(尋求確認),實際上可以將這兩個動作合一,招手的同時點頭和微笑(syn+ack)。於是四個動作就簡化成了三個動作,張三招手–李四點頭微笑並招手–張三點頭微笑。這就是三次握手的本質,中間的一次動作是兩個動作的合併。
我們看到有兩個中間狀態,syn_sent和syn_rcvd,這兩個狀態叫著「半開啟」狀態,就是向對方招手了,但是還沒來得及看到對方的點頭微笑。syn_sent是主動開啟方的「半開啟」狀態,syn_rcvd是被動開啟方的「半開啟」狀態。客戶端是主動開啟方,伺服器是被動開啟方。
-
syn_sent: syn package has been sent
-
syn_rcvd: syn package has been received
TCP 資料傳輸
TCP 資料傳輸就是兩個人隔空對話,差了一點距離,所以需要對方反覆確認聽見了自己的話。
張三喊了一句話(data),李四聽見了之後要向張三回覆自己聽見了(ack)。
如果張三喊了一句,半天沒聽到李四回覆,張三就認為自己的話被大風吹走了,李四沒聽見,所以需要重新喊話,這就是tcp重傳。
也有可能是李四聽到了張三的話,但是李四向張三的回覆被大風吹走了,以至於張三沒聽見李四的回覆。張三並不能判斷究竟是自己的話被大風吹走了還是李四的回覆被大風吹走了,張三也不用管,重傳一下就是。
既然會重傳,李四就有可能同一句話聽見了兩次,這就是「去重」。「重傳」和「去重」工作作業系統的網路核心模組都已經幫我們處理好了,使用者層是不用關心的。
張三可以向李四喊話,同樣李四也可以向張三喊話,因為tcp連結是「雙工的」,雙方都可以主動發起資料傳輸。不過無論是哪方喊話,都需要收到對方的確認才能認為對方收到了自己的喊話。
張三可能是個高射炮,一說連說了八句話,這時候李四可以不用一句一句回覆,而是連續聽了這八句話之後,一起向對方回覆說前面你說的八句話我都聽見了,這就是批次ack。但是張三也不能一次性說了太多話,李四的腦子短時間可能無法消化太多,兩人之間需要有協商好的合適的傳送和接受速率,這個就是「TCP視窗大小」。
網路環境的資料互動同人類之間的對話還要複雜一些,它存在資料包亂序的現象。同一個來源發出來的不同資料包在「網際路由」上可能會走過不同的路徑,最終達到同一個地方時,順序就不一樣了。作業系統的網路核心模組會負責對資料包進行排序,到使用者層時順序就已經完全一致了。
TCP 四次揮手
TCP斷開連結的過程和建立連結的過程比較類似,只不過中間的兩部並不總是會合成一步走,所以它分成了4個動作,張三揮手(fin)——李四傷感地微笑(ack)——李四揮手(fin)——張三傷感地微笑(ack)。
之所以中間的兩個動作沒有合併,是因為tcp存在「半關閉」狀態,也就是單向關閉。張三已經揮了手,可是人還沒有走,只是不再說話,但是耳朵還是可以繼續聽,李四呢繼續喊話。等待李四累了,也不再說話了,朝張三揮了揮手,張三傷感地微笑了一下,才徹底結束了。
上面有一個非常特殊的狀態time_wait
,它是主動關閉的一方在回覆完對方的揮手後進入的一個長期狀態,這個狀態標準的持續時間是4分鐘,4分鐘後才會進入到closed狀態,釋放套接字資源。不過在具體實現上這個時間是可以調整的。
它就好比主動分手方要承擔的責任,是你提出的要分手,你得付出代價。這個後果就是持續4分鐘的time_wait
狀態,不能釋放套接字資源(埠),就好比守寡期,這段時間內套接字資源(埠)不得回收利用。
它的作用是重傳最後一個ack報文,確保對方可以收到。因為如果對方沒有收到ack的話,會重傳fin報文,處於time_wait狀態的套接字會立即向對方重發ack報文。
同時在這段時間內,該連結在對話期間於網際路由上產生的殘留報文(因為路徑過於崎嶇,資料報文走的時間太長,重傳的報文都收到了,原始報文還在路上)傳過來時,都會被立即丟棄掉。4分鐘的時間足以使得這些殘留報文徹底消逝。不然當新的埠被重覆利用時,這些殘留報文可能會幹擾新的連結。
4分鐘就是2個MSL,每個MSL是2分鐘。MSL就是maximium segment lifetime
——最長報文壽命。這個時間是由官方RFC協議規定的。至於為什麼是2個MSL而不是1個MSL,我還沒有看到一個非常滿意的解釋。
四次揮手也並不總是四次揮手,中間的兩個動作有時候是可以合併一起進行的,這個時候就成了三次揮手,主動關閉方就會從fin_wait_1
狀態直接進入到time_wait
狀態,跳過了fin_wait_2
狀態。
總結
TCP狀態轉換是一個非常複雜的過程,本文僅對一些簡單的基礎知識點進行了類比講解。關於TCP的更多知識還需要讀者去搜尋相關技術文章進入深入學習。如果讀者對TCP的基礎知識掌握得比較牢固,高階的知識理解起來就不會太過於吃力。
如果你對 Dubbo 感興趣,歡迎加入我的知識星球一起交流。
目前在知識星球(https://t.zsxq.com/2VbiaEu)更新瞭如下 Dubbo 原始碼解析如下:
01. 除錯環境搭建
02. 專案結構一覽
03. 配置 Configuration
04. 核心流程一覽
05. 拓展機制 SPI
06. 執行緒池
07. 服務暴露 Export
08. 服務取用 Refer
09. 註冊中心 Registry
10. 動態編譯 Compile
11. 動態代理 Proxy
12. 服務呼叫 Invoke
13. 呼叫特性
14. 過濾器 Filter
15. NIO 伺服器
16. P2P 伺服器
17. HTTP 伺服器
18. 序列化 Serialization
19. 叢集容錯 Cluster
20. 優雅停機
21. 日誌適配
22. 狀態檢查
23. 監控中心 Monitor
24. 管理中心 Admin
25. 運維命令 QOS
26. 鏈路追蹤 Tracing
…
一共 60 篇++