三次握手的誤解與錯誤類比 (RFC 解讀)

來自個人公衆號 『 YongHao 寫東西的 Cache 』 打個小廣告,仍是但願寫的東西有人看🙊面試

分享一下看法,權當拋磚引玉bash

三次握手的誤解與錯誤類比(RFC解讀)

關於TCP三次握手幾乎是應屆畢業生面試常見的問題了,然而網上還不少比比皆是的錯誤,以知乎 TCP 爲何是三次握手,而不是兩次或四次? 上的熱門答案爲例子,第一個3.6K 次贊同的類比就是錯誤的:網絡

三次握手:
「喂,你聽獲得嗎?」
「我聽獲得呀,你聽獲得我嗎?」
「我能聽到你,今天 balabala……」
複製代碼

一樣這個107次贊同的類比也是錯誤的:app

握手和敬軍禮同樣,源自「敵我雙方互相確認對方手裏沒有武器、無惡意」的儀式。(雖然雙方互相請求確認須要四步,但因爲中間的確認和請求是由同一我的執行的,因此合併成了一步) 
正恩伸出手說:你看,我手裏沒有武器。(SYN) 
朗普看了看說:嗯,確實沒有。(ACK)
因而也伸出手說:你看,我手裏也沒有武器。(SYN)
正恩看了看說:嗯,看來你確實有誠意。(ACK)
複製代碼

這兩個類比就是想固然的錯誤,爲何會錯誤,看徹底文相信你便了然於心。less

另外還有一個就是在謝希仁著《計算機網絡》第四版中,講 「三次握手」 的目的是 「爲了防止已失效的鏈接請求報文段忽然又傳送到了服務端,於是產生錯誤」,這個只能算是表因,並不涉及本質。socket

謝希仁版《計算機網絡》中的例子是這樣的,「已失效的鏈接請求報文段」 的產生在這樣一種狀況下:client 發出的第一個鏈接請求報文段並無丟失,而是在某個網絡結點長時間的滯留了,以至延誤到鏈接釋放之後的某個時間纔到達 server。原本這是一個早已失效的報文段。但 server 收到此失效的鏈接請求報文段後,就誤認爲是 client 再次發出的一個新的鏈接請求。因而就向 client 發出確認報文段,贊成創建鏈接。假設不採用 「三次握手」,那麼只要 server 發出確認,新的鏈接就創建了。因爲如今 client 並無發出創建鏈接的請求,所以不會理睬 server 的確認,也不會向 server 發送數據。但 server 卻覺得新的運輸鏈接已經創建,並一直等待 client 發來數據。這樣,server 的不少資源就白白浪費掉了。採用 「三次握手」 的辦法能夠防止上述現象發生。例如剛纔那種狀況,client 不會向 server 的確認發出確認。server 因爲收不到確認,就知道 client 並無要求創建鏈接。」ide

若是你細讀RFC793,也就是 TCP 的協議 RFC,你就會發現裏面就講到了爲何三次握手是必須的——TCP 須要 seq 序列號來作可靠重傳或接收,而避免鏈接複用時沒法分辨出 seq 是延遲或者是舊連接的 seq,所以須要三次握手來約定肯定雙方的 ISN(初始 seq 序列號)。ui

下面給出詳細的 RFC 解讀說明:(數據分組稱爲分段(Segment),國內一般用包來稱呼)this


咱們首先要知道到一點就是, TCP 的可靠鏈接是靠 seq( sequence numbers 序列號)來達成的。spa

A fundamental notion in the design is that every octet of data sent over a TCP connection has a sequence number. Since every octet is sequenced, each of them can be acknowledged.
The acknowledgment mechanism employed is cumulative so that an acknowledgment of sequence number X indicates that all octets up to but not including X have been received.

TCP 設計中一個基本設定就是,經過TCP 鏈接發送的每個包,都有一個sequence number。而由於每一個包都是有序列號的,因此都能被確認收到這些包。

確認機制是累計的,因此一個對sequence number X 的確認,意味着 X 序列號以前(不包括 X) 包都是被確認接收到的。

The protocol places no restriction on a particular connection being used over and over again.

The problem that arises from this is -- "how does the TCP identify duplicate segments from previous incarnations of the connection?" This problem becomes apparent if the connection is being opened and closed in quick succession, or if the connection breaks with loss of memory and is then reestablished.

TCP 協議是不限制一個特定的鏈接(兩端 socket 同樣)被重複使用的。

因此這樣就有一個問題:這條鏈接忽然斷開重連後,TCP 怎麼樣識別以前舊連接重發的包?——這就須要獨一無二的 ISN(初始序列號)機制。

