初始狀態時,TCP A 處于連接關閉狀態, TCP B 處于監聽狀態. 也就是通常所說的 A 時 TCP 客戶段,B 是服務端.
A 發送 SYN 給 B, 并附有 SEQ, 請求建立 TCP 連接。 A 發送 SYN 后,狀態切換為 SYN-SENT, B 接收到 A 發送的 SYN 后狀態切換為 SYN-RECEIVED.
B 收到 A 的 SYN 之后,發送它的 SYN(注意,這里 A 和 B 的 SEQ 是相互獨立的 ),并附上 ACK 標記用以表明 B 收到了 A 的 SYN 包。 這里注意: B 發送的 ACK 的值為 101,它代表 B 收到了序列號為 100和100之前的所有字節數據,并告訴 A 自己期待下一次收到序列號以101開始的數據. A 在接收到 B 的 SYN 之后,狀態轉化為 ESTABLISHED.
A 收到 B 的 SYN 之后, 需要發送 ACK 給 B 告訴 B 自己收到了它的 SYN + ACK 包. 這里注意:A 發送的ACK的值為 301, 原因是 B 的 SYN 中的 SEQ 是 300. A 發送的 SEQ 是 101,原因是上一次的請求中序列號已經增長到了 100. 下一個可用的序列號就是 101. 在B接收到 A 的 ACK 之后,它的狀態切換為 ESTABLSHED. 至此,三次握手已經完成,一個 TCP 連接已經成功建立。
在這條 TCP 連接上可以進行數據傳輸.
在了解了基本的流程之后,我們來使用 wireshark 包應用以下所學:
如下圖,忽略其中的黑色記錄,一共四條記錄,對應于上圖中的 2-5.
接下來,我們詳細看看每條記錄
客戶端 SYN
這是建立 TCP 連接三次握手中的第一次。
這張圖涵蓋的信息很多,全部字段的含義在前文中已經描述過,這里我們僅僅關注個別字段.
Flags 字段中 SYN 標記為 1. 表明當前 TCP 包是一個 SYN 包. 首先發送這個數據包的 TCP 段為請求建立 TCP 連接的端點.
Sequence Number 字段的值為 2292773402. Wireshark 為了方便我們查看,引入另外一個字段 relative sequence number. 這個字段的值是基于 initial sequence number 計算所得. 正如前文 TCP 基礎中所說,當當前 TCP 包是一個 SYN 包時,Sequence number 就是 Initial Sequence Number, 因此 這里 relative sequence number 的值是 0.
服務端 SYN + ACK
這是建立 TCP 連接三次握手中的第二次。
Flags 字段中 SYN 和 ACK 字段均為 1
Sequence Number 字段的值為 4127119125 (前面說過,客戶端的 SEQ 和 服務端的 SEQ 是獨立的,他們之間沒有聯系), Relative Sequence Number 為 0.
Acknowledge Number 字段的值為 2292773403. 客戶段發送的 SYN 中 的 SEQ 值是 2292773402. 序列號 2292773403 告訴客戶端 2292773403以及之前的所有數據已經收到。 在接收到這個響應之后, 客戶端便可以確信服務段收到了自己發送的 SYN 包.
客戶端 ACK
這是建立 TCP 連接三次握手中的第三次。
Flags 字段 ACK 為 1
Sequence Number 的值為 2292773403. 這個值于服務端發送給我們 SYN+ACK 包中的 ACK 值相同.
Acknowledge Number 的值為 4127119126. 這里需要注意服務第發送的 SYN+ACK 包中的 SEQ 的值為 4127119125.
B 收到 A 發送的 SYN+ACK。 至此,雙方均進入 ESTABLISHED 狀態,一個連接已然建立成功.
特殊情況: 舊的重復的 SYN
TCP 協議的設計為三次握手的一個很重要原因就是處理舊的重復的SYN包
RFC793 原文: The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion
這里我們就來看看它是如何處理的。
看下圖的 TCP 包流程:
初始狀態下, A 為客戶端,處于 CLOSED 狀態。 B 為服務端,處于LISTEN狀態
某一時刻,A 發送 SYN 給 B,請求建立 TCP 連接
B 在收到 A 剛剛發送的 SYN 包之前, 收到了一個舊的重復的來自 A 的 SYN.
對于 B 段來說,在收到來自 A 的 SYN 包時,它是不知道那是一個舊的重復的 SYN 包的,因此它就想就受到一個普通的 SYN一樣響應這個 SYN 包. 發送 SYN+ACK 進行確認.
A 端收到來自 B 段的 SYN+ACK, 發現其中的 ACK 字段的值不正確. 因為自己發送的 SYN 中 SEQ 值為 100,響應SYN+ACK包中的 ACK的值應該為 101,但是它收到的 SYN+ACK包中的ACK的值卻是91. 在收到這個非法的SYN+ACK之后,A 段發送 RST,并附上錯誤的SEQ序列號. B 端收到A發送過來的 RST 之后,便得知 A 重置了上一個SYN想要建立的TCP連接,B 段重置所有關于為上一個 SYN 所記錄的狀態,并重新回到 LISTEN 狀態.
對于 TCP 協議來說,數據是不會丟失的,也就是說 B 段遲早會收到 A 發送的 SYN(SEQ=100). B 端收到 A 的 RST 之后,狀態切換到了 LISTEN之后, A 發送 SYN(SEQ=100)到達,此時B依然回像通常情況一樣. 如果 SYN(SEQ=100)到達 B 段早于 A 發送的 RST,將會是一個更復雜的情況,涉及到雙發均發送 RST 的場景. 這里能力有限,略掉.