用戶數據報協議(UDP):UDP(用戶數據報協議)是一個簡單的面向數據報的傳輸層協議。提供的是非面向連接的、不可靠的數據流傳輸。UDP不提供可靠性,也不提供報文到達確認、排序以及流量控制等功能。

??想了解更多關于開源的內容,請訪問:??
??51CTO 開源基礎軟件社區??
??https://ost.51cto.com??
一、前言
在net_demo.h文件里,testFun是什么?它是怎么選擇客戶端還是服務端再跳轉到tcp_client_test.c文件執行TcpClientTest()函數的呢?

搞不懂別人高深的代碼,于是后面就仿照官方代碼,自己寫了一份簡單的代碼,并且學習另一種傳輸協議UDP。
二、UDP簡介
1、定義
用戶數據報協議(UDP):UDP(用戶數據報協議)是一個簡單的面向數據報的傳輸層協議。提供的是非面向連接的、不可靠的數據流傳輸。UDP不提供可靠性,也不提供報文到達確認、排序以及流量控制等功能。它只是把應用程序傳給IP層的數據報發送出去,但是并不能保證它們能到達目的地。因此報文可能會丟失、重復以及亂序等。但由于UDP在傳輸數據報前不用在客戶和服務器之間建立一個連接,且沒有超時重發等機制,故而傳輸速度很快。
2、復習一下TCP
“面向連接的TCP”就是在正式通信前必須要與對方建立起連接。TCP協議是一種可靠的、一對一的、面向有連接的通信協議。
3、UDP/TCP區別
TCP和UDP最大的區別就是:TCP是面向連接的,UDP是無連接的。TCP協議和UDP協議各有所長、各有所短,適用于不同要求的通信環境。TCP協議和UDP協議之間的差別如下表所示。
在實際的使用中,TCP主要應用于文件傳輸精確性相對要求較高且不是很緊急的情景,比如電子郵件、遠程登錄等。有時在這些應用場景下即使丟失一兩個字節也會造成不可挽回的錯誤,所以這些場景中一般都使用TCP傳輸協議。由于UDP可以提高傳輸效率,所以UDP被廣泛應用于數據量大且精確性要求不高的數據傳輸,比如我們平常在網站上觀看視頻或者聽音樂的時候應用的基本上都是UDP傳輸協議。

開發流程圖:
UDP:

TCP:

三、代碼
1、把連接WiFi的代碼搬過來

在net_params.h文件里配置WiFi:
#ifndef PARAM_HOTSPOT_SSID
#define PARAM_HOTSPOT_SSID "Fsr" // your AP SSID
#endif
#ifndef PARAM_HOTSPOT_PSK
#define PARAM_HOTSPOT_PSK "12345678" // your AP PSK
#endif
連接WiFi:
//連接wifi
WifiDeviceConfig config = {0};
// 準備AP的配置參數
strcpy(config.ssid, PARAM_HOTSPOT_SSID);
strcpy(config.preSharedKey, PARAM_HOTSPOT_PSK);
config.securityType = PARAM_HOTSPOT_TYPE;
osDelay(10);
//開始連接
int netId = ConnectToHotspot(&config);
記得在udp文件夾里的BUILD.gn編譯WiFi的.c文件:

2、簡單的UDP
// 1.創建udp
int sock_fd;
int ret;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0); //SOCK_DGRAM:UDP類型的socket
if (sock_fd < 0)
{
perror("sock_fd create error\r\n");
return;
}
// 2.配置地址
struct sockaddr_in send_addr;
socklen_t send_addr_len = sizeof(send_addr);
//內存初始化
memset((void *)&send_addr, 0, send_addr_len);
send_addr.sin_family = AF_INET;
send_addr.sin_addr.s_addr = inet_addr("192.168.11.41"); // 是將一個點分十進制的IP轉換成一個長整數型數(u_long類型)
send_addr.sin_port = htons(1234); // 端口號,從主機字節序轉為網絡字節序
// 3.配置發送消息
char *msg = "hello world";
while (1)
{
//4.發送
// UDP socket 是 “無連接的” ,因此每次發送都必須先指定目標主機和端口,主機可以是多播地址
ret = sendto(sock_fd, msg, strlen(msg), 0, (struct sockaddr *)&send_addr, send_addr_len);
printf("send UDP message {%s}done!\r\n", msg);
usleep(1 * 1000 * 1000);
// 5.接收
struct sockaddr_in fromAddr = {0};
socklen_t fromLen = sizeof(fromAddr);
// UDP socket 是 “無連接的” ,因此每次接收時前并不知道消息來自何處,通過 fromAddr 參數可以得到發送方的信息(主機、端口號)
ret = recvfrom(sock_fd, &response, sizeof(response), 0, (struct sockaddr *)&fromAddr, &fromLen);
if (ret <= 0)
{
printf("recvfrom failed or abort, %ld!\r\n", ret);
}
response[ret] = '\0';
printf("recv UDP message {%s} %ld done!\r\n", response, ret);
printf("peer info: ipaddr = %s, port = %d\r\n", inet_ntoa(fromAddr.sin_addr), ntohs(fromAddr.sin_port)); //將網絡地址轉換成“.”點隔的字符串格式。將一個16位數由網絡字節順序轉換為主機字節順序
}
代碼中主要的函數與TCP一樣都是用socket,已在??上文,【FFH】學習設備開發之Hi3861-TCPclient-開關燈??解釋。
兩者開發過程的區別:
① 創建socket的類型type=SOCK_STREAM(TCP)/SOCK_DGRAM(UDP)。
int sockfd = socket(AF_INET, type, 0);
②TCP需要多一步與主機連接,而UDP不需要。

3、代碼運行結果
netcat,-u代表主機使用UDP協議傳輸,-l 開啟監聽,-p指定端口。

四、UDP廣播
因為UDP是無連接的,并且一對多發送消息,自然而然就具有廣播消息的功能。
下面給出主要代碼:
// 1.創建udp
int sock_fd;
int ret;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
{
perror("sock_fd create error\r\n");
return;
}
// 2.設置廣播模式
int yes = 1;
ret = setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&yes, sizeof(yes));
// SOL_SOCKET表示給當前的socketfd,賦予SO_BROADCAST廣播權限
if (ret == -1)
{
perror("setsockopt error\r\n");
return;
}
// 3.配置廣播地址
struct sockaddr_in broadcast_addr;
socklen_t broadcast_addr_len = sizeof(broadcast_addr);
//初始化地址內存
memset((void *)&broadcast_addr, 0, broadcast_addr_len);
broadcast_addr.sin_family = AF_INET;
broadcast_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); //INADDR_BROADCAST:要發送給所有主機的地址
broadcast_addr.sin_port = htons(1234);
char *msg = "hello ";
while (1)
{
ret = sendto(sock_fd, msg, strlen(msg), 0, (struct sockaddr *)&broadcast_addr, broadcast_addr_len);
usleep(1 * 1000 * 1000);
}
設置廣播模式的函數:
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&yes, sizeof(yes));
int setsockopt( int socket, int level, int option_name,const void *option_value, size_t ,ption_len);
第一個參數socket是套接字描述符。
第二個參數level是被設置的選項的級別,如果想要在套接字級別上設置選項,就必須把level設置為 SOL_SOCKET。
第三個參數option_name指定準備設置的選項,option_name可以有哪些取值,這取決于level。當level取SOL_SOCKET時,option_name可取
SO_DEBUG,打開或關閉調試信息。
SO_REUSEADDR,打開或關閉地址復用功能。 當option_value不等于0時,打開,否則,關閉。
SO_DONTROUTE,打開或關閉路由查找功能。 當option_value不等于0時,打開,否則,關閉。
SO_BROADCAST,允許或禁止發送廣播數據。 當option_value不等于0時,允許,否則,禁止。
不需要確定發給哪個特定的主機,所以要設置廣播的地址為所有。
broadcast_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); //INADDR_BROADCAST:要發送給所有主機的地址
??想了解更多關于開源的內容,請訪問:??
??51CTO 開源基礎軟件社區??
??https://ost.51cto.com??。