一:摘要概述
系列第一文TCP(一) – 初識TCP中描述了TCP是一個面向連接的傳輸層協議,這也是TCP協議保證可靠性的重要一環。客戶端與服務端建立連接的方式就是通過三次握手,三次握手的過程中將會交換大量數據信息。本文的目的就是詳細解釋TCP三次握手的過程、狀態變更以及交換的初始數據信息
二:協議標誌
第一篇文章中提到TCP協議頭有一個Flags標誌,標誌中的信息代表了數據包的類型。三次握手的過程中就會使用到SYN、ACK包,具體常用的標誌含義如下表所示:
序號 | 標籤種類 | 標籤含義 | 備註 |
---|---|---|---|
1 | SYN | 創建連接數據包 | 三次握手時傳遞 |
2 | ACK | 確認數據包 | 數據接收方收到數據包後確認通信的數據包 |
3 | FIN | 斷開數據包 | 四次揮手時傳遞 |
4 | RST | 強制斷開 | 某些不合法操作時強制斷開連接返回數據包 |
5 | PSH | 傳輸層別緩存,立即將數據交給應用層 |
三:連接過程
- 客戶端發送SYN包到服務端,Hello兄弟我想創建一個連接
- 服務端收到客戶端SYN包回覆SYN+ACK,兄弟我收到連接請求可以連接
- 客戶端回覆服務端ACK包,好的,連接創建成功接下來將發送數據
四:狀態變更
如第三節示例圖所示:
- 服務端啓動應用監聽某個端口時就會處於LISTEN狀態
- 客戶端發起連接發送SYN包客戶端處於SYN-SENT狀態
- 服務端收到SYN包回覆SYN+ACK後處於SYN-RECV狀態
- 客戶端收到SYN+ACK回覆ACK後處於ESTAB狀態
- 服務端收到ACK包後處於ESTAB狀態
五: Sequence Number
連接建立的過程中可以看到有0、1信息的交換,這個信息到底是什麼?有什麼作用?
5.1 作用詳解
TCP協議是一個可靠的協議,如果數據包丟了那麼會進行重傳嘗試,但是數據包有很多,發送方怎麼確認哪一段數據包丟失?依賴的就是Sequence Number,可以理解爲該屬性就是數據包的編碼,方便TCP協議管理數據
5.2 初始值定義
很多資料上亦或是本文上述繪製的過程圖中都將Sequence Number的初始值定義爲0,現實中的該初始值真爲0麼?必然這個概念是錯誤的,也就是爲了方便描述計數,所以將其初始值定義0。WireShark抓包工具中顯示0是因爲其自動幫我們做了處理,可以通過設置:Edit(編輯E) – Preferences(首選項P) – Protocols – TCP恢復原始值顯示
實際上Sequence Number的初始值是一個隨時間遞增的數值,有着自己的生成算法。使用tcpdump抓包顯示結果如下:
5.3 固定初始值
這時候就會有人問爲什麼Sequence Numer的初始值不能設置爲一個固定值?有以下兩個原因值得深思:
- TCP連接四元組件:src host/port + dst host/port,這些連接信息十分容易獲取,如果固定初始值就使得第三方十分容易構造一個在窗口允許範圍內的RST數據包關閉連接,這樣導致的後果可想而知
- 後面會講到SO_REUSEADDR參數,端口複用的情況下如果Sequence Number固定值開始,那麼有可能造成新舊連接的數據包一致,就會導致服務端無法判斷這個數據包到底是什麼時候的數據包
六:ACK確認碼
三次握手創建連接時候發現每次對方發送SYN包後都需要ACK進行確認,不僅如此,後續包括數據傳輸、四次揮手過程都需要ACK確認,這樣才能保證數據接收方收到了傳輸的數據。ACK確認的值到底是多少?其實也比較簡單,ACK 數值 = Sequence Number + 數據包大小,表示這個範圍內的數據已經接收到,下次傳遞數據的時候請使用ACK數值作爲你的Sequence Numer傳遞
七:三次握手重試
三次握手的過程其實就是客戶端與服務端三次數據包傳遞交互的過程,既然涉及到數據包的傳遞那麼就有可能因爲網絡波動等原因導致的數據包丟失。這時候發送數據包的一方就會根據情況嘗試重試
7.1 SYN重試
天有不測風雲,人有禍兮旦福。網絡波動導致客戶端發送的SYN包服務端未收到從而沒有回覆SYN+ACK,那麼這時候客戶端會如何處理?明顯客戶端會進行重試,也就是當客戶端在一定時間內未收到服務端的ACK確認那麼就會重新發送SYN包,示例如下
重試多少次?這個數值由服務器的tcp_syn_retries
決定,查看該數值命令如下:
[root@bogon ~]# cat /proc/sys/net/ipv4/tcp_syn_retries
packetdrill構建SYN_SENT狀態連接使用如下腳本:
// 新建一個 server socket
+0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
// 客戶端 connect
+0 connect(3, ..., ...) = -1
7.2 SYN + ACK重試
服務端接收到客戶端傳送的SYN包之後會回覆SYN+ACK,這時候如果該數據包丟失的話客戶端不會回覆ACK,自然當時限達到的情況下服務端需要重新發送SYN + ACK。重試的次數由參數tcp_synack_retries
空值,查看該數值命令如下:
[root@bogon ~]# cat /proc/sys/net/ipv4/tcp_synack_retries
packetdrill構建SYN_RECV狀態連接使用如下腳本:
+0 < S 0:0(0) win 65535 <mss 100>
+0 > S. 0:0(0) ack 1 <...>