【網絡編程】TCPIP-5-UDP



前言

說明:

  • 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. 第 1 階段:向 UDP 套接字注冊目標 IP 和端口號。
  2. 第 2 階段:傳輸數據。
  3. 第 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和端口號,耗時。

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM