傳輸層主要的作用就是建立端到端的連接。比如電腦的微信的通信,就需要跨越多個網絡設備(交換機和路由器)再和微信的服務器建立連接。
傳輸層需要具有以下的特點:
- 會話的多復用:如電腦上開啟的多個應用,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 的區別,以及適用的場景。