
??想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):??
??51CTO 開(kāi)源基礎(chǔ)軟件社區(qū)??
??https://ost.51cto.com??
一、前言
在net_demo.h文件里,testFun是什么?它是怎么選擇客戶端還是服務(wù)端再跳轉(zhuǎn)到tcp_client_test.c文件執(zhí)行TcpClientTest()函數(shù)的呢?

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

開(kāi)發(fā)流程圖:
UDP:

TCP:

三、代碼
1、把連接WiFi的代碼搬過(guò)來(lái)

在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};
// 準(zhǔn)備AP的配置參數(shù)
strcpy(config.ssid, PARAM_HOTSPOT_SSID);
strcpy(config.preSharedKey, PARAM_HOTSPOT_PSK);
config.securityType = PARAM_HOTSPOT_TYPE;
osDelay(10);
//開(kāi)始連接
int netId = ConnectToHotspot(&config);
記得在udp文件夾里的BUILD.gn編譯WiFi的.c文件:

2、簡(jiǎn)單的UDP
// 1.創(chuàng)建udp
int sock_fd;
int ret;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0); //SOCK_DGRAM:UDP類(lèi)型的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);
//內(nèi)存初始化
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"); // 是將一個(gè)點(diǎn)分十進(jìn)制的IP轉(zhuǎn)換成一個(gè)長(zhǎng)整數(shù)型數(shù)(u_long類(lèi)型)
send_addr.sin_port = htons(1234); // 端口號(hào),從主機(jī)字節(jié)序轉(zhuǎn)為網(wǎng)絡(luò)字節(jié)序
// 3.配置發(fā)送消息
char *msg = "hello world";
while (1)
{
//4.發(fā)送
// UDP socket 是 “無(wú)連接的” ,因此每次發(fā)送都必須先指定目標(biāo)主機(jī)和端口,主機(jī)可以是多播地址
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 是 “無(wú)連接的” ,因此每次接收時(shí)前并不知道消息來(lái)自何處,通過(guò) fromAddr 參數(shù)可以得到發(fā)送方的信息(主機(jī)、端口號(hào))
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)); //將網(wǎng)絡(luò)地址轉(zhuǎn)換成“.”點(diǎn)隔的字符串格式。將一個(gè)16位數(shù)由網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換為主機(jī)字節(jié)順序
}
代碼中主要的函數(shù)與TCP一樣都是用socket,已在??上文,【FFH】學(xué)習(xí)設(shè)備開(kāi)發(fā)之Hi3861-TCPclient-開(kāi)關(guān)燈??解釋。
兩者開(kāi)發(fā)過(guò)程的區(qū)別:
① 創(chuàng)建socket的類(lèi)型type=SOCK_STREAM(TCP)/SOCK_DGRAM(UDP)。
int sockfd = socket(AF_INET, type, 0);
②TCP需要多一步與主機(jī)連接,而UDP不需要。

3、代碼運(yùn)行結(jié)果
netcat,-u代表主機(jī)使用UDP協(xié)議傳輸,-l 開(kāi)啟監(jiān)聽(tīng),-p指定端口。

四、UDP廣播
因?yàn)閁DP是無(wú)連接的,并且一對(duì)多發(fā)送消息,自然而然就具有廣播消息的功能。
下面給出主要代碼:
// 1.創(chuàng)建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.設(shè)置廣播模式
int yes = 1;
ret = setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&yes, sizeof(yes));
// SOL_SOCKET表示給當(dāng)前的socketfd,賦予SO_BROADCAST廣播權(quán)限
if (ret == -1)
{
perror("setsockopt error\r\n");
return;
}
// 3.配置廣播地址
struct sockaddr_in broadcast_addr;
socklen_t broadcast_addr_len = sizeof(broadcast_addr);
//初始化地址內(nèi)存
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:要發(fā)送給所有主機(jī)的地址
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);
}
設(shè)置廣播模式的函數(shù):
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);
第一個(gè)參數(shù)socket是套接字描述符。
第二個(gè)參數(shù)level是被設(shè)置的選項(xiàng)的級(jí)別,如果想要在套接字級(jí)別上設(shè)置選項(xiàng),就必須把level設(shè)置為 SOL_SOCKET。
第三個(gè)參數(shù)option_name指定準(zhǔn)備設(shè)置的選項(xiàng),option_name可以有哪些取值,這取決于level。當(dāng)level取SOL_SOCKET時(shí),option_name可取
SO_DEBUG,打開(kāi)或關(guān)閉調(diào)試信息。
SO_REUSEADDR,打開(kāi)或關(guān)閉地址復(fù)用功能。 當(dāng)option_value不等于0時(shí),打開(kāi),否則,關(guān)閉。
SO_DONTROUTE,打開(kāi)或關(guān)閉路由查找功能。 當(dāng)option_value不等于0時(shí),打開(kāi),否則,關(guān)閉。
SO_BROADCAST,允許或禁止發(fā)送廣播數(shù)據(jù)。 當(dāng)option_value不等于0時(shí),允許,否則,禁止。
不需要確定發(fā)給哪個(gè)特定的主機(jī),所以要設(shè)置廣播的地址為所有。
broadcast_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); //INADDR_BROADCAST:要發(fā)送給所有主機(jī)的地址
??想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):??
??51CTO 開(kāi)源基礎(chǔ)軟件社區(qū)??
??https://ost.51cto.com??。