9.9. UDP -- 送出封包,聽天由命¶
UDP(使用者資料報協定,User Datagram Protocol)是傳輸層所提供的兩種服務中較為簡單的一種。每一次 UDP 傳送就是一個 資料報(datagram)——一塊自成一體、定址至某個 IP 與某個埠號的位元組,獨自被丟進網路之中。協定能送達就送達;送不到時,它不會重試、不會警告傳送端,也不會在資料報之間保留任何順序。
9.9.1. UDP 在 IP 之上增加了什麼¶
UDP 是建構在 IP 之上的一層薄薄的封裝。它在原始的 IP 傳遞之上增加了三樣東西:
兩端皆有的 埠號,使得資料報能被傳遞到目的主機上正確的 程式,而不只是送達主機本身(請參閱 連接埠)。
一個 長度欄位,讓接收端確切知道要讀取多少位元組的酬載(payload)。
一個涵蓋標頭與酬載的 小型檢查碼(checksum),讓接收端能偵測出損毀的資料報並予以丟棄。
如此而已。IP 做過或沒做過的其他一切,UDP 全都原封不動地保留。資料報不會被重試。它們可能亂序抵達。它們可能因底層網路的怪異行為而被複製。它們也可能在網路壅塞、目的端的緩衝區已滿,或途中某個路由器決定丟棄時,被悄無聲息地丟掉。
每一個 UDP 資料報都是獨立送出的。如果其中一個在傳輸途中遺失,沒有任何東西會告知傳送端或接收端——這個缺口是無聲無息的。¶
9.9.2. 為什麼會有人使用它¶
如果 UDP 這麼不可靠,為什麼還要有它?有三個理由。
速度與額外負擔。 一次 UDP 傳送就是送出一個封包;沒有交握、沒有確認、沒有需要維護的連線狀態。延遲與頻寬成本都極低。
獨立的訊息。 有些流量是一連串的 狀態更新,其中每一則訊息都是一份全新的快照,而舊的訊息毫無價值。一台每秒發布「我還活著,這是我目前的讀數」的相機,在意的是傳遞每一筆 新的 讀數,而非重播那些遺失的讀數。
多播(Multicast)。 單一個 UDP 資料報可以一次送給許多接收端。(TCP 做不到這一點;每一條 TCP 連線都恰好介於兩個端點之間。)這對服務探索、傳給眾多監聽者的遙測資料,以及視訊串流都很有用。
在網路狀況良好、且傳送端對遺失的容忍度很高的情況下,UDP 是正確的選擇。在傳遞與順序必須獲得保證的情況下,UDP 要嘛需要在其上再疊加一層可靠性機制,要嘛應用程式乾脆改用 TCP。
9.9.3. 何時 不 該使用它¶
反過來的情況也值得明確說明。在下列情況下,UDP 是錯誤的選擇:
每一個位元組都很重要。 組態資料、程式碼更新、簽署過的交易——任何只要少了一個位元組就會使結果出錯的情況。
順序很重要。 UDP 的 datagram 2 可能比 datagram 1 先抵達。
訊息很大。 大型酬載必須由應用程式自行拆分成較小的資料報;傳輸層不會代勞。只要其中任何一片遺失,整則訊息就不完整。
如果上述任何一點適用,請改用 TCP。
9.9.4. 相機上的具體範例¶
現實世界中相機端的 UDP 流量範例:
探索(Discovery)。 相機在啟動時向區域網路廣播一個「誰在這裡」的資料報;其他裝置則予以回覆。
遙測(Telemetry)。 相機每秒送出一個帶有當前感測器讀數的小型資料報給收集端。偶爾遺失一筆樣本並無大礙,因為下一筆反正一秒後就會抵達。
時間同步。 NTP(網路時間協定)執行於 UDP 之上。client 送出一個小型請求,server 以當前時間回覆;如果回覆遺失,client 稍後再問一次即可。
DNS 查詢。 向網路詢問「這個名稱對應到哪個 IP?」是執行於 UDP 之上的。(在 名稱與 DNS 中說明。)
在傳輸層其餘的部分都就定位之後,用於傳送與接收資料報的 Python API 將於 UDP socket 說明。