1. 程式人生 > >TCP基礎 —— 為什麼建立連線需要三次握手,而斷開連線則需要四次?能不能是三次?

TCP基礎 —— 為什麼建立連線需要三次握手,而斷開連線則需要四次?能不能是三次?

一、TCP包概述

一個 segment 包含 header 和 data 兩個部分,對於這篇文章需要理解的就是,Sequence number 和 Acknowledgement number) 這兩個欄位。TCP 的可靠傳輸就是基於這兩個欄位來實現的。 雖然文章的主旨是三次握手(three-way handshake)與四次揮手(four-way handshake),但不理解 Sequence number 和 Acknowledgement number 就無法真正的理解這兩個過程。

Sequence number

  • 當開始一個 TCP 會話時,此時 SYN 位為 1,會生成一個隨機的 Sequence number,後續使用 Sequence number 則從 Sequence number + 1 開始。
  • 其他時候,則表示為 data 部分第一位的位置,值為此位資料的 Sequence number ,即基於初始 Sequence number + 1 + offset。

其實很好理解,我們先拋開第一次生成的 Sequence number,後續的 TCP 頭中的 Sequence number 都指的是 data 部分第一位的序號。比如:

  • 我這次傳送的 Sequence number 為 100,資料長度為 100,
  • 那麼我下一次傳送的 Sequence number 就應該是 200,再假定資料長度為 50,
  • 如果要進行第三次傳送,那麼 Sequence number 的值應為 250。

下面的圖簡單的表明了傳送兩個連續的 segment 的 Sequence number 變化情況,忽略了TCP頭,TCP頭 並不計入 Length

Acknowledgement number

  • 回覆收到的最大 Sequence number + 1,表示期望收到的 Sequence number 的值。

和上面的其實一樣的道理,比如收到 Sequence number 為 100,資料長度為 100,那麼我們就回復 Acknowledgement number = 200。

二、三次握手過程概述

有了上面的基礎,我們再開始看握手過程,TCP連線三次握手的過程如下,為了方便描述:

  • SEQ_NUM 代表 TCP header 中的 Sequence number
  • ACK_NUM 代表 TCP header 中的 Acknowledgment number
  • DATA_LEN 代表 segment 中 data 的長度
SYN(1) ACK(0) SEQ_NUM(0) ACK_NUM(0) DATA_LEN(0)    ====>
                                                   <====  SYN(1) ACK(1) SEQ_NUM(100) ACK_NUM(1) DATA_LEN(0)
SYN(0) ACK(1) SEQ_NUM(1) ACK_NUM(101) DATA_LEN(0)  ====>
  • 連線發起方將 SYN 位設定為1,然後隨機生成一個 SEQ_NUM_A,傳送給被髮起方。
  • 被髮起方回覆 ACK(1) ACK_NUM(SEQ_NUM_A+1),同時也需將 SYN 位設定為1,然後自己也隨機生成一個 SEQ_NUM_B。
  • 連線發起方收到上個 segment 後,回覆 ACK(1) ACK_NUM(SEQ_NUM_B+1),當被髮起方收到這個 segment 後,連線建立成功。

三、可能存在的疑問

1、為什麼要強調 Sequence number 和 Acknowledgement number?

假設同樣是三次握手,但是很簡單:

SYN(1) ACK(0)  ====>
               <====  SYN(1) ACK(1)
SYN(0) ACK(1)  ====>

看起來沒問題,但實際上由於網路傳輸是不可靠的,如果沒有 Sequence number 我們無法保證此時收到的 segment 的順序性,也無法得知是否丟失了某個 segment。同樣是一個 SYN(1) ACK(1) segment,它有可能是上一次建立連線時被髮送方誤認為已經丟失的 segment,甚至更特殊的情況。

在資料傳輸的過程中也是如此,被重發的 segment,丟失的 segment,連續傳送的 100 個 segment 如果沒有 Sequence number 作為保障,他們到達(或者到達不了)接收方的排列組合方式可能千奇百怪。

2、那麼在 Sequence number 和 Acknowledgement number 的保障下,如何保證自己的訊息被對方收到呢?

這個其實很好理解,實際上就是自己傳送出去的這部分 Sequence number 被 ack 了即可,即:一去一回(實際上可以多去一回,或者可能一去多回,但這裡只說最簡單的情況)。

SEQ_NUM(100) DATA_LEN(100)    ====>
                              <====   ACK(200)

如果沒有收到對方的 ack,或者收到的 ack 非此 segment 的 ack,則代表對方沒有收到自己的訊息,比如下面這個例子,此時我們能得知,對方沒有收到或者暫時還沒收到我們傳送過去的第二個 segment。

此時也可以區分出收到的 segment 是否屬於本次連線,因為在建立連線後我們會生成一個新的 Sequence number。

SEQ_NUM(100) DATA_LEN(100)    ====>
                              <====   ACK(200)
SEQ_NUM(200) DATA_LEN(100)    ====>
                              <====   ACK(200)

3、這就很好理解 TCP 連線握手為什麼是三次了。

  • 發起連線方發出SYN,並收到ACK,這就是兩次網路傳輸了。
  • 同樣被連線方也發出SYN,且等待對方回覆,這也是兩次網路傳輸。

加起來難道不是四次嗎?實際上被連線方將對連線方 SYN(1) 的回覆和自己 SYN(1) 的請求合併了,所以建立一個 TCP 連線最少只需要經過三次網路傳輸。

4、那為什麼 TCP 斷開連線需要四次,而不是三次?

  • 發起斷開方發出FIN,並收到ACK,這就是兩次網路傳輸了。
  • 同樣被斷開方也發出FIN,且等待對方回覆,這也是兩次網路傳輸。

同樣的邏輯分析下來,實際上也可以僅經過三次傳輸就斷開此次連線,但為什麼我們會說四次揮手呢?這是因為如果在收到FIN時,彼時還有資料未傳輸完,則先回復關於 FIN 的 ACK,告知對方我已經知道你要斷開了。則等待傳輸完畢後,被斷開方再發送 FIN,告知自己也已經可以斷開連線。

但實際上完全可以是“三次揮手”,如果收到 FIN 時,已經沒有資料要傳輸,則是