天天看點

#盲盒+碼#【FFH】學習裝置開發之Hi3861-UDP-廣播一、前言二、UDP簡介三、代碼四、UDP廣播

(目錄)

【本文正在參加「盲盒」+碼有獎征文活動】:https://ost.51cto.com/posts/19288

一、前言

上文,【FFH】學習裝置開發之Hi3861-TCPclient-開關燈留下了的疑惑:

在net_demo.h檔案裡,testFun是什麼?它是怎麼選擇用戶端還是服務端再跳轉到tcp_client_test.c檔案執行TcpClientTest()函數的呢?

#盲盒+碼#【FFH】學習裝置開發之Hi3861-UDP-廣播一、前言二、UDP簡介三、代碼四、UDP廣播

搞不懂别人高深的代碼,于是後面就仿照官方代碼,自己寫了一份簡單的代碼,并且學習另一種傳輸協定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傳輸協定。

#盲盒+碼#【FFH】學習裝置開發之Hi3861-UDP-廣播一、前言二、UDP簡介三、代碼四、UDP廣播

開發流程圖:

UDP👇

#盲盒+碼#【FFH】學習裝置開發之Hi3861-UDP-廣播一、前言二、UDP簡介三、代碼四、UDP廣播

TCP👇

#盲盒+碼#【FFH】學習裝置開發之Hi3861-UDP-廣播一、前言二、UDP簡介三、代碼四、UDP廣播

三、代碼

1.把連接配接WiFi的代碼搬過來

#盲盒+碼#【FFH】學習裝置開發之Hi3861-UDP-廣播一、前言二、UDP簡介三、代碼四、UDP廣播

在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檔案==

#盲盒+碼#【FFH】學習裝置開發之Hi3861-UDP-廣播一、前言二、UDP簡介三、代碼四、UDP廣播

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不需要

#盲盒+碼#【FFH】學習裝置開發之Hi3861-UDP-廣播一、前言二、UDP簡介三、代碼四、UDP廣播

3.代碼運作結果

netcat,-u代表主機使用UDP協定傳輸,-l 開啟監聽,-p指定端口

#盲盒+碼#【FFH】學習裝置開發之Hi3861-UDP-廣播一、前言二、UDP簡介三、代碼四、UDP廣播

四、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:要發送給所有主機的位址
           

本文作者:Z·y

繼續閱讀