本文學習內容來源於掘金小冊《深入理解TCP協議》與圖書《TCP/IP詳解卷I》,有興趣的朋友可以購買相關小冊進行更細緻的學習
一:前情概述
TCP是可靠的、面向連接的、基於字節流的、雙全工協議,耳熟能詳的一句話概括了TCP協議所有重要的特性。面向連接的特性一定程度上保證了TCP協議的可靠性,橫向對比同是傳輸層的無連接UDP協議。所以本文將講述TCP協議進行數據傳輸通信前建立邏輯通道的故事,三次握手與四次揮手
二:認識協議頭
應用層報文到傳輸層時,傳輸層會爲其添加報文頭,學習協議的開始應該由頭開始。從協議頭開始抽絲剝繭認識該協議,當然,本文講述TCP協議的三次握手與四次揮手過程,所以只會列舉與本文內容相關的字段
- Source Port : 2字節、源端口(wireshark過濾表達式tpc.port就是這個原因)
- Destination Port : 2字節、目標端口
- Sequence number : 4字節、序列號(描述數據包開始座標)
- Acknowledgment number : 4字節、確認碼(描述確認數據包序列號 + 1,即下次數據包傳送序列號開始座標)
- Flags : 2字節、數據包類型標籤,主要有如下表幾種值
序號 | 標籤種類 | 標籤含義 | 備註 |
---|---|---|---|
1 | SYN | 創建連接數據包 | 三次握手時傳遞 |
2 | ACK | 確認數據包 | 數據接收方收到數據包後確認通信的數據包 |
3 | FIN | 斷開數據包 | 四次揮手時傳遞 |
4 | RST | 強制斷開 | 某些不合法操作時強制斷開連接返回數據包 |
5 | PSH | 傳輸層別緩存,立即將數據交給應用層 |
三:三次握手
開局一張圖,故事全靠編。三次握手簡而言之就是TCP爲了建立邏輯連接,客戶端與服務端進行三次通信的故事。中間過程伴隨着信息交換、狀態變更等過程
3.1 信息交換
第一次握手、第一次應答時協議頭中的Seq字段都是爲了告訴連接對方數據包傳輸初始序列號。通過wireshark抓包截圖可以看到都是0,很多文章都說這裏是0開始。那麼事情的真相到底是什麼情況
通過tcpdump監控抓包顯示三次握手過程中交換的seq信息並不是0開始,並且抓包過程重複幾次你發現每次seq值都不一致。難道wireshark抓包信息有誤?並不是,seq初始值的生成算法會根據時間變化計算取得
,wireshark中爲了方便查看相對計算爲0、1進行表達。若想恢復算法值如下所示:Edit(編輯E) – Preferences(首選項P) – Protocols – TCP
當然爲什麼會採用這樣的算法計算得出這樣一個數值,每次從一個固定的序列號開始不好麼?這個問題後面章節詳細討論。至於ACK就顯得很簡單了,每次收到數據包後進行相對應的確認,數據傳輸方可以根據是否確認決定是否進行重傳等操作。ACK數值一定是seq + 1,前面講了ACK還表示下次數據傳輸開始序列號
信息的交換當然不僅僅是初始化序列化交換這麼簡單,如最大段大小(MSS)、窗口大小(Win)、窗口縮放因子(WS)、是否支持選擇確認(SACK_PERM)也會在握手過程中交互。但是本文主要目的爲講解三次握手過程,所以相關內容留待後續解決
3.2 狀態變更
創建連接的過程中必然伴隨着狀態的變更,不然鬼知道到了哪一步該執行什麼樣的操作。簡要步驟描述如下所示:
- 雙方處於
CLOSED
關閉狀態,然後服務端啓動應用將某端口變更爲LISTEN
監聽狀態 - 客戶端第一次握手發送SYN包,狀態調整爲
SYN-SENT
- 服務端接收到SYN請求包,進行SYN+ACK操作。狀態變更爲
SYN-RCVD
- 客戶端收到服務端的請求確認後進行ACK響應操作,狀態變更爲
ESTABLISHED
就緒 - 服務端接收到客戶端的ACK確認包狀態變更爲
ESTABLISHED
就緒,雙方連接建立成功
四:握手詳解
三次握手的過程通過第三章已經基本清楚,但是也帶來了新的問題:
- 三次握手是否可以修改爲兩次
- 初始序列號是否可以固定值
4.1 可否兩次握手
對於爲什麼選擇三次握手這個數字,通過前面的內容說實話不會回答的很好。這個答案結合後續的知識在這裏作答個人理解如下:
- 除去第二次握手交互,即服務端到客戶端的SYN + ACK。那麼客戶端將不會知道服務端是否收到連接SYN請求,也就無法進行超時重傳。當然如果這時候直接進行正式數據包的傳輸,服務端無法接收同時也浪費客戶端資源
- 除去第三次握手交互,服務端不清楚客戶端是否確認準備就緒,佔用服務端資源。連接隊列裏的連接將一直得不到利用以及釋放,新連接將因爲溢出而無法連接,整個應用崩潰無法提供服務
4.2 序列號固定
TCP連接四元組爲源IP、源端口、目的IP、目的端口,即初始序列號唯一固定也不會影響TCP連接。但是注意一點,獲取應用IP與端口是十分簡單的操作,當知道所有信息之後模擬RST包(強制關閉)將十分容易,連接安全也就沒法進行保證
同時,如果啓用了SO_REUSEADDR(端口複用)。很容易導致新連接與舊連接數據包串包,誰也沒法保證接收到的數據包是否因爲網絡原因而延遲的舊連接數據包
五:四次揮手
四次揮手相對於三次握手多了兩個關鍵的節點,這兩個關鍵的節點將是本章學習重點。添加這兩個過程狀態的原因是什麼?中間都發生了什麼故事?
5.1 兩次回覆
前面接觸三次握手的時候服務端接收到客戶端發送的SYN包後將會返回SYN+ACK,即同時進行確認與連接。但是,四次揮手過程示意圖中這個步驟分解爲兩步。首先進行ACK確認,然後再進行FIN關閉。
原因如下:
當客戶端發送FIN包後會啓動定時器,當定時器時間到達時會重發FIN包。但是這時候服務端有可能數據並未傳輸完畢,如果等待數據傳輸完畢以後再統一返回FIN+ACK可能導致客戶端多次發送FIN。所以四次揮手過程中將先對客戶端FIN進行確認,等待數據傳輸完畢時再執行FIN
5.2 TIME-WAIT
比較神奇的一個狀態,主動斷開連接的一方將進入該狀態。問題來了,既然都已經斷開連接了爲什麼還要藕斷絲連,何不果斷點?
- 一方面因爲網絡延遲,需要等待連接所有數據包失效
- 一方面因爲需要處理最後ACK丟包情況的發生
TCP協議頭中存在字段MSL(Max Segment Lifetime)即數據包生存最大時長,這個最大時長就是數據包在網絡中可以存在的最大時間。如果老連接不等待MSL時間,那麼新的連接就可能接收到因爲網絡延遲原因抵達的老連接數據包。這樣的數據包新連接無法進行正確的處理。2MSL原因是最大程度計算保證來回的時長,下圖複製來源於張師傅掘金小冊內容,學習相關內容請購買小冊進行學習
因爲網絡原因,服務端發出FIN後並未收到客戶端確認的ACK。將會導致服務端處於LAST-ACK狀態,定時器到達時間後重發FIN包,若這時客戶端已經關閉,將會導致服務端持續重發FIN包,直到最大次數限制,這將會消耗服務端資源。2MSL時長能保證服務端的FIN重發包到達客戶端,客戶端重新進行ACK
六:分手詳解
對於TCP四次揮手談及最多的內容當屬是否可以三次揮手?爲什麼要等待兩個MSL時間?第二個問題在前面已經有了答案,第一個問題解釋如下:
大清亡了?爲嘛FIN和ACK搞一塊去了?任何結論都需要實踐的基礎,wireshark抓包的結果告訴我們,如果當服務端沒有數據進行傳輸時,是可以將FIN與ACK在同一個包中傳輸,即三次揮手既視感。這與三次握手異曲同工,所以三次握手如果開心也可以設計爲四次握手,將SYN與ACK拆開,當然這樣的操作必然代碼性能的消耗