CCNA-Part5 - 傳輸層 ,TCP 為什麼是三次握手?

以終為始發表於2020-07-09

傳輸層主要的作用就是建立端到端的連線。比如電腦的微信的通訊,就需要跨越多個網路裝置(交換機和路由器)再和微信的伺服器建立連線。

傳輸層需要具有以下的特點:

  • 會話的多複用:如電腦上開啟的多個應用,QQ,微信等,這就意味著同時需要建立多個會話。
  • 識別應用程式:通過埠號,來區分不同的應用程式。
  • 分段:在傳送資料時,將資料段分為多個部分進行傳送,然後在接收端重新組裝這些資料段。(TCP)
  • 流量控制:在傳送端和接受端,兩者傳送和接受的頻寬不一致時,可以動態的調整頻寬。(TCP)
  • 面向連線:確認對方能接收時再傳送(TCP)。
  • 可靠性:保證對方一定能收到資料。(TCP)。

TCP 和 UDP 就是實現上述特點的協議。

TCP 報文段

TCP 連線過程 - 三次握手

三次握手是個老生常談的話題,還得在大學時死記硬背這個連線過程,現在想想還蠻好玩的。都說檢驗一個人是否理解一件事情,就讓他給一個從來沒有聽過的人講,講明白了,自然證明也就懂了。其實這就是主動學習和被動學習的體現,大家有興趣可以查查費曼學習法,講授給他人,寫文章等都是一種主動學習的方式。有點扯遠了,回到三次握手的過程,希望能給大家講清楚。

在談起連線過程前,先要對 TCP Header 中這幾個欄位有一個清晰的瞭解,在上面的導圖也提到了,下面著重強調一下:

關於 Flag 欄位: 該欄位是 1 Byte,共 8 位,如果每一位置 1 後,就代表該 TCP 報文開啟了對應的功能,其中三次握手共涉及 3 個欄位,注意我這裡是用大寫的表示,並且只有 1 和 0 兩種狀態:

  • SYN 欄位:當置 1 時,表示該報文是請求連線的報文段,用於在 TCP 連線時的第一個連線。
  • ACK 欄位:當置 1 時,表示該報文是確認報文,用於表示對方發的請求連線已經收到,這裡給予確認。
  • FIN 欄位:當置 1 時,表示傳送方已經傳送完畢,請求關閉連線。

關於序號欄位 seq: 注意這裡是小寫,共有 4 Byte,32 位。和 IP 層一樣,有時當傳送的資料超過固定的 MTU 大小時,會將資料包拆成多個小的資料包傳送,但資料是有順序要求,為了保證資料的順序,就有了該欄位 seq,用於表示第一個位元組在整個位元組流的標號。

關於確認號欄位 ack: 和 seq 一樣,4 Byte. 表示對收到的資料進行確認,並告訴傳送方接下來期待的序列號是什麼。

最後還需要明確一點,TCP 建立的是雙向的連線,因為在端點的另一方可以是傳送方也可能是接收方,明確這點非常重要。

上面看起來比較抽象,一會實際抓包,對照起來看就清晰了。先有個印象,先來分析下 TCP 的連線過程。

一個 TCP 連線會有三個狀態:

  • 建立連線的狀態
  • 傳輸資料的狀態
  • 關閉連線的轉檯

先看連線狀態:

  1. 客戶端想要和服務端建立連線:置位 Flag 欄位中的 SYN=1,表示請求建立連線。seq = x,表示傳送的第一個位元組的序號是 x.

  2. 服務端收到客戶端請求:

    1. 置位 Flag 欄位中的 ACK=1,表示確認跟你連線連結。ack= x + 1,表示前 x 個位元組已經收到,期待傳送 x+1個位元組。
    2. 由於剛才連線過程是客戶端到服務端的,同樣的,服務端也需要向客戶端也發一個請求用於建立連線,這樣才是雙向連線。正常來說,服務端應該再發一個請求,但由於 TCP 的報頭設計,可以將請求功能和確認功能放在一個資料包裡,通過 Flag 同時置位 SYN 和 ACK 。這也就是為什麼連線時,三次握手而不是四次。
  3. 客戶端收到服務端的回覆,通過 ack=x+1 知道,服務端收到了 x 位元組的資料,期待接受 x+1 位元組的資料。通過 SYN=1,seq=y 知道服務端想要和自己連線連線。進而回復 ACK=1,seq=x+1,ack=y+1.

傳輸狀態:

如何保證資料的可靠性傳輸?

在客戶端給服務傳輸資料時,如果收到 ACK 的報文,則代表服務端確實收到了傳遞的資料。考慮這樣一種情況,客戶端向服務端傳送了 seq=10 的報文,但卻收到服務端 ack=10 的回覆。其實這就代表著,存在丟包的情況,服務端並未收到客戶端 seq=10 的包。這時服務端會重新傳送 seq=10 的報文,也就是說,在客戶端只有成功的接收了服務端的 ACK 包,才會繼續向下傳遞。

如何進行流量控制?

在 TCP Header 中有一個叫視窗大小的欄位。當路由器傳送或者接收資料時,會將資料先快取起來,這個視窗就是對應的快取區。由於造價的不同,不同的裝置對應的緩衝區大小也不一樣。考慮這樣一種情況,客戶端的視窗大小為 3 Byte,但服務端的視窗大小為 2 Byte。這就意味著,服務端只能先快取 2 Byte 的資料。接著來看下,TCP 是如何進行流量控制的:

  • 第一次,客戶端會給服務端直接傳送 3 Byte 的資料,注意是通過一個包裡面包含 3 Byte 的資料。但由於服務端只有 2 Byte 的快取空間,第三個 Byte 的內容會被丟掉。
  • 接著服務端會回報,進行 ACK 確認,傳送 ack=3,代表接受到了 seq=2 之前的資料。同時還會傳送視窗大小 WS(2)告訴客戶端自己只有 2 Byte 的快取空間。
  • 之後客戶端只會傳送快取空間為 2 的資料包給服務端。

考慮一種特殊的情況:

在服務端收到 2 Byte 的資料時,此時 CPU 的處理變得低效,只從緩衝空間中取出了 1 個位元組傳送終端。

這時服務端再給客戶端回包時,除了正常回復 ack=x+1 的確認,還會改變視窗大小為 1.,這就意味著告訴服務端,下次給我發包時

只能接受一個 Byte 的資料。

實際抓包看一下,這裡以訪問百度為例子:

第一次握手:

結合之前說的:

  • 在 Flag 欄位中,SYN 置1,表示這是一個請求連線的報文。
  • seq=0,注意這裡的 0 只是抓包軟體的相對值,真實值是下面的 ac f7 f8 8b.
  • 源埠為:8548
  • 目的埠為:443

再看第二次握手:

同樣:

  • 服務端再 Flag 為 SYN 和 ACK 置位,表示該報具有確認和請求連線的功能。
  • ack = ac f7 f8 8c 正好是之前 seq 的大小 +1.
  • seq = 0,其實這裡也是相對的位置,真實值為 fd 15 e1 c4,在 ack 的前四位元組就是 syn.

最後第三次握手:

  • 表示客戶端確認和服務建立連線,對應 Flag ACK 置位。
  • ack = fd 15 e1 c5, fd 15 e1 c4 + 1.

斷開連線:

理解了 TCP 連線的過程,再看斷開連線就很容易了。一樣的,首先要明確,已經建立的連線是雙向的連線。如果想要斷開的話,自然雙方都要發起一個請求。

而報文內部也大致相同,僅僅是把 Flag 欄位中 SYN 換成 FIN 欄位,表示想要斷開連線。

同樣,假如有客戶端和服務端已經建立了連線。

  1. 客戶端向服務端傳送報文,其中 FIN=1,表示請求斷開連線。(斷開的是客戶端到服務端的連線)
  2. 服務端收到後回包,ACK=FIN+1,表示收到客戶端的情況,確認斷開。
  3. 服務端再次傳送請求,置位 FIN=1,表示要和服務端斷開連線。(斷開的是服務端到客戶端的連線)
  4. 客戶端收到後,ACK=FIN+1,表示確認斷開和服務的連線。

這裡一定會有疑問,為什麼在三次握手中,可以在一個資料包中同時置位 SYN 和 ACK 表示確認和請求連線的過程,等到了斷開連線時,就不能這樣做呢,而是需要傳送兩次。

原因就在於,由於連線是雙向的,第1,2步驟表示,客戶端已經沒有資料傳送給服務端了。但這時,服務端可能還有資料正在傳送給客戶端,等待資料傳送完成後。才能進行 3,4 步驟,然後斷開服務端到客戶端的連線。

UDP 報文段

學習了 TCP,再看 UDP 自然就很簡單了。

先看 UDP 的特性:

  • 工作在傳輸層
  • 執行有限制的差錯校驗
  • 提供盡力而為的傳輸
  • 不可能的傳輸
  • 開銷低,傳輸的效率高

可以看到,UDP 的 Header 中,僅有源埠,目的埠用於識別應用程式。

下面來比較一下 TCP 和 UDP:

關於連線的型別:面向連線/面向無連線

序號:TCP 由於是可靠傳輸,所以需要對收到的內容進行 ACK 確認,而 UDP 直接傳送,不管對方能不能接受。

應用場景:TCP 要求可靠的通訊,如郵件,FTP,瀏覽網頁,下載等服務。而 UDP 則適合語音,視訊,HDCP等等。

總結

傳輸層的作用就是建立端到端的連線,為了實現該功能,一般有兩種協議 TCP 和 UDP 可以選擇。

對於 TCP 來說,需要了解並理解三次握手和四次揮手的過程。比較 TCP 和 UDP 的不同,以及適用的場景。

最後可以用這幾個問題檢驗自己,對於都可以在文章中找到答案:

  1. TCP 在建立連線時,為什麼是三次握手而不是四次?
  2. TCP 在斷開連線時,為什麼是四次而不是三次?
  3. TCP 是如何保證可靠傳輸的?
  4. TCP 是如何進行流量控制的?
  5. TCP 和 UDP 的區別,以及適用的場景。

相關文章