論tcp的n次握手和m次揮手

在計算機網絡的TCP/IP協議棧中,位於傳輸層中的協議最多見的有兩款,TCPUDP。而說到TCP,入門的兩個知識點也是考點,甚至揪細節的話,還會成爲疑點的就是三次握手和四次揮手算法

開局一張圖,tcp狀態機,從該狀態機中,咱們能夠看到咱們此次將要討論的兩個話題。緩存

論tcp的n次握手和m次揮手

三次握手

三次握手的大體內容,甚至能夠畫成下圖的漫畫。服務器

論tcp的n次握手和m次揮手

調侃歸調侃,然而道理仍是這麼個道理。咱們知道TCP的目的是提供可靠的字節流服務,爲了準確無誤地將數據送達目的地,TCP協議採納三次握手策略來創建傳輸信道,其過程以下:網絡

論tcp的n次握手和m次揮手

  1. 最初兩端的TCP進程都處於CLOSE(關閉)狀態。
  2. 發送方A主動發送一個帶有SYN( synchronize)標誌的數據包給接收方B。A這個時候會主動(active open)connect服務器,而且發送SYN,假設序列號爲J, 接收方是被動(passive open),咱們要關注的是客戶端以及服務器發送的序列號都是隨機的
  3. 接收方B在收到SYN後,若是贊成創建鏈接,會回傳發送另外一個SYN以及一個ACK(應答)給發送端A,ACK的序列號是 J+1表示是給SYNJ的應答,新發送的SYN K序列號是K的數據包傳遞確認信息,表示我收到了。這個序列號也是隨機的
  4. 發送方A再回傳一個帶有ACK標誌的數據包,表明我知道了,表示握手結束。發送方在收到新SYNK, ACKJ+1後,也迴應ACKK+1 以表示收到了,鏈接信道創建
  5. 而後兩邊就能夠開始數據發送數據了

本質

咱們不妨先來看看TCP報文首部的格式併發

論tcp的n次握手和m次揮手

這裏TCP報文其實涉及到不少內容,不過這一次咱們只關注控制位,由於控制位跟咱們的三次握手四次揮手有關係。app

  • URG:當URG=1時,代表緊急指針字段有效,告訴系統這個報文段裏有緊急數據,優先傳送。
  • ACK:當ACK=1時,確認號字段有效,ACK=0時確認號字段無效
  • PSH:收到TCP的PSH=1的報文段,就儘快的交付接受應用進程,而不用等到整個緩存都滿了再向上交付。
  • RST:當RSH=1時代表TCP鏈接出現錯誤,必須釋放鏈接,而後再從新創建鏈接。
  • SYN:SYN=1時表示這是一個鏈接請求或鏈接接受報文。
  • FIN:FIN=1表示此報文段的發送端的數據以發送完成,並要求釋放運輸鏈接。

須要注意的是,在三次握手以後傳輸數據的時候,若是握手完第一個數據包是客戶端發送,第一個數據包的seq和ack和第三次握手的同樣,那麼以後發送的seq和ack是根據上一個接收包的seq、ack和len(數據長度)獲得,具體爲:seq=ack,ack=seq+lenless

序列號的做用

序列號的做用是使得一個TCP接收端可丟棄重複的報文段,記錄以雜亂次序到達的報文段。由於TCP使用IP來傳輸報文段,而IP不提供重複消除或者保證次序正確的功能。另外一方面,TCP是一個字節流協議,毫不會以雜亂的次序給上層程序發送數據。所以TCP接收端會被迫先保持大序列號的數據不交給應用程序,直到缺失的小序列號的報文段被填滿。socket

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

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.ide

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。

ISN的計算方式以下:

ISN = M + F(localhost, localport, remotehost, remoteport)

其中M是一個計時器,每隔4µs加1。 F是一個Hash算法,根據源IP、目的IP、源端口、目的端口生成一個隨機數值

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

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

三次握手的做用

一個TCP鏈接由一個4元組構成,分別是兩個IP地址和兩個端口號。一個TCP鏈接一般分爲三個階段:啓動、數據傳輸、退出(關閉)。客戶端和服務端通訊前要進行鏈接,「3次握手」的做用就是雙方都能明確本身和對方的收、發能力是正常的。

三次握手的重要功能之一就是客戶端和服務端交換ISN(Initial Sequence Number), 這樣對方知道接下來接收數據的時候如何按序列號組裝數據。

非要三次握手嗎?

在三次握手的過程當中,實際上實現的效果是讓服務器端知道客戶端知道服務器知道。這麼說確實是有套娃的意味,可能就會有疑問了,讓服務器知道客戶端知道不就能夠了嗎?或者是難道不須要讓客戶端知道服務器端知道客戶端知道服務器知道嘛?爲什麼要畫蛇添足?筆者曾在謝希仁老師的書中能夠學習其的見解,他的觀點是爲了防止已失效的鏈接請求報文忽然又傳送到了對端,於是產生錯誤。

謝老師進一步解釋,所謂「已失效的鏈接請求報文段」是這樣產生的。

  • 考慮一種正常狀況, A發出鏈接請求,但因鏈接請求報文丟失而未收到確認。因而A再重傳一次鏈接請求。後來收到了確認,創建了鏈接。數據傳輸完畢後,就釋放了鏈接。A共發送了兩個鏈接請求報文段,其中第一個丟失,第二個到達了B,沒有「已失效的鏈接請求報文段"。
  • 現假定出現一種異常狀況,即A發出的第一個鏈接請求報文段並無丟失,而是在某些網絡結點長時間滯留了,以至延誤到鏈接釋放之後的某個時間纔到達B。原本這是一個早已失效的報文段。但B收到此失效的鏈接請求報文段後,就誤認爲是A又發出一次新的鏈接請求。因而就向A發出確認報文段,贊成創建鏈接。假定不採用報文握手,那麼只要B發出確認,新的鏈接就創建了。
  • 因爲如今A並無發出創建鏈接的請求,所以不會理睬B的確認,也不會向B發送數據。但B卻覺得新的運輸鏈接已經創建了,並一直等待A發來數據。B的許多資源就這樣白白浪費了。

以上是學術派的理解,主要是從異常處理這個方面進行分析,若是是二次握手的話,如若出現擁塞而致使的重連則會致使,對端服務的異常,須要另外設計異常的處理機制。

以後在Google Groups的TopLanguage中看到一帖討論TCP「三次握手」以爲挺有意思。貼主提出「TCP創建鏈接爲何是三次握手?」的問題,在衆多回復中,有一條回覆寫道:「這個問題的本質是, 信道不可靠, 可是通訊雙發須要就某個問題達成一致。而要解決這個問題, 不管你在消息中包含什麼信息, 三次通訊是理論上的最小值。因此三次握手不是TCP自己的要求, 而是爲了知足‘在不可靠信道上可靠地傳輸信息’這一需求所致使的。 請注意這裏的本質需求,信道不可靠,數據傳輸要可靠。三次達到了, 後面你想接着握手也好, 發數據也罷, 跟進行可靠信息傳輸的需求就不要緊了。所以,若是信道是可靠的, 即不管何時發出消息,對方必定能收到,或者你不關心是否要保證對方收到你的消息, 那就能像UDP那樣直接發送消息就能夠了。」

這其實可視爲對「三次握手」目的的另外一種窺探角度。滿屏的工程化思惟,畢竟這是對於信道傳輸不可靠的妥協,完成對可靠信息的傳輸。畢竟3次握手完成兩個重要的功能,既要雙方作好發送數據的準備工做(雙方都知道彼此已準備好),也要容許雙方就初始序列號進行協商,這個序列號在握手過程當中被髮送和確認。

好比說咱們如今把三次握手改爲僅須要兩次握手,死鎖是可能發生的。假設計算機S和C之間的通訊,此時C給S發送一個鏈接請求分組,S收到了這個分組,併發 送了確認應答分組。按照兩次握手的協定,S認爲鏈接已經成功地創建了,能夠開始發送數據分組。但是,C在S的應答分組在傳輸中被丟失的狀況下,將不知道S 是否已準備好,不知道S創建什麼樣的序列號,C甚至懷疑S是否收到本身的鏈接請求分組。在這種狀況下,C認爲鏈接還未創建成功,將忽略S發來的任何數據分 組,只等待鏈接確認應答分組。而S在發出的分組超時後,重複發送一樣的分組。這樣就造成了死鎖。漫畫圖解如圖所示

論tcp的n次握手和m次揮手

論tcp的n次握手和m次揮手

論tcp的n次握手和m次揮手

在前面的描述中咱們知道發送方與接收方都會有本身的 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].

由於 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 無需迴應,則能夠不作三次握手。

四次揮手(Four-Way-Wavehand)

說完三次握手,那另外的四次揮手也是這樣嗎?爲了妥協、爲了排除異常狀況嗎?讓咱們一步步演繹推理一下。

相關文章
相關標籤/搜索