When new connections are created, an initial sequence number (ISN) generator is employed which selects a new 32 bit ISN. The generator is bound to a (possibly fictitious) 32 bit clock whose low order bit is incremented roughly every 4 microseconds. Thus, the ISN cycles approximately every 4.55 hours. Since we assume that segments will stay in the network no more than the Maximum Segment Lifetime (MSL) and that the MSL is less than 4.55 hours we can reasonably assume that ISN's will be unique.

當一個新鏈接創建時,初始序列號( initial sequence number ISN)生成器會生成一個新的32位的 ISN。

這個生成器會用一個32位長的時鐘,差很少4µs 增加一次,所以 ISN 會在大約 4.55 小時循環一次

2^32位的計數器,須要2^32*4 µs才能自增完,除以1小時共有多少µs即可算出2^32*4 /(1*60*60*1000*1000)=4.772185884

而一個段在網絡中並不會比最大分段壽命(Maximum Segment Lifetime (MSL) ,默認使用2分鐘)長,MSL 比4.55小時要短,因此咱們能夠認爲 ISN 會是惟一的。

發送方與接收方都會有本身的 ISN (下面的例子中就是 X 與 Y)來作雙方互發通訊,具體的描述以下:

  1. A --> B SYN my sequence number is X
  2. A <-- B ACK your sequence number is X
  3. A <-- B SYN my sequence number is Y
  4. A --> B ACK your sequence number is Y

2與3都是 B 發送給 A,所以能夠合併在一塊兒,所以成爲three way (or three message) handshake(其實翻譯爲三步握手,或者是三次通訊握手更爲準確)

所以最終能夠得出,三次握手是必須的:

A three way handshake is necessary because sequence numbers are not tied to a global clock in the network, and TCPs may have different mechanisms for picking the ISN's. The receiver of the first SYN has no way of knowing whether the segment was an old delayed one or not, unless it remembers the last sequence number used on the connection (which is not always possible), and so it must ask the sender to verify this SYN. The three way handshake and the advantages of a clock-driven scheme are discussed in [3].

三次握手(A three way handshake)是必須的, 由於 sequence numbers(序列號)沒有綁定到整個網絡的全局時鐘(所有統一使用一個時鐘,就能夠肯定這個包是否是延遲到的)以及 TCPs 可能有不一樣的機制來選擇 ISN(初始序列號)。

接收方接收到第一個 SYN 時,沒有辦法知道這個 SYN 是是否延遲了好久了,除非他有辦法記住在這條鏈接中,最後接收到的那個sequence numbers(然而這不老是可行的)。

這句話的意思是:一個 seq 過來了,跟如今記住的 seq 不同,我怎麼知道他是上條延遲的,仍是上上條延遲的呢?

因此,接收方必定須要跟發送方確認 SYN。

假設不確認 SYN 中的 SEQ,那麼就只有:

  1. A --> B SYN my sequence number is X
  2. A <-- B ACK your sequence number is X SYN my sequence number is Y

只有B確認了收到了 A 的 SEQ, A 沒法確認收到 B 的。也就是說,只有 A 發送給 B 的包都是可靠的, 而 B 發送給 A 的則不是,因此這不是可靠的鏈接。這種狀況若是隻須要 A 發送給 B ,B 無需迴應,則能夠不作三次握手。

三次握手詳細過程

TCP A                                                TCP B

  1.  CLOSED                                               LISTEN

  2.  SYN-SENT    --> <SEQ=100><CTL=SYN>               --> SYN-RECEIVED

  3.  ESTABLISHED <-- <SEQ=300><ACK=101><CTL=SYN,ACK>  <-- SYN-RECEIVED

  4.  ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK>       --> ESTABLISHED

  5.  ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK><DATA> --> ESTABLISHED

          Basic 3-Way Handshake for Connection Synchronization

                                Figure 7.
複製代碼

在上圖

  • 第二行中, A 發送了 SEQ 100,標誌位是 SYN;
  • 第三行,B 發回了 ACK 101 與 SEQ 300,標誌位是 SYN 與 ACK(兩個過程合併了)。注意,ACK 是101意味着,B 但願接收到 101序列號開始的數據段。
  • 第四行,A 返回了空的數據,SEQ 101, ACK 301,標誌位爲 ACK。至此,雙方的開始 SEQ (也就是 ISN)號100與300都被確認接收到了。
  • 第五行,開始正式發送數據包,注意的是 ACK 依舊是第四行的301,由於沒有須要 ACK 的 SYN 了(第四行已經 ACK 完)。
相關文章
相關標籤/搜索