HarmonyOS开发实战:UDP通讯示例规范

news2024/10/6 6:10:20

 1. UDP简介

UDP协议是传输层协议的一种,它不需要建立连接,是不可靠、无序的,相对于TCP协议报文更简单,在特定场景下有更高的数据传输效率,在现代的网络通讯中有广泛的应用,以最新的HTTP/3为例,它是基于QUIC(Quick UDP Internet Connections)协议的,从协议名字就不难看出,这个基础协议也是UDP的,现在就扔掉对UDP的偏见,深入、彻底的了解UDP,从而更好地掌握鸿蒙网络编程。

2. UDP通讯的常用方法

鸿蒙封装的UDP操作类位于模块socket中,使用如下的方式导入:

import socket from '@ohos.net.socket';

        socket模块包括了众多的UDP操作方法,就本文而言,重点需要掌握的是如下四个:

1)constructUDPSocketInstance(): UDPSocket

创建一个UDPSocket对象,在使用UDPSocket的方法以前需要创建该对象。

2)bind(address: NetAddress): Promise<void>

绑定IP地址和端口,端口可以指定或由系统随机分配,可以使用0.0.0.0表示本机IP地址;使用Promise方式作为异步方法。

3)send(options: UDPSendOptions): Promise<void>

通过UDPSocket连接发送数据,因为UDP是无连接的,给定IP地址和端口即可发送数据,不用考虑对方是否真的存在;使用Promise方式作为异步方法。

4)on(type: 'message', callback: Callback<{message: ArrayBuffer, remoteInfo: SocketRemoteInfo}>): void

订阅UDPSocket连接的接收消息事件,当套接字接收到消息时触发该事件,其中message表示接收到的消息,remoteInfo是发送方信息;使用callback方式作为异步方法。

除了这些方法,为了得到系统IP地址,还使用了wifiManager模块的getIpInfo方法,该方法并不是UDP通讯必须的,就不详细介绍了。

3. UDP通讯示例

为演示UDP通讯的方式,本示例实现了一个使用UDP协议发送、接收消息的功能,运行后的界面如下所示:

cke_203578.png

下面详细介绍创建该应用的步骤。

步骤1:创建Empty Ability项目。

步骤2:在module.json5配置文件加上对权限的声明:

"requestPermissions": [

      {

        "name": "ohos.permission.INTERNET"

      },

      {

        "name": "ohos.permission.GET_WIFI_INFO"

      }

    ]

这里分别添加了访问互联网和访问WIFI信息的权限。

步骤3:在Index.ets文件里添加如下的代码:

import socket from '@ohos.net.socket';
import wifiManager from '@ohos.wifiManager';
import systemDateTime from '@ohos.systemDateTime';
import util from '@ohos.util';

//执行UDP通讯的对象
let udpSocket = socket.constructUDPSocketInstance();

//说明:本地的IP地址不是必须知道的,绑定时绑定到IP:0.0.0.0即可,显示本地IP地址的目的是方便对方发送信息过来
//本地IP的数值形式
let ipNum = wifiManager.getIpInfo().ipAddress
//本地IP的字符串形式
let localIp = (ipNum >>> 24) + '.' + (ipNum >> 16 & 0xFF) + '.' + (ipNum >> 8 & 0xFF) + '.' + (ipNum & 0xFF);

@Entry
@Component
struct Index {
  //连接、通讯历史记录
  @State msgHistory: string = ''
  //要发送的信息
  @State sendMsg: string = ''
  //本地端口
  @State localPort: number = 9990
  //目的IP地址
  @State targetIp: string = "0.0.0.0"
  //目的端口
  @State targetPort: number = 9990
  scroller: Scroller = new Scroller()

  build() {
    Row() {
      Column() {
        Text("UDP通讯示例")
          .fontSize(14)
          .fontWeight(FontWeight.Bold)
          .width('100%')
          .textAlign(TextAlign.Center)
          .padding(10)

        Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
          Text("本地IP和端口:")
            .width(110)
            .fontSize(14)
            .flexGrow(0)
          Text(localIp)
            .width(80)
            .fontSize(14)
            .flexGrow(0)
          TextInput({ text: this.localPort.toString() })
            .type(InputType.Number)
            .onChange((value) => {
              this.localPort = parseInt(value)
            })
            .width(100)
            .flexGrow(3)

          Button("绑定")
            .onClick(() => {
              this.bind2Port()
            })
            .width(80)
            .fontSize(14)
            .flexGrow(0)

        }.width('100%')
        .padding(10)

        Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
          Text("对端IP和端口:")
            .fontSize(14)
            .width(110)
            .flexGrow(1)

          TextInput({ text: this.targetIp })
            .onChange((value) => {
              this.targetIp = value
            })
            .width(100)
            .flexGrow(4)

          Text(":")
            .width(5)
            .flexGrow(0)

          TextInput({ text: this.targetPort.toString() })
            .type(InputType.Number)
            .onChange((value) => {
              this.targetPort = parseInt(value)
            })
            .flexGrow(2)
            .width(60)
        }
        .width('100%')
        .padding(10)

        Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {

          TextInput({ placeholder: "输入要发送的消息" }).onChange((value) => {
            this.sendMsg = value
          })
            .width(200)
            .flexGrow(1)

          Button("发送")
            .width(80)
            .flexGrow(0)
            .onClick(() => {
              this.sendMsg2Target()
            })
        }
        .width('100%')
        .padding(10)

        Scroll(this.scroller) {
          Text(this.msgHistory)
            .textAlign(TextAlign.Start)
            .padding(10)
            .width('100%')
            .backgroundColor(0xeeeeee)
        }
        .align(Alignment.Top)
        .backgroundColor(0xeeeeee)
        .height(300)
        .flexGrow(1)
        .scrollable(ScrollDirection.Vertical)
        .scrollBar(BarState.On)
        .scrollBarWidth(20)
      }
      .width('100%')
      .justifyContent(FlexAlign.Start)
      .height('100%')
    }
    .height('100%')
  }

  //发送消息到目的ip和端口
  sendMsg2Target() {
    //目的ip和端口
    let remoteAddress = { address: this.targetIp, port: this.targetPort, family: 1 }
    udpSocket.send({ data: this.sendMsg, address: remoteAddress })
      .then(async () => {
        this.msgHistory += "我:" + this.sendMsg + await getCurrentTimeString() + "\r\n"
      })
      .catch((e) => {
        this.msgHistory += '发送失败' + e.message + "\r\n";
      })
  }

  //绑定本地端口
  async bind2Port() {
    //本地地址
    let localAddress = { address: "0.0.0.0", port: this.localPort, family: 1 }

    await udpSocket.bind(localAddress)
      .then(() => {
        this.msgHistory = 'bind success' + "\r\n";
      })
      .catch((e) => {
        this.msgHistory = 'bind fail' + e.message + "\r\n";
      })

    //收到消息时的处理
    udpSocket.on("message", async (value) => {
      let msg = buf2String(value.message)
      let remoteIP = value.remoteInfo.address
      let remotePort = value.remoteInfo.port.toString()
      //对端ip地址和端口的字符串形式
      let remoteAddr = "[" + remoteIP + ":" + remotePort + "]:"
      let time = await getCurrentTimeString()
      this.msgHistory += remoteAddr + msg + time + "\r\n"
      this.scroller.scrollEdge(Edge.Bottom)
    })
  }
}

//同步获取当前时间的字符串形式
async function getCurrentTimeString() {
  let time = ""
  await  systemDateTime.getDate().then(
    (date) => {
      time = date.getHours().toString() + ":" + date.getMinutes().toString()
      + ":" + date.getSeconds().toString()
    }
  )
  return "[" + time + "]"
}

//ArrayBuffer转utf8字符串
function buf2String(buf: ArrayBuffer) {
  let msgArray = new Uint8Array(buf);
  let textDecoder = util.TextDecoder.create("utf-8");
  return textDecoder.decodeWithStream(msgArray)
}

步骤4:编译运行。如果通过模拟器来部署运行,需要使用远程模拟器

cke_315118.png

使用本地模拟器可能会有网络问题。

另外,使用模拟器的时候要确保打开WIFI,可以通过设置->WLAN页面开启。

步骤5:配置本地端口和对端IP地址和端口,然后单击绑定按钮绑定到本地指定的端口,再单击发送按钮发送消息。可以根据实际需要配置,如果没有对端的UDP应用,把本应用作为对端也可以,也就是相当于自己给自己发送消息,最后自己再接收,如图所示:

cke_416491.png

​这样就完成了一个简单的UDP消息发送应

最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?但是又不知道从哪里下手,而且学习时频繁踩坑,最终浪费大量时间。所以本人整理了一些比较合适的鸿蒙(HarmonyOS NEXT)学习路径和一些资料的整理供小伙伴学习

 →纯血鸿蒙Next全套最新学习资料

希望这一份鸿蒙学习资料能够给大家带来帮助

                             
                                     

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

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

相关文章

无需修改代码,深入探究 pytest 如何自动查找并加载三方插件

相信测试的同学或者python开发同学&#xff0c;都知道pytest框架&#xff0c;pytest不仅是一个功能强大的测试框架&#xff0c;同时还是一个插件化的测试平台。 插件只需配置就可以直接使用&#xff0c;而不需要测试代码配合。如果安装了插件&#xff0c;pytest则可以自动查找…

基于python的随机森林回归预测+贝叶斯优化超参数前后训练效果对比

目录 1.导入必要的库 2.导入数据与数据预处理 3.查看数据分布 4.特征选择 5.模型建立与训练 6.训练集预测结果 7.模型评估 8.预测新数据 9.贝叶斯优化超参数 1.导入必要的库 # 导入所需的库 from sklearn.model_selection import cross_val_score import pandas as …

【聊聊原子性,中断,以及nodejs中的具体示例】

什么是原子性 从一个例子说起&#xff0c; x &#xff0c;读和写 &#xff0c; 如图假设多线程&#xff0c;线程1和线程2同时操作变量x&#xff0c;进行x的操作&#xff0c;那么由于写的过程中&#xff0c;都会先读一份x数据到cpu的寄存器中&#xff0c;所以这个时候cpu1 和 c…

Michael.W基于Foundry精读Openzeppelin第61期——ERC1967Upgrade.sol

Michael.W基于Foundry精读Openzeppelin第61期——ERC1967Upgrade.sol 0. 版本0.1 ERC1967Upgrade.sol 1. 目标合约2. 代码精读2.1 _getImplementation() internal && _upgradeTo(address newImplementation) internal2.2 _upgradeToAndCall(address newImplementation,…

11--ElasticStack7-ELK+Kafka

前言&#xff1a;日志分析管理平台对于平时的规模化运维占的权重非常大&#xff0c;这一章涉及的程序较多&#xff0c;会将每个程序的基础使用和模块分开梳理&#xff0c;基础概念会分布在每小节开头&#xff0c;最后串联成一个完整的工作环境。 1、ELK架构 ELK 是一个非常流…

基于机器学习的永磁同步电机矢量控制策略-高分资源-下载可用!

基于机器学习的永磁同步电机矢量控制策略 优势 训练了RL-Agent&#xff0c;能够提高电机在非线性负载下的性能。 部分程序 仿真结果 转矩估计及dq轴电流。 代码有偿&#xff0c;50&#xff0c;需要的可以联系。

【STM32HAL库学习】通信方式:USART、IIC、SPI

通信的目的&#xff1a;将一个设备的数据传送到另一个设备&#xff0c;扩展硬件系统 通信接口区别 名称引脚双工时钟电平设备USARTTX、RX全双工异步单端点对点I2CSCL、SDA半双工同步单端多设备SPISCLK、MOSI、MISO、CS全双工同步单端多设备CANCAN_H、CAN_L半双工异步差分多设…

【antd + vue】表格行合并,同时使用插槽

一、需求说明 表格中&#xff0c;如果一个学校有多个考试科目&#xff0c;则分行展示&#xff0c;其余列&#xff0c;则合并为一行展示&#xff0c;如图所示 二、需求分析 1、表格行合并 相当于有4行&#xff0c;其中1、2行是同一个学校包含不同考试科目及对应人次的数据&am…

COB封装的LED显示屏是什么?

COB&#xff08;Chip on Board&#xff09;封装的LED显示屏&#xff0c;是一种采用先进倒装COB封装技术的显示屏&#xff0c;其中LED芯片是直接被安装并封装在PCB电路板上&#xff0c;而不是先对单个封装再焊接至电路板&#xff0c;与SMD&#xff08;Surface Mount Device&…

Java知识点整理 18 — Lambda表达式

一. 简介 Lambda 表达式是函数式编程思想的体现&#xff0c;强调做什么&#xff0c;而不是以什么方式去做。 面向对象编程思想强调的是对象&#xff0c;必须通过对象的形式来做一些事情。比如多线程执行任务&#xff0c;需要创建对象&#xff0c;对象需要实现指定接口&#x…

【吴恩达机器学习-week2】可选实验:使用 Scikit-Learn 进行线性回归

支持我的工作 &#x1f389; &#x1f4c3;亲爱的朋友们&#xff0c;感谢你们一直以来对我的关注和支持&#xff01; &#x1f4aa;&#x1f3fb; 为了提供更优质的内容和更有趣的创作&#xff0c;我付出了大量的时间和精力。如果你觉得我的内容对你有帮助或带来了欢乐&#xf…

看完这篇文章你就知道什么是未来软件开发的方向了!即生成式AI在软件开发领域的革新=CodeFlying

从最早的UGC&#xff08;用户生成内容&#xff09;到PGC&#xff08;专业生成内容&#xff09;再到AIGC&#xff08;人工智能生成内容&#xff09;体现了web1.0→web2.0→web3.0的发展历程。 毫无疑问UGC已经成为了当前拥有群体数量最大的内容生产方式。 同时随着人工智能技术…

SAP 表字段调整,表维护生成器调整

表维护生成器->已生成的对象->更改->专家模式

Linux下的wifi开发

了解什么是wifi 可参考&#xff1a; 什么是Wi-Fi&#xff1f;Wi-Fi和WLAN的区别是什么&#xff1f; - 华为 (huawei.com) WLAN的基本元素 工作站STA&#xff08;Station&#xff09;&#xff1a;支持802.11标准的终端设备。例如带无线网卡的电脑、支持WLAN的手机等。 接入点AP&…

OpenSSH RCE (CVE-2024-6387) | 附poc | 小试

Ⅰ 漏洞描述 OpenSSH 远程代码执行漏洞(CVE-2024-6387)&#xff0c;该漏洞是由于OpenSSH服务器 (sshd) 中的信号处理程序竞争问题&#xff0c;未经身份验证的攻击者可以利用此漏洞在Linux系统上以root身份执行任意代码。 Ⅱ 影响范围 8.5p1 < OpenSSH < 9.8p1 但OpenSS…

数学建模--层次分析法~~深入解读

目录 1.基本概念 &#xff08;1&#xff09;研究案例 &#xff08;2&#xff09;模型框架 &#xff08;3&#xff09;阐述说明 &#xff08;4&#xff09;注意事项 2.模型的建立和求解 &#xff08;1&#xff09;数量级的统一 &#xff08;2&#xff09;归一化处理 &am…

用Vue3和Rough.js绘制一个粗糙的3D条形图

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 使用 Rough.js 和 D3.js 绘制粗糙手写风格条形图 应用场景 该代码适用于需要在 Web 应用程序中创建具有粗糙手写风格的条形图的情况。它可以用于数据可视化、信息图表或任何需要以独特和有吸引力的方式呈现数…

Java StringBuffer类和StringBuilder类

在使用 StringBuffer 类时&#xff0c;每次都会对 StringBuffer 对象本身进行操作&#xff0c;而不是生成新的对象&#xff0c;所以如果需要对字符串进行修改推荐使用 StringBuffer。 StringBuilder 类在 Java 5 中被提出&#xff0c;它和 StringBuffer 之间的最大不同在于 St…

【PYG】Cora数据集分类任务计算损失,cross_entropy为什么不能直接替换成mse_loss

cross_entropy计算误差方式&#xff0c;输入向量z为[1,2,3]&#xff0c;预测y为[1]&#xff0c;选择数为2&#xff0c;计算出一大坨e的式子为3.405&#xff0c;再用-23.405计算得到1.405MSE计算误差方式&#xff0c;输入z为[1,2,3]&#xff0c;预测向量应该是[1,0,0]&#xff0…

IAR工程目录移动报错(改变文件目录结构)

刚开始用IAR&#xff0c;记录一下。 工作中使用华大单片机&#xff0c;例程的文件目录结构太复杂了想精简一点。 1.如果原本的C文件相对工程文件&#xff08;.eww文件&#xff09;路径变化了&#xff0c;需要先打开工程&#xff0c;再将所有的.c文件右键Add添加进工程&#xf…