傳輸層主要的作用就是建立端到端的連線。比如電腦的微信的通訊,就需要跨越多個網路裝置(交換機和路由器)再和微信的伺服器建立連線。
傳輸層需要具有以下的特點:
- 會話的多複用:如電腦上開啟的多個應用,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 連線會有三個狀態:
- 建立連線的狀態
- 傳輸資料的狀態
- 關閉連線的轉檯
先看連線狀態:
-
客戶端想要和服務端建立連線:置位 Flag 欄位中的 SYN=1,表示請求建立連線。seq = x,表示傳送的第一個位元組的序號是 x.
-
服務端收到客戶端請求:
- 置位 Flag 欄位中的 ACK=1,表示確認跟你連線連結。ack= x + 1,表示前 x 個位元組已經收到,期待傳送 x+1個位元組。
- 由於剛才連線過程是客戶端到服務端的,同樣的,服務端也需要向客戶端也發一個請求用於建立連線,這樣才是雙向連線。正常來說,服務端應該再發一個請求,但由於 TCP 的報頭設計,可以將請求功能和確認功能放在一個資料包裡,通過 Flag 同時置位 SYN 和 ACK 。這也就是為什麼連線時,三次握手而不是四次。
-
客戶端收到服務端的回覆,通過 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 欄位,表示想要斷開連線。
同樣,假如有客戶端和服務端已經建立了連線。
- 客戶端向服務端傳送報文,其中 FIN=1,表示請求斷開連線。(斷開的是客戶端到服務端的連線)
- 服務端收到後回包,ACK=FIN+1,表示收到客戶端的情況,確認斷開。
- 服務端再次傳送請求,置位 FIN=1,表示要和服務端斷開連線。(斷開的是服務端到客戶端的連線)
- 客戶端收到後,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 的不同,以及適用的場景。
最後可以用這幾個問題檢驗自己,對於都可以在文章中找到答案:
- TCP 在建立連線時,為什麼是三次握手而不是四次?
- TCP 在斷開連線時,為什麼是四次而不是三次?
- TCP 是如何保證可靠傳輸的?
- TCP 是如何進行流量控制的?
- TCP 和 UDP 的區別,以及適用的場景。