肝了一週的 UDP 基礎知識終於出來了。

程式設計師cxuan發表於2020-11-26

我把自己以往的文章彙總成為了 Github ,歡迎各位大佬 star
https://github.com/crisxuan/bestJavaer
已提交此篇文章

運輸層位於應用層和網路層之間,是 OSI 分層體系中的第四層,同時也是網路體系結構的重要部分。運輸層主要負責網路上的端到端通訊。

肝了一週的 UDP 基礎知識終於出來了。

運輸層為執行在不同主機上的應用程式之間的通訊起著至關重要的作用。下面我們就來一起探討一下關於運輸層的協議部分

運輸層概述

計算機網路的運輸層非常類似於高速公路,高速公路負責把人或者物品從一端運送到另一端,而計算機網路的運輸層則負責把報文從一端運輸到另一端,這個端指的就是 端系統。在計算機網路中,任意一個可以交換資訊的介質都可以稱為端系統,比如手機、網路媒體、電腦、運營商等。

在運輸層運輸報文的過程中,會遵守一定的協議規範,比如一次傳輸的資料限制、選擇什麼樣的運輸協議等。運輸層實現了讓兩個互不相關的主機進行邏輯通訊的功能,看起來像是讓兩個主機相連一樣。

運輸層協議是在端系統中實現的,而不是在路由器中實現的。路由只是做識別地址並轉發的功能。這就比如快遞員送快遞一樣,當然是要由地址的接受人也就是 xxx 號樓 xxx 單元 xxx 室的這個人來判斷了!

TCP 如何判斷是哪個埠的呢?

還記得資料包的結構嗎,這裡來回顧一下

資料包經過每層後,該層協議都會在資料包附上包首部,一個完整的包首部圖如上所示。

在資料傳輸到運輸層後,會為其附上 TCP 首部,首部包含著源埠號和目的埠號。

在傳送端,運輸層將從傳送應用程式程式接收到的報文轉化成運輸層分組,分組在計算機網路中也稱為 報文段(segment)。運輸層一般會將報文段進行分割,分割成為較小的塊,為每一塊加上運輸層首部並將其向目的地傳送。

在傳送過程中,可選的運輸層協議(也就是交通工具) 主要有 TCPUDP ,關於這兩種運輸協議的選擇及其特性也是我們著重探討的重點。

TCP 和 UDP 前置知識

在 TCP/IP 協議中能夠實現傳輸層功能的,最具代表性的就是 TCP 和 UDP。提起 TCP 和 UDP ,就得先從這兩個協議的定義說起。

TCP 叫做傳輸控制協議(TCP,Transmission Control Protocol),通過名稱可以大致知道 TCP 協議有控制傳輸的功能,主要體現在其可控,可控就表示著可靠,確實是這樣的,TCP 為應用層提供了一種可靠的、面向連線的服務,它能夠將分組可靠的傳輸到服務端。

UDP 叫做 使用者資料包協議(UDP,User Datagram Protocol),通過名稱可以知道 UDP 把重點放在了資料包上,它為應用層提供了一種無需建立連線就可以直接傳送資料包的方法。

怎麼計算機網路中的術語對一個資料的描述這麼多啊?

在計算機網路中,在不同層之間會有不同的描述。我們上面提到會將運輸層的分組稱為報文段,除此之外,還會將 TCP 中的分組也稱為報文段,然而將 UDP 的分組稱為資料包,同時也將網路層的分組稱為資料包

但是為了統一,一般在計算機網路中我們統一稱 TCP 和 UDP 的報文為 報文段,這個就相當於是約定,到底如何稱呼不用過多糾結啦。

套接字

在 TCP 或者 UDP 傳送具體的報文資訊前,需要先經過一扇 ,這個門就是套接字(socket),套接字向上連線著應用層,向下連線著網路層。在作業系統中,作業系統分別為應用和硬體提供了介面(Application Programming Interface)。而在計算機網路中,套接字同樣是一種介面,它也是有介面 API 的。

使用 TCP 或 UDP 通訊時,會廣泛用到套接字的 API,使用這套 API 設定 IP 地址、埠號,實現資料的傳送和接收。

現在我們知道了, Socket 和 TCP/IP 沒有必然聯絡,Socket 的出現只是方便了 TCP/IP 的使用,如何方便使用呢?你可以直接使用下面 Socket API 的這些方法。

方法 描述
create() 建立一個 socket
bind() 套接字標識,一般用於繫結埠號
listen() 準備接收連線
connect() 準備充當傳送者
accept() 準備作為接收者
write() 傳送資料
read() 接收資料
close() 關閉連線

套接字型別

套接字的主要型別有三種,下面我們分別介紹一下

  • 資料包套接字(Datagram sockets):資料包套接字提供一種無連線的服務,而且並不能保證資料傳輸的可靠性。資料有可能在傳輸過程中丟失或出現資料重複,且無法保證順序地接收到資料。資料包套接字使用UDP( User DatagramProtocol)協議進行資料的傳輸。由於資料包套接字不能保證資料傳輸的可靠性,對於有可能出現的資料丟失情況,需要在程式中做相應的處理。
  • 流套接字(Stream sockets):流套接字用於提供面向連線、可靠的資料傳輸服務。能夠保證資料的可靠性、順序性。流套接字之所以能夠實現可靠的資料服務,原因在於其使用了傳輸控制協議,即 TCP(The Transmission Control Protocol)協議
  • 原始套接字(Raw sockets): 原始套接字允許直接傳送和接收 IP 資料包,而無需任何特定於協議的傳輸層格式,原始套接字可以讀寫核心沒有處理過的 IP 資料包。

套接字處理過程

在計算機網路中,要想實現通訊,必須至少需要兩個端系統,至少需要一對兩個套接字才行。下面是套接字的通訊過程。

肝了一週的 UDP 基礎知識終於出來了。
  1. socket 中的 API 用於建立通訊鏈路中的端點,建立完成後,會返回描述該套接字的套接字描述符

就像使用檔案描述符來訪問檔案一樣,套接字描述符用來訪問套接字。

  1. 當應用程式具有套接字描述符後,它可以將唯一的名稱繫結在套接字上,伺服器必須繫結一個名稱才能在網路中訪問
  2. 在為服務端分配了 socket 並且將名稱使用 bind 繫結到套接字上後,將會呼叫 listen api。listen 表示客戶端願意等待連線的意願,listen 必須在 accept api 之前呼叫。
  3. 客戶端應用程式在流套接字(基於 TCP)上呼叫 connect 發起與伺服器的連線請求。
  4. 伺服器應用程式使用acceptAPI 接受客戶端連線請求,伺服器必須先成功呼叫 bind 和 listen 後,再呼叫 accept api。
  5. 在流套接字之間建立連線後,客戶端和伺服器就可以發起 read/write api 呼叫了。
  6. 當伺服器或客戶端要停止操作時,就會呼叫 close API 釋放套接字獲取的所有系統資源。

雖然套接字 API 位於應用程式層和傳輸層之間的通訊模型中,但是套接字 API 不屬於通訊模型。套接字 API 允許應用程式與傳輸層和網路層進行互動。

在往下繼續聊之前,我們先播放一個小插曲,簡單聊一聊 IP。

聊聊 IP

IPInternet Protocol(網際互連協議)的縮寫,是 TCP/IP 體系中的網路層協議。設計 IP 的初衷主要想解決兩類問題

  • 提高網路擴充套件性:實現大規模網路互聯
  • 對應用層和鏈路層進行解藕,讓二者獨立發展。

IP 是整個 TCP/IP 協議族的核心,也是構成網際網路的基礎。為了實現大規模網路的互通互聯,IP 更加註重適應性、簡潔性和可操作性,並在可靠性做了一定的犧牲。IP 不保證分組的交付時限和可靠性,所傳送分組有可能出現丟失、重複、延遲或亂序等問題。

我們知道,TCP 協議的下一層就是 IP 協議層,既然 IP 不可靠,那麼如何保證資料能夠準確無誤地到達呢?

這就涉及到 TCP 傳輸機制的問題了,我們後面聊到 TCP 的時候再說。

埠號

在聊埠號前,先來聊一聊檔案描述以及 socket 和埠號的關係

為了方便資源的使用,提高機器的效能、利用率和穩定性等等原因,我們的計算機都有一層軟體叫做作業系統,它用於幫我們管理計算機可以使用的資源,當我們的程式要使用一個資源的時候,可以向作業系統申請,再由作業系統為我們的程式分配和管理資源。通常當我們要訪問一個核心裝置或檔案時,程式可以呼叫系統函式,系統就會為我們開啟裝置或檔案,然後返回一個檔案描述符fd(或稱為ID,是一個整數),我們要訪問該裝置或檔案,只能通過該檔案描述符。可以認為該編號對應著開啟的檔案或裝置。

而當我們的程式要使用網路時,要使用到對應的作業系統核心的操作和網路卡裝置,所以我們可以向作業系統申請,然後系統會為我們建立一個套接字 Socket,並返回這個 Socket 的ID,以後我們的程式要使用網路資源,只要向這個 Socket 的編號 ID 操作即可。而我們的每一個網路通訊的程式至少對應著一個 Socket。向 Socket 的 ID 中寫資料,相當於向網路傳送資料,向 Socket 中讀資料,相當於接收資料。而且這些套接字都有唯一識別符號——檔案描述符 fd。

埠號是 16 位的非負整數,它的範圍是 0 - 65535 之間,這個範圍會分為三種不同的埠號段,由 Internet 號碼分配機構 IANA 進行分配

  • 周知/標準埠號,它的範圍是 0 - 1023
  • 註冊埠號,範圍是 1024 - 49151
  • 私有埠號,範圍是 49152 - 6553

一臺計算機上可以執行多個應用程式,當一個報文段到達主機後,應該傳輸給哪個應用程式呢?你怎麼知道這個報文段就是傳遞給 HTTP 伺服器而不是 SSH 伺服器的呢?

是憑藉埠號嗎?當報文到達伺服器時,是埠號來區分不同應用程式的,所以應該藉助埠號來區分。

舉個例子反駁一下 cxuan,假如到達伺服器的兩條資料都是由 80 埠發出的你該如何區分呢?或者說到達伺服器的兩條資料埠一樣,協議不同,該如何區分呢?

所以僅憑埠號來確定某一條報文顯然是不夠的。

網際網路上一般使用 源 IP 地址、目標 IP 地址、源埠號、目標埠號 來進行區分。如果其中的某一項不同,就被認為是不同的報文段。這些也是多路分解和多路複用 的基礎。

確定埠號

在實際通訊之前,需要先確定一下埠號,確定埠號的方法分為兩種:

  • 標準既定的埠號

標準既定的埠號是靜態分配的,每個程式都會有自己的埠號,每個埠號都有不同的用途。埠號是一個 16 位元的數,其大小在 0 - 65535 之間,0 - 1023 範圍內的埠號都是動態分配的既定埠號,例如 HTTP 使用 80 埠來標識,FTP 使用 21 埠來標識,SSH 使用 22 來標識。這類埠號有一個特殊的名字,叫做 周知埠號(Well-Known Port Number)

  • 時序分配的埠號

第二種分配埠號的方式是一種動態分配法,在這種方法下,客戶端應用程式可以完全不用自己設定埠號,憑藉作業系統進行分配,作業系統可以為每個應用程式分配互不衝突的埠號。這種動態分配埠號的機制即使是同一個客戶端發起的 TCP 連線,也能識別不同的連線。

多路複用和多路分解

我們上面聊到了在主機上的每個套接字都會分配一個埠號,當報文段到達主機時,運輸層會檢查報文段中的目的埠號,並將其定向到相應的套接字,然後報文段中的資料通過套接字進入其所連線的程式。下面我們來聊一下什麼是多路複用和多路分解的概念。

多路複用和多路分解分為兩種,即無連線的多路複用(多路分解)和面向連線的多路複用(多路分解)

無連線的多路複用和多路分解

開發人員會編寫程式碼確定埠號是周知埠號還是時序分配的埠號。假如主機 A 中的一個 10637 埠要向主機 B 中的 45438 埠傳送資料,運輸層採用的是 UDP 協議,資料在應用層產生後,會在運輸層中加工處理,然後在網路層將資料封裝得到 IP 資料包,IP 資料包通過鏈路層盡力而為的交付給主機 B,然後主機 B 會檢查報文段中的埠號判斷是哪個套接字的,這一系列的過程如下所示

UDP 套接字就是一個二元組,二元組包含目的 IP 地址和目的埠號。

所以,如果兩個 UDP 報文段有不同的源 IP 地址和/或相同的源埠號,但是具有相同的目的 IP 地址和目的埠號,那麼這兩個報文會通過套接字定位到相同的目的程式。

這裡思考一個問題,主機 A 給主機 B 傳送一個訊息,為什麼還需要知道源埠號呢?比如我給妹子表達出我對你有點意思的資訊,妹子還需要知道這個資訊是從我的哪個器官發出的嗎?知道是我這個人對你有點意思不就完了?實際上是需要的,因為妹子如果要表達出她對你也有點意思,她是不是可能會親你一口,那她得知道往哪親吧?

這就是,在 A 到 B 的報文段中,源埠號會作為 返回地址 的一部分,即當 B 需要回發一個報文段給 A 時,B 需要從 A 到 B 中的源埠號取值,如下圖所示

面向連線的多路複用與多路分解

如果說無連線的多路複用和多路分解指的是 UDP 的話,那麼面向連線的多路複用與多路分解指的是 TCP 了,TCP 和 UDP 在報文結構上的差別是,UDP 是一個二元組而 TCP 是一個四元組,即源 IP 地址、目標 IP 地址、源埠號、目標埠號 ,這個我們上面也提到了。當一個 TCP 報文段從網路到達一臺主機時,這個主機會根據這四個值拆解到對應的套接字上。

上圖顯示了面向連線的多路複用和多路分解的過程,圖中主機 C 向主機 B 發起了兩個 HTTP 請求,主機 A 向主機 C 發起了一個 HTTP 請求,主機 A、B、C 都有自己唯一的 IP 地址,當主機 C 發出 HTTP 請求後,主機 B 能夠分解這兩個 HTTP 連線,因為主機 C 發出請求的兩個源埠號不同,所以對於主機 B 來說,這是兩條請求,主機 B 能夠進行分解。對於主機 A 和主機 C 來說,這兩個主機有不同的 IP 地址,所以對於主機 B 來說,也能夠進行分解。

UDP

終於,我們開始了對 UDP 協議的探討,淦起!

UDP 的全稱是 使用者資料包協議(UDP,User Datagram Protocol),UDP 為應用程式提供了一種無需建立連線就可以傳送封裝的 IP 資料包的方法。如果應用程式開發人員選擇的是 UDP 而不是 TCP 的話,那麼該應用程式相當於就是和 IP 直接打交道的。

從應用程式傳遞過來的資料,會附加上多路複用/多路分解的源和目的埠號欄位,以及其他欄位,然後將形成的報文傳遞給網路層,網路層將運輸層報文段封裝到 IP 資料包中,然後盡力而為的交付給目標主機。最關鍵的一點就是,使用 UDP 協議在將資料包傳遞給目標主機時,傳送方和接收方的運輸層實體間是沒有握手的。正因為如此,UDP 被稱為是無連線的協議。

UDP 特點

UDP 協議一般作為流媒體應用、語音交流、視訊會議所使用的傳輸層協議,我們大家都知道的 DNS 協議底層也使用了 UDP 協議,這些應用或協議之所以選擇 UDP 主要是因為以下這幾點

  • 速度快,採用 UDP 協議時,只要應用程式將資料傳給 UDP,UDP 就會將此資料打包進 UDP 報文段並立刻傳遞給網路層,然後 TCP 有擁塞控制的功能,它會在傳送前判斷網際網路的擁堵情況,如果網際網路極度阻塞,那麼就會抑制 TCP 的傳送方。使用 UDP 的目的就是希望實時性。
  • 無須建立連線,TCP 在資料傳輸之前需要經過三次握手的操作,而 UDP 則無須任何準備即可進行資料傳輸。因此 UDP 沒有建立連線的時延。如果使用 TCP 和 UDP 來比喻開發人員:TCP 就是那種凡事都要設計好,沒設計不會進行開發的工程師,需要把一切因素考慮在內後再開幹!所以非常靠譜;而 UDP 就是那種上來直接乾乾幹,接到專案需求馬上就開幹,也不管設計,也不管技術選型,就是幹,這種開發人員非常不靠譜,但是適合快速迭代開發,因為可以馬上上手!
  • 無連線狀態,TCP 需要在端系統中維護連線狀態,連線狀態包括接收和傳送快取、擁塞控制引數以及序號和確認號的引數,在 UDP 中沒有這些引數,也沒有傳送快取和接受快取。因此,某些專門用於某種特定應用的伺服器當應用程式執行在 UDP 上,一般能支援更多的活躍使用者
  • 分組首部開銷小,每個 TCP 報文段都有 20 位元組的首部開銷,而 UDP 僅僅只有 8 位元組的開銷。

這裡需要注意一點,並不是所有使用 UDP 協議的應用層都是不可靠的,應用程式可以自己實現可靠的資料傳輸,通過增加確認和重傳機制。所以使用 UDP 協議最大的特點就是速度快。

UDP 報文結構

下面來一起看一下 UDP 的報文結構,每個 UDP 報文分為 UDP 報頭和 UDP 資料區兩部分。報頭由 4 個 16 位長(2 位元組)欄位組成,分別說明該報文的源埠、目的埠、報文長度和校驗值。

肝了一週的 UDP 基礎知識終於出來了。
  • 源埠號(Source Port) :這個欄位佔據 UDP 報文頭的前 16 位,通常包含傳送資料包的應用程式所使用的 UDP 埠。接收端的應用程式利用這個欄位的值作為傳送響應的目的地址。這個欄位是可選項,有時不會設定源埠號。沒有源埠號就預設為 0 ,通常用於不需要返回訊息的通訊中。
  • 目標埠號(Destination Port): 表示接收端埠,欄位長為 16 位
  • 長度(Length): 該欄位佔據 16 位,表示 UDP 資料包長度,包含 UDP 報文頭和 UDP 資料長度。因為 UDP 報文頭長度是 8 個位元組,所以這個值最小為 8,最大長度為 65535 位元組。
  • 校驗和(Checksum):UDP 使用校驗和來保證資料安全性,UDP 的校驗和也提供了差錯檢測功能,差錯檢測用於校驗報文段從源到目標主機的過程中,資料的完整性是否發生了改變。傳送方的 UDP 對報文段中的 16 位元字的和進行反碼運算,求和時遇到的位溢位都會被忽略,比如下面這個例子,三個 16 位元的數字進行相加
肝了一週的 UDP 基礎知識終於出來了。

​ 這些 16 位元的前兩個和是

肝了一週的 UDP 基礎知識終於出來了。

​ 然後再將上面的結果和第三個 16 位元的數進行相加

肝了一週的 UDP 基礎知識終於出來了。

最後一次相加的位會進行溢位,溢位位 1 要被捨棄,然後進行反碼運算,反碼運算就是將所有的 1 變為 0 ,0 變為 1。因此 1000 0100 1001 0101 的反碼就是 0111 1011 0110 1010,這就是校驗和,如果在接收方,資料沒有出現差錯,那麼全部的 4 個 16 位元的數值進行運算,同時也包括校驗和,如果最後結果的值不是 1111 1111 1111 1111 的話,那麼就表示傳輸過程中的資料出現了差錯。

下面來想一個問題,為什麼 UDP 會提供差錯檢測的功能?

這其實是一種 端到端 的設計原則,這個原則說的是要讓傳輸中各種錯誤發生的概率降低到一個可以接受的水平

檔案從主機A傳到主機B,也就是說AB主機要通訊,需要經過三個環節:首先是主機A從磁碟上讀取檔案並將資料分組成一個個資料包packet,,然後資料包通過連線主機A和主機B的網路傳輸到主機B,最後是主機B收到資料包並將資料包寫入磁碟。在這個看似簡單其實很複雜的過程中可能會由於某些原因而影響正常通訊。比如:磁碟上檔案讀寫錯誤、緩衝溢位、記憶體出錯、網路擁擠等等這些因素都有可能導致資料包的出錯或者丟失,由此可見用於通訊的網路是不可靠的。

由於實現通訊只要經過上述三個環節,那麼我們就想是否在其中某個環節上增加一個檢錯糾錯機制來用於對資訊進行把關呢?

網路層肯定不能做這件事,因為網路層的最主要目的是增大資料傳輸的速率,網路層不需要考慮資料的完整性,資料的完整性和正確性交給端系統去檢測就行了,因此在資料傳輸中,對於網路層只能要求其提供儘可能好的資料傳輸服務,而不可能寄希望於網路層提供資料完整性的服務。

UDP 不可靠的原因是它雖然提供差錯檢測的功能,但是對於差錯沒有恢復能力更不會有重傳機制

另外,cxuan 肝了六本 PDF,公號回覆 cxuan ,領取作者全部 PDF

你也可以在下方連結獲取

連結: https://pan.baidu.com/s/1mYAeS9hIhdMFh2rF3FDk0A 密碼: p9rs

相關文章