肝了一周的 UDP 基礎知識終於出來了。
我把自己以往的文章匯總成為了 Github ,歡迎各位大佬 star
//github.com/crisxuan/bestJavaer
已提交此篇文章
運輸層
位於應用層和網路層之間,是 OSI 分層體系中的第四層,同時也是網路體系結構的重要部分。運輸層主要負責網路上的端到端通訊。
運輸層為運行在不同主機上的應用程式之間的通訊起著至關重要的作用。下面我們就來一起探討一下關於運輸層的協議部分
運輸層概述
電腦網路的運輸層非常類似於高速公路,高速公路負責把人或者物品從一端運送到另一端,而電腦網路的運輸層則負責把報文從一端運輸到另一端,這個端指的就是 端系統
。在電腦網路中,任意一個可以交換資訊的介質都可以稱為端系統,比如手機、網路媒體、電腦、運營商等。
在運輸層運輸報文的過程中,會遵守一定的協議規範,比如一次傳輸的數據限制、選擇什麼樣的運輸協議等。運輸層實現了讓兩個互不相關的主機進行邏輯通訊
的功能,看起來像是讓兩個主機相連一樣。
運輸層協議是在端系統中實現的,而不是在路由器中實現的。路由只是做識別地址並轉發的功能。這就比如快遞員送快遞一樣,當然是要由地址的接受人也就是 xxx 號樓 xxx 單元 xxx 室的這個人來判斷了!
TCP 如何判斷是哪個埠的呢?
還記得數據包的結構嗎,這裡來回顧一下
數據包經過每層後,該層協議都會在數據包附上包首部,一個完整的包首部圖如上所示。
在數據傳輸到運輸層後,會為其附上 TCP 首部,首部包含著源埠號和目的埠號。
在發送端,運輸層將從發送應用程式進程接收到的報文轉化成運輸層分組
,分組在電腦網路中也稱為 報文段(segment)
。運輸層一般會將報文段進行分割,分割成為較小的塊,為每一塊加上運輸層首部並將其向目的地發送。
在發送過程中,可選的運輸層協議(也就是交通工具) 主要有 TCP
和 UDP
,關於這兩種運輸協議的選擇及其特性也是我們著重探討的重點。
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 數據包。
套接字處理過程
在電腦網路中,要想實現通訊,必須至少需要兩個端系統,至少需要一對兩個套接字才行。下面是套接字的通訊過程。
- socket 中的 API 用於創建通訊鏈路中的端點,創建完成後,會返回描述該套接字的
套接字描述符
。
就像使用文件描述符來訪問文件一樣,套接字描述符用來訪問套接字。
- 當應用程式具有套接字描述符後,它可以將唯一的名稱綁定在套接字上,伺服器必須綁定一個名稱才能在網路中訪問
- 在為服務端分配了 socket 並且將名稱使用 bind 綁定到套接字上後,將會調用 listen api。
listen
表示客戶端願意等待連接的意願,listen 必須在 accept api 之前調用。 - 客戶端應用程式在流套接字(基於 TCP)上調用
connect
發起與伺服器的連接請求。 - 伺服器應用程式使用
accept
API 接受客戶端連接請求,伺服器必須先成功調用 bind 和 listen 後,再調用 accept api。 - 在流套接字之間建立連接後,客戶端和伺服器就可以發起 read/write api 調用了。
- 當伺服器或客戶端要停止操作時,就會調用
close
API 釋放套接字獲取的所有系統資源。
雖然套接字 API 位於應用程式層和傳輸層之間的通訊模型中,但是套接字 API 不屬於通訊模型。套接字 API 允許應用程式與傳輸層和網路層進行交互。
在往下繼續聊之前,我們先播放一個小插曲,簡單聊一聊 IP。
聊聊 IP
IP
是Internet 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 位元組)欄位組成,分別說明該報文的源埠、目的埠、報文長度和校驗值。
源埠號(Source Port)
:這個欄位佔據 UDP 報文頭的前 16 位,通常包含發送數據報的應用程式所使用的 UDP 埠。接收端的應用程式利用這個欄位的值作為發送響應的目的地址。這個欄位是可選項,有時不會設置源埠號。沒有源埠號就默認為 0 ,通常用於不需要返回消息的通訊中。目標埠號(Destination Port)
: 表示接收端埠,欄位長為 16 位長度(Length)
: 該欄位佔據 16 位,表示 UDP 數據報長度,包含 UDP 報文頭和 UDP 數據長度。因為 UDP 報文頭長度是 8 個位元組,所以這個值最小為 8,最大長度為 65535 位元組。校驗和(Checksum)
:UDP 使用校驗和來保證數據安全性,UDP 的校驗和也提供了差錯檢測功能,差錯檢測用於校驗報文段從源到目標主機的過程中,數據的完整性是否發生了改變。發送方的 UDP 對報文段中的 16 比特字的和進行反碼運算,求和時遇到的位溢出都會被忽略,比如下面這個例子,三個 16 比特的數字進行相加
這些 16 比特的前兩個和是
然後再將上面的結果和第三個 16 比特的數進行相加
最後一次相加的位會進行溢出,溢出位 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。
你也可以在下方鏈接獲取
鏈接: //pan.baidu.com/s/1mYAeS9hIhdMFh2rF3FDk0A 密碼: p9rs