天天看點

【網絡程式設計】TCPIP-5-UDP

目錄

  • ​​前言​​
  • ​5. UDP 網絡程式設計​
  • ​​5.1 UDP 的工作原理​​
  • ​​5.2 UDP 的高效性​​
  • ​5.3 實作 UDP 服務端/用戶端​
  • ​​5.3.1 概念​​
  • ​​5.3.2 UDP 的資料 I/O 函數​​
  • ​​5.3.3 UDP 用戶端位址配置設定​​
  • ​​5.4 UDP 的資料傳輸特性​​
  • ​​5.5 UDP 調用 connect 函數​​
  • ​​參考​​

前言

說明:

  • demo 基于 Linux。

5. UDP 網絡程式設計

UDP 是無連接配接的,不需要建立連接配接。

5.1 UDP 的工作原理

參考圖:

【網絡程式設計】TCPIP-5-UDP

主機B的資料包中包含目的主機的IP+端口号。

其中IP是把資料的目的主機位址,端口号是目的主機對用的程式。

路由器小知識:

  • IP:主機位址。如目的IP,每個路由器都會檢查IP,若在本地,就往本地端口轉發,若不在,就往外端口發。(在轉發過程中是不會變的)
  • MAC:裝置位址。目的MAC,下一個實體連接配接的裝置位址。(在轉發過程中可能會變,因為參考的是下一個,而不是最終目的)

5.2 UDP 的高效性

UDP 不需要建立連接配接和響應校驗,實時性比 TCP 高。

一般用在網絡實時傳遞的視訊或者音頻中,因為丢失部分資料也不會影響太大。

5.3 實作 UDP 服務端/用戶端

5.3.1 概念

UDP 伺服器和用戶端均隻需一個套接字:

  • TCP 中,套接字之間應該是一對一的關系。
  • UDP 中,不管是伺服器端還是用戶端都隻需要 1 個套接字,就可以任意傳輸。

如圖:

【網絡程式設計】TCPIP-5-UDP

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和端口号,耗時。

繼續閱讀