OpenHarmony轻量设备获取网络中设备IP方式-组播

news2024/11/27 22:28:58

一、简介

​ 在Openharmony的轻量和小型系统中,受限于cpu与内存资源等原因,网络协议一般使用lwip的实现。而且受限资源影响,lwip的socket数与端口数都通过宏定义控制在7-8个以内。在物联IOT实际应用中,经常会出现多台IOT设备在未知对方IP的状态下,需要对多台IOT设备进行控制与通信。此时可以通过组播的方式获取对方设备的IP地址,后续就可以对对方设备进行控制与通信。

另:

1.在IOT设备通信中,关于可信安全的问题,在Openharmony中可以使用hichain的可信安全进行认证。

2.在IOT设备通信中,Openharmony除了采用传统的组播与广播方式控制其它的IOT设备,还可以选择Openharmony的软总线来实现,而软总线的可信认证即是采用的hichain进行安全认证。

此两课题有时间再详述。

​ 本文通过代码示例,在两台不同的L0的轻量系统中通过组播获取对方设备的IP后,发送指定的信息。

二、组播

​ 组播指的是报文从一个源发出,被转发到一组特定的接收者,相同的报文在每条链路上最多有一份。相较于传统的单播和广播,组播可以有效地节约网络带宽、降低网络负载。

​ 组播是一种允许一个或多个组播源发送同一报文到多个接收者的技术。组播源将一份报文发送到特定的组播地址,组播地址不同于单播地址,它不属于特定某个主机,而是属于一组主机。一个组播地址表示一个群组,需要接收组播报文的接收者都加入这个群组。

​ 广播传输数据源必须与用户在同一网段,组播可以跨网段传输。

​ 为了使组播源和组播组成员进行通信,需要提供网络层组播使用的IP组播地址。

2.1 组播地址

​ D类地址空间分配给IPv4组播使用,地址范围从224.0.0.0到239.255.255.255.

​ 224.0.0.0~224.0.0.255 永久组地址,为路由协议预留的IP地址,用于标识一组特定的网络设备,供路由协议 、拓扑查找等使用,不用于组播转发。

​ 224.0.1.0~239.255.255.255 均可作为组播地址使用,只是组范围有所不同,具体范围使用可以查找IPv4组播地址规范。

三、Openharmony的两类L0设备

3.1 拓维Niobe开发板

  • 代码分支

    https://gitee.com/talkweb_oh/niobe

  • wifi连接示例代码

​ https://gitee.com/talkweb_oh/niobe/tree/master/applications/app/TW301_Network_wifista

  • udp示例

    https://gitee.com/talkweb_oh/niobe/tree/master/applications/app/TW305_Network_udpclient

    https://gitee.com/talkweb_oh/niobe/tree/master/applications/app/TW305_Network_udpserver

  • tcp示例

    https://gitee.com/talkweb_oh/niobe/tree/master/applications/app/TW304_Network_tcpclient

    https://gitee.com/talkweb_oh/niobe/tree/master/applications/app/TW304_Network_tcpserver

3.2 小熊派BearPi-HM Nano开发板

  • 代码分支

​ https://gitee.com/bearpi/bearpi-hm_nano

  • wifi连接示例

    https://gitee.com/bearpi/bearpi-hm_nano/tree/master/applications/BearPi/BearPi-HM_Nano/sample/D2_iot_wifi_sta_connect

  • udp示例

    https://gitee.com/bearpi/bearpi-hm_nano/tree/master/applications/BearPi/BearPi-HM_Nano/sample/D3_iot_udp_client

  • tdp示例

    https://gitee.com/bearpi/bearpi-hm_nano/tree/master/applications/BearPi/BearPi-HM_Nano/sample/D4_iot_tcp_server

四、组播获取代码示例

4.1 常量定义

constexpr const char* MULTICAST_DATA = "multicast"; // 组播测试数据
constexpr const char* MULTICAST_IP = "234.3.3.3";   // 组播地址
constexpr uint32_t MULTICAST_PORT = 9000;		    // 组播端口
constexpr uint32_t SEND_DATA_PORT = 9001;		    // 发送数据端口

4.2 组播源

void UdpSendThread(void)
{
    wifiConnect("wifi name", "wifi password");
    int udp_socket = CreateUdpSocket(); // 创建udp socket略过...
    if (udp_socket >= 0) {
        printf("CreateUdpSocket udp_socket:%d\n", udp_socket);
        // 指定发送的组播地址和端口
        struct sockaddr_in mcast_addr;
        memset(&mcast_addr, 0, sizeof(mcast_addr));
        mcast_addr.sin_family = AF_INET;
        mcast_addr.sin_addr.s_addr = inet_addr(MULTICAST_IP);
        mcast_addr.sin_port = htons(MULTICAST_PORT);
    }
    while (true) {
        // 发送测试数据multicast
        int sendLength = sendto(udp_socket, MULTICAST_DATA, 9, 0, (struct socketaddr*)&mcast_addr, sizeof(mcast_addr));
        if (sendLength < 0) {
            printf("sendto udp_socket:%d fail,sendLength:%d", udp_socket, sendLength);
        } else {
            g_SendUdpSucc++;	
            if (g_SendUdpSucc >= 10) {
                break;	// 此为测试,组播报文只发送10次,实际项目中应在从接收者收到应答数据包时,停止发送。
            }
        }
        Delay(1000);
    }
    close(udp_socket);
}

4.3 组播接收端

void UdpRecvThread(void)
{
	wifiConnect("wifi name", "wifi password");
    struct sockaddr_in server_socket;
    int upd_socket = CreateUdpSocket();
    if (udp_socket >= 0) {
        bzero(&server_sock, sizeof(server_sock));
        server_sock.sin_family = AF_INET;
        server_sock.sin_addr.s_addr = htonl(INADDR_ANY);
        server_sock.sin_port = htons(MULTICAST_PORT);
        
        // 调用bind绑定socket地址
        if (bind(udp_socket, (struct sockaddr*)&server_socket, sizeof(struct sockaddr)) == -1) {
            perror("bind error\r\n");
            closesocket(udp_socket);
            exit(1);
        }
        struct ip_mreq mreq;
        // 加入组播,接收组播信息
        mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP);
        mreq.imr_interface.s_addr = htonl(INADDR_ANY);
        int error = setsocket(udp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
        if (error < 0) {
            printf("setsocket fail error:%d\r\n", error);
            closesocket(udp_socket);
            exit(1);
        }
        while (true) {
            printf("select wait....\r\n");
            struct sockaddr_in recvAddr;
            memset(recvBuff, 0, sizeof(recvBuff));
            memset(&recvAddr, 0, sizeof(struct sockaddr));
            int sinSize = sizeof(struct sockaddr_in);
            int ret = recvfrom(udp_socket, recvBuff, sizeof(recvBuff)) - 1, 0, (struct sockaddr*)&recvAddr, (socklen_t *)&sinSize);
            printf("recv from UDP client IP:%s\r\n", inet_ntoa(recvAddr.sin_addr));
            printf("recv data:%s\r\n", recvBuff); // 组播测试数据"multicast"
            osDelay(10);
            // 校验组播数据
            if (strlen(recvBuff) == 9 && memcmp(recvBuff, MULTICAST_DATA, 10) == 0) {
                memset(remoteIp, 0, IPADDR_LEN);
                strcpy(remoteIp, inet_ntoa(recvAddr.sin_addr));
                printf("get peer device IP:%s\r\n", remoteIp);
                break;
            }
		}
        closesocket(udp_socket);
    }
}

4.4 通过获取的远端地址remoteIp,发送数据到对端设备

void TcpSendThread()
{
    struct sockaddr_in sendAddr;
    socklen_t addrLen = sizeof(sendAddr);
    // tcp socket
    int sock_fd = -1;
    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        exit(1);
    }
    
    sendAddr.sin_family = AF_INET;
    sendAddr.sin_port = htons(SEND_DATA_PORT);
    sendAddr.sin_addr.s_addr = inet_addr(remoteIp);
    
    int ret = connect(sock_fd, (struct sockaddr*)&sendAddr, sizeof(sendAddr));
    if (ret != 0) {
        printf("connect server fail ret:%d\r\n", ret);
        closesocket(sock_fd);
        exit(1);
    }
    
    while (true) {
        bzero(recvBuff, sizeof(recvBuff));
        // 发送数据
        printf("send remote device %s:%d=>data:%s\r\n",remoteIp, SEND_DATA_PORT, sendData);
        sendto(sock_fd, sendData, strlen(sendData), 0, (struct sockaddr*)&sendAddr, addrLen);
        
        sleep(10);
        // 接收服务端返回应答数据
        memset(recvBuff, 0, sizeof(recvBuff));
        recvfrom(sock_fd, recvBuff, 0, (struct socketaddr*)&sendAddr, &addrLen);
        printf("recv remote device %s:%d=>data:%s\r\n", remoteIp, SEND_DATA_PORT, recvBuff);
    }
    closesocket(sock_fd);
}

4.5 组播端接受对端数据

void TcpRecvThread()
{
    struct sockaddr_in serverSock, clientSock, *cliAddr;
    int sinSize = sizeof(struct sockaddr_in);
    int sock_fd = -1;
    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        exit(1);
    }
    bzero(&serverSock, sizeof(serverSock));
    serverSock.sin_family = AF_INET;
    serverSock.sin_addr.s_addr = htonl(INADDR_ANY);
    serverSock.sin_port = htons(SEND_DATA_PORT);
    
    if (bind(sock_fd, (struct sockaddr*)&serverSock, sizeof(struct sockaddr)) == -1) {
        printf("bind fail\r\n");
        closesocket(sock_fd);
        exit(1);
    }
    // listen监听
    if (listen(sock_fd, TCP_BACKLOG) == -1) {
        printf("listen fail\r\n");
        exit(1);
    }
    
    while (true) {
        if ((new_fd = accept(sock_fd, (struct sockaddr*)&clientSock,(socklen_t*)&sinSize)) == -1) {
            printf("accept fail\r\n");
        	closesocket(sock_fd);
            exit(1);
        }
        cliAddr = malloc(sizeof(struct sockaddr));
        if (cliAddr != NULL) {
            memcpy(cliAddr, &clientSock, sizeof(struct sockaddr));
        }
        ssize ret;
        while (true) {
            if ((ret = recv(new_fd, recvBuff, sizeof(recvBuff), 0)) == -1) {
                printf("recv fail\r\n");
            }
            printf("recv data:%s\r\n", recvBuff);
            sleep(2);
            
            if ((ret = send(new_fd, sendBuff, strlen(sendBuff) + 1, 0)) == -1) {
                printf("send fail\r\n");
            }
            printf("send data:%s\r\n", sendBuff);
            sleep(2);
        }
        printf("server exit\r\n");
        close(new_fd);
    }
    closesocket(sock_fd);
}

4.6 demo打印结果显示

拓维Niobe开发板:
在这里插入图片描述
小熊派BearPi-HM Nano开发板:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/777992.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL 的缓存介绍

点击上方↑“追梦 Java”关注&#xff0c;一起追梦&#xff01; 一般情况下&#xff0c;我们不会用到数据库自带的缓存&#xff0c;所以 MySQL 默认是不开启缓存的。只有以读为主的业务&#xff0c;数据不变化的情况下&#xff0c;可以开启数据库的缓存。 查看缓存是否开启&…

NAT技术是什么?谈谈它的实现方式、优缺点以及作用

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 前言 随着网络的不断发展&#xff0c;网络的应用也越来越多&#xff0c;有限的IPV4地址就显得不怎么够用&#xff0c;所以出现了NAT技术&…

Linux系统下的文件权限

目录 一&#xff1a;关于人的权限 二&#xff1a;关于文件本身的权限 三&#xff1a;修改文件属性 chmod 身份/-rwx chmod 八进制数 四、更改文件所有人 ​编辑五、umask权限掩码 umask权限掩码的计算 Linux系统不以文件后缀来区分文件类型&#xff0c;当我们使用ls -l命…

springCloud整合Nacos当配置中心和注册中心

前言 本文主要介绍SpringCloud使用Nacos当配置中心和注册中心&#xff0c;通过一个简单的Demo学习Naocs的基本配置以及不同微服务之间通过RestTemplate 及openfeign完成接口调用。 Nacos的安装配置 Nacos的学习资源主要有以下两个 Nacos管网 Nacos管网Nacos GitHub NacosGi…

智力差异性对课程的影响

“收藏从未停止&#xff0c;练习从未开始”&#xff0c;或许有那么一些好题好方法&#xff0c;在被你选中收藏后却遗忘在收藏夹里积起了灰&#xff1f;今天请务必打开你沉甸甸的收藏重新回顾&#xff0c;分享一下那些曾让你拍案叫绝的好东西吧&#xff01; 你可以从以下几个方…

debian/PVE安装好后拔显卡后连接不了网络

目录 前因 原因 解决办法 注意 需要主板bios支持跳过显卡自检 解决好主板bios问题然后再继续解决系统问题 前因 前几天装了个​Proxmox​ ve当做一个服务器7*24开机 但是由于转好系统后&#xff0c;显卡就不需要了 加上它耗电的原因&#xff08;我的gtx650平时空载有1…

JavaFx开发图形化界面常用颜色对照表

LightPink 浅粉色 #FFB6C1 255,182,193 Pink 粉红 #FFC0CB 255,192,203 Crimson 猩红 #DC143C 220,20,60 LavenderBlush …

【PostgreSQL内核学习(六)—— 工具使用学习】

工具使用学习 工具使用学习安装中出现的问题 声明&#xff1a;本文的工具学习内容来自于《小宇带你学pg内核分析》 工具的代码仓库链接为&#xff1a; https://github.com/shenyuflying/pgNodeGraph 此外&#xff0c;我还参考了以下文章&#xff1a; https://rng-songbaobao.bl…

【腾讯云 Cloud Studio 实战训练营】使用Cloud Studio制作蛋仔派对兑换码工具

目录 &#x1f373;前言&#x1f373;实验介绍&#x1f373;产品介绍&#x1f373;抓包分析&#x1f603;登录分析&#x1f603;&#x1f603;第一步&#xff0c;获取验证码&#x1f603;&#x1f603;第二步&#xff0c;保存验证码&#x1f603;&#x1f603;第三步&#xff0…

RocketMQ教程-(4)-领域模型概述

Apache RocketMQ 是一款典型的分布式架构下的中间件产品&#xff0c;使用异步通信方式和发布订阅的消息传输模型。通信方式和传输模型的具体说明&#xff0c;请参见下文通信方式介绍和消息传输模型介绍。 Apache RocketMQ 产品具备异步通信的优势&#xff0c;系统拓扑简单、上下…

ffmpeg中filter_query_formats函数解析

ffmpeg中filter_query_formats主要起一个pix fmt引用指定的功能。 下下结论&#xff1a; 先看几个结构体定义&#xff1a; //删除了一些与本次分析不必要的成员 struct AVFilterLink {AVFilterContext *src; ///< source filterAVFilterPad *srcpad; ///<…

C语言程序运行需要的两大环境《C语言进阶》

目录 程序的翻译环境和执行环境 翻译环境分为两部分&#xff0c;编译链接 第一步&#xff1a;预编译&#xff08;预处理&#xff09; 第二步&#xff0c;编译 第三步&#xff1a;汇编 关于运行环境分为四点&#xff1a; 关于链接库 程序的翻译环境和执行环境 在 ANSI C(标…

三十章:Segmenter:Transformer for Semantic Segmentation ——分割器:用于语义分割的Transformer

0.摘要 图像分割在单个图像块的级别上经常存在歧义&#xff0c;并需要上下文信息来达到标签一致性。在本文中&#xff0c;我们介绍了一种用于语义分割的Transformer模型- Segmenter。与基于卷积的方法相比&#xff0c;我们的方法允许在第一层和整个网络中对全局上下文进行建模。…

C语言 与 C++ 通讯录对比实现(附源码)

目录 1.通讯录的基本框架 C语言版 C版 2.增加联系人 C语言版 C版 3.删除联系人 C语言版 C版 4.查找与打印联系人 C语言版 C版 5.修改联系人 C语言版 C版 6.排序联系人 C语言版 C版 7.其他 8.总结 本文章将对C语言、C版本的通讯录进行对比实现。其中C版本引入大量C语言没有的特性…

macOS 源码编译 Percona XtraBackup

percona-xtrabackup-2.4.28.tar.gz安装依赖 ╰─➤ brew install cmake ╰─➤ cmake --version cmake version 3.27.0brew 安装 ╰─➤ brew update╰─➤ brew search xtrabackup > Formulae percona-xtrabackup╰─➤ brew install percona-xtrabackup╰─➤ xtr…

提升 API 可靠性的五种方法

API 在我们的数字世界中发挥着关键的作用&#xff0c;使各种不同的应用能够相互通信。然而&#xff0c;这些 API 的可靠性是保证依赖它们的应用程序功能正常、性能稳定的关键因素。本文&#xff0c;我们将探讨提高 API 可靠性的五种主要策略。 1.全面测试 要确保 API 的可靠性…

Kubernetes 入门

Kubernetes 入门 文章目录 Kubernetes 入门一、Kubernetes 环境部署1. 环境准备2. 测试部署 Nginx3. 在任意节点使用 kubectl 二、深入 pod1. 使用配置文件部署引用2. 探针 三、资源调度1.标签与选择器2.Deployment3.StatefulSet4.DaemonSet5.HPA6. Service7. Ingress8. 配置管…

【Unity】为角色添加动画

如何添加动画 在Animations的AnimationClips文件夹下自己为角色创建一个文件夹 为角色添加Animator 然后选中上面创建的文件夹&#xff0c;拖动到上图中的Controller中 点击最上方任务栏的Window>Animation>Animation&#xff0c;这会弹出一个Animation窗口 该窗口存在时…

算法leetcode|63. 不同路径 II(rust重拳出击)

文章目录 63. 不同路径 II&#xff1a;样例 1&#xff1a;样例 2&#xff1a;提示&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 63. 不同路径 II&#xff1a; 一个机器人位于一个 m x n 网格…

SQL_SQL_常见面试问题

问题类型 &#xff1a;SQL优化 问题描述 &#xff1a;用户浏览日志&#xff08;date, user_id, video_id&#xff09;, 统计 2020.03.29 观看不同视频个数的前5名 user_id。 思路 &#xff1a;主要注意预计算&#xff0c;避免直接去重 解决方案 &#xff1a; Hive_HQL_Hive…