目錄
前言
說明:
- demo 基於 Linux。
5. UDP 網路程式設計
UDP 是無連線的,不需要建立連線。
5.1 UDP 的工作原理
參考圖:
主機B的資料包中包含目的主機的IP+埠號。
其中IP是把資料的目的主機地址,埠號是目的主機對用的程式。
路由器小知識:
- IP:主機地址。如目的IP,每個路由器都會檢查IP,若在本地,就往本地埠轉發,若不在,就往外埠發。(在轉發過程中是不會變的)
- MAC:裝置地址。目的MAC,下一個物理連線的裝置地址。(在轉發過程中可能會變,因為參考的是下一個,而不是最終目的)
5.2 UDP 的高效性
UDP 不需要建立連線和響應校驗,實時性比 TCP 高。
一般用在網路實時傳遞的視訊或者音訊中,因為丟失部分資料也不會影響太大。
5.3 實現 UDP 服務端/客戶端
5.3.1 概念
UDP 伺服器和客戶端均只需一個套接字:
- TCP 中,套接字之間應該是一對一的關係。
- UDP 中,不管是伺服器端還是客戶端都只需要 1 個套接字,就可以任意傳輸。
如圖:
5.3.2 UDP 的資料 I/O 函式
/*
sock: 用於傳輸資料的 UDP 套接字
buff: 儲存待傳輸資料的緩衝地址值
nbytes: 待傳輸的資料長度,以位元組為單位
flags: 可選項引數,若沒有則傳遞 0
to: 存有目標地址的 sockaddr 結構體變數的地址值
addrlen: 傳遞給引數 to 的地址值結構體變數長度
成功時返回傳輸的位元組數,失敗時返回 -1
*/
#include <sys/socket.h>
ssize_t sendto(int sock, void *buff, size_t nbytes, int flags,
struct sockaddr *to, socklen_t addrlen);
/*
sock: 用於傳輸資料的 UDP 套接字
buff: 儲存待傳輸資料的緩衝地址值
nbytes: 待傳輸的資料長度,以位元組為單位
flags: 可選項引數,若沒有則傳遞 0
from: 存有傳送端地址資訊的 sockaddr 結構體變數的地址值
addrlen: 儲存引數 from 的結構體變數長度的變數地址值。
成功時返回傳輸的位元組數,失敗時返回 -1
*/
#include <sys/socket.h>
ssize_t recvfrom(int sock, void *buff, size_t nbytes, int flags,
struct sockaddr *from, socklen_t *addrlen);
5.3.3 UDP 客戶端地址分配
TCP 中客戶端地址可以設定也可以系統分配,(TCP connect() 函式自動完成分配IP&埠號),建立連線後就固定使用。
UDP 中客戶端中呼叫 sendto() 函式時自動分配 IP 和 埠號,首次呼叫才分配,分配後使用直至程式結束(有興趣可以看看UDP打洞技術)。
也可以在呼叫 sendto() 函式前使用 bind() 函式繫結本機 IP。
5.4 UDP 的資料傳輸特性
TCP 是流式的資料傳輸,訊息沒有邊界,需要應用層自己去定義訊息邊界。
UDP 是資料包傳輸,所以協議保證了一次只能接收一個資料包。
個人表達:資料邊界意思是,資料會不會自動分割,比如兩個結構體連續存在一段記憶體中,那是有邊界的,結構體把其分割了。若把其資料拷貝到陣列裡面,那是無邊界的,因為分不清從哪裡才是分割線。
所以UDP中本端發 N 次到對端,對端就得收 N 次。
5.5 UDP 呼叫 connect 函式
UDP套接字分:
- 未連線(unconnected)UDP 套接字。
- 已連線(connect)UDP 套接字。
瞭解下 sendto() 函式傳輸資料過程:
- 第 1 階段:向 UDP 套接字註冊目標 IP 和埠號。
- 第 2 階段:傳輸資料。
- 第 3 階段:刪除 UDP 套接字中註冊的目標地址資訊。
其實需要頻繁傳送,那第一階段和第三階段是重複多餘的,所以可以使用 已連線(connect)UDP 套接字。
建立已連線 UDP 套接字:
- 注意:這裡的已連線並不是與對端建立連線,而是繫結目標埠到 UDP socket 中,後面呼叫 sendto() 就不用執行①、③步了。
sock = socket(PF_INET, SOCK_DGRAM, 0);
memset(&adr, 0, sizeof(adr));
adr.sin_family = AF_INET;
adr.sin_addr.s_addr = inet_addr(argv[1]);
adr.sin_port = htons(atoi(argv[2]));
connect(sock, (struct sockaddr *)&adr, sizeof(adr));
小知識:
- UDP 是可以使用 bind() 函式的,主要是配置本地IP和埠號。若不適用,則由系統隨機分配。
- UDP 是可以使用 connect() 函式的,主要是配置遠端IP和埠號。若不使用,則每次呼叫 sendto() 函式時都要設定、刪除遠端IP和埠號,耗時。
參考
- 《TCP/IP網路程式設計》
- 李柱明部落格-TCP/IP TCP詳細筆記
- github 阿婆主