OpenHarmony之 网络管理 Socket 模块的使用

系统 OpenHarmony
本期将为您展示一下:如何 使用 Socket模块实现 DAYU200开发板 和 Windows PC (SocketTool 工具)的之间的数据传输。

​想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com​

一、介绍

Socket 模块可以用来进行数据传输,支持TCP和UDP两种协议。

本期将为您展示一下:

如何 使用 Socket模块实现 DAYU200开发板 和 Windows PC (SocketTool 工具)的之间的数据传输。

演示环境:

  • OpenHarmony SDK API Version 8(3.1.5.5)。
  • DevEco Studio 3.0 Beta3。
  • Build Version: 3.0.0.900, built on March 30, 2022。
  • Runtime version: 11.0.13+7-b1751.21 amd64。

二、开发步骤

1、权限声明

创建项目后,打开项目config.json 配置文件,在module内添加权限声明

  "reqPermissions": [
      {
        "name": "ohos.permission.INTERNET"  //socket权限
      },
      {
        "name": "ohos.permission.GET_WIFI_INFO" //获取wifi地址权限
      }
    ]
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

2、通过TCP协议方式实现

import需要的socket模块及辅助模块。

Logger是一个自定义的日志模块,方便快速记录和输出统一格式的日志。

import socket from '@ohos.net.socket';
//wifi模块,用于获取当前IP地址
import wifi from '@ohos.wifi';
//日志模块
import logger from '../common/Logger'
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

创建一个TCPSocket连接,返回一个TCPSocket对象。

//tcp连接对象
let tcp = socket.constructTCPSocketInstance();
//目标地址和端口
let targetAddr = {
  address: '192.168.0.168',
  family: 1, 
  port: 0 //7001/8001
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

(可选)订阅TCPSocket相关的订阅事件。

订阅了connect 、message、close 事件。message事件的回调中可以获取接收到的数据,但是一个ArrayBuffer,需要通过resolveArrayBuffer函数进行进一步解析。

 aboutToAppear() {
    this.tcpInit()
  }
  tcpInit() {
    tcp.on('connect', () => {
      this.status = '已连接'
      logger.getInstance(this).debug("on tcp connect success");
    });
    tcp.on('message', value => {
      this.message_recv = this.resolveArrayBuffer(value.message)
      logger.getInstance(this)
        .debug(`on tcp message:${this.message_recv},remoteInfo:${JSON.stringify(value.remoteInfo)}`);
    });
    tcp.on('close', () => {
      logger.getInstance(this).debug("on tcp close success");
    });

  }
  //解析ArrayBuffer
  resolveArrayBuffer(message: ArrayBuffer): string {
    if (message instanceof ArrayBuffer) {
      let dataView = new DataView(message)
      logger.getInstance(this).debug(`length ${dataView.byteLength}`)
      let str = ""
      for (let i = 0;i < dataView.byteLength; ++i) {
        let c = String.fromCharCode(dataView.getUint8(i))
        if (c !== "\n") {
          str += c
        }
      }
      logger.getInstance(this).debug(`message aray buffer:${str}`)
      return str;
    }
  }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.

绑定IP地址和端口,端口可以指定或由系统随机分配。

  //本地地址和端口
    let localAddr = {
        address: this.resolveIP(wifi.getIpInfo().ipAddress),
        port: 0
    }
    //bind本地地址
    tcp.bind({ address: this.localAddr.address, port: 8000, family: 1 })
      .then(() => {
        logger.getInstance(this).debug(`bind tcp success`);
      }).catch(err => {
      logger.getInstance(this).error(`bind tcp failed ${err}`);
      return
    });    
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

连接到指定的IP地址和端口。

Button('连接TCP')
  .width('90%')
  .height(80)
  .margin({ top: 20 })
  .type(ButtonType.Capsule)
  .onClick(() => {
    this.tcpConnect()
  })
  //连接TCP
  tcpConnect() {
    tcp.getState()
      .then((data) => {
        logger.getInstance(this).debug(`====${JSON.stringify(data)}`)
        if (data.isClose) {
          this.tcpInit()
        }
        //开始连接
        tcp.connect(
          {
            address: { address: targetAddr.address, port: 8001, family: 1 }, timeout: 6000
          }
        ).then(() => {
          logger.getInstance(this).debug(`connect success`);
        }).catch((error) => {
          logger.getInstance(this).error(`connect failed ${JSON.stringify(error)}`);
        })
      })
  }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.

发送数据。

Button('发送TCP消息')
    .width('90%')
    .height(80)
    .margin({ top: 20 })
    .type(ButtonType.Capsule)
    .onClick(() => {
        this.tcpSend()
    })
//发送TCP消息
tcpSend() {
    //查看状态
    tcp.getState().then((data) => {
        logger.getInstance(this).debug(`====${JSON.stringify(data)}`)
        //已连接,就发送数据
        if (data.isConnected) {
            //发送消息
            tcp.send(
                { data: this.message_send, }
            ).then(() => {
                logger.getInstance(this).debug(`send success`);
            }).catch((error) => {
                logger.getInstance(this).error(`send failed ${JSON.stringify(error)}`);
            })
        } else {
            logger.getInstance(this).error(`not connect`);
        }
    })
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.

Socket连接使用完毕后,主动关闭。

Button('关闭TCP连接')
    .width('90%')
    .height(80)
    .margin({ top: 20 })
    .type(ButtonType.Capsule)
    .onClick(() => {
        this.tcpClose()
    })
//关闭TCP连接
tcpClose() {
    tcp.close().then(() => {
        this.status = '已断开'
        logger.getInstance(this).debug(`tcp.close success`)
    }).catch((err) => {
        logger.getInstance(this).error(`tcp.close error:${JSON.stringify(err)}`)
    })
    tcp.off('close');
    tcp.off('message');
    tcp.off('connect');
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

3、通过UDP协议方式实现

import需要的socket模块及辅助模块。

Logger是一个自定义的日志模块,方便快速记录和输出统一格式的日志。

import socket from '@ohos.net.socket';
//wifi模块,用于获取当前IP地址
import wifi from '@ohos.wifi';
//日志模块
import logger from '../common/Logger'
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

创建一个UDPSocket连接,返回一个UDPSocket对象。

//udp连接对象
let udp = socket.constructUDPSocketInstance();
//目标地址和端口
let targetAddr = {
  address: '192.168.0.168',
  family: 1,
  port: 0 //7001/8001
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

(可选)订阅UDPSocket相关的订阅事件。

订阅了listening、message、close 事件。message事件的回调中可以获取接收到的数据,但是一个ArrayBuffer,需要通过resolveArrayBuffer函数进行进一步解析。

  aboutToAppear() {
    this.udpInit()
  }
  udpInit() {
    udp.on('listening', () => {
      logger.getInstance(this).debug("on udp listening success");
    });
    udp.on('message', value => {
      this.message_recv = this.resolveArrayBuffer(value.message)
      logger.getInstance(this)
        .debug(`on udp message:${this.message_recv},remoteInfo:${JSON.stringify(value.remoteInfo)}`);
    });
    udp.on('close', () => {
      logger.getInstance(this).debug(`on udp close success`);
    });
  }
  //解析ArrayBuffer
  resolveArrayBuffer(message: ArrayBuffer): string {
    if (message instanceof ArrayBuffer) {
      let dataView = new DataView(message)
      logger.getInstance(this).debug(`length ${dataView.byteLength}`)
      let str = ""
      for (let i = 0;i < dataView.byteLength; ++i) {
        let c = String.fromCharCode(dataView.getUint8(i))
        if (c !== "\n") {
          str += c
        }
      }
      logger.getInstance(this).debug(`message aray buffer:${str}`)
      return str;
    }
  }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.

绑定IP地址和端口,端口可以指定或由系统随机分配。

 //本地地址和端口
    let localAddr = {
        address: this.resolveIP(wifi.getIpInfo().ipAddress),
        port: 0
    }
    //bind本地地址
    udp.bind({ address: this.localAddr.address, family: 1 })
      .then(() => {
        logger.getInstance(this).debug(`bind upd success`);
      }).catch((err) => {
      logger.getInstance(this).error(`bind upd failed ${JSON.stringify(err)}`);
    })    
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

发送数据。

Button('发送UDP消息')
    .width('90%')
    .height(80)
    .margin({ top: 50 })
    .type(ButtonType.Capsule)
    .onClick(() => {
      this.udpSend()
  })  
//发送UDP消息
udpSend() {
    //查看状态
    udp.getState().then((data) => {
      logger.getInstance(this).debug(`====${JSON.stringify(data)}`)
      //如果已关闭,就重新初始化
      if (data.isClose) {
        this.udpInit()
      }
      //已绑定,就发送数据
      if (data.isBound) {
        udp.send(
          {
            data: this.message_send,
            address: { address: targetAddr.address, port: 7001, family: 1 }
          }
        )
          .then(() => {
            logger.getInstance(this).debug(`send success`);
          })
          .catch((error) => {
            logger.getInstance(this).error(`send failed ${typeof error.code}`);
          })
      }
    })
  }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.

Socket连接使用完毕后,主动关闭。

Button('关闭UDP')
    .width('90%')
    .height(80)
    .margin({ top: 20 })
    .type(ButtonType.Capsule)
    .onClick(() => {
        this.udpClose()
    })
udpClose() {
    udp.close().then(() => {
        logger.getInstance(this).debug("udp.close success");
    }).catch((err) => {
        logger.getInstance(this).error(`udp.close error:${JSON.stringify(err)}`)
    })
    udp.off('message');
    udp.off('listening');
    udp.off('close');
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

三、效果展示

windows系统上打开SocketTool 工具,分别创建TCP Server、UDP Server,监听端口分别为8001、7001。

点击,DAYU200开发板应用上 “连接” 按钮,Socket工具显示已连接。

点击 DAYU200开发板应用上 “发送TCP消息” 按钮,Socket工具显示接收到的消息。

Socket工具上输入要回复的消息,点击 “发送数据” 按钮。

点击 DAYU200开发板应用上 “发送UDP消息” 按钮,Socket工具UDP Server 栏 显示接收到的消息。

Socket工具上输入要回复的消息 ,点击 “发送数据” 按钮。

四、思考总结

TCP是面向连接的协议,在收发数据前必须和对方建立可靠的连接,UDP是一个面向无连接的协议,数据传输前,源端和终端不建立连接,所以TCP的方式比UDP多一个connect的过程。

getState接口的状态值有3个,isBound、isClose 是udp/tcp 都会用,isConnected是给tcp用的 。{“isBound”:true,“isClose”:false,“isConnected”:true}

用Socket 工具回复消息后,DAYU200侧如果要显示接收到的消息,需要再发送一次消息。

五、参考资料

1、HarmonyOS API参考。

https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-net-socket-0000001144636978。

2、OpenHarmony Gitee 样例指导。

https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/connectivity/socket-connection.md。

​想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com​​。

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2022-05-24 15:06:57

AbilityeTS FA鸿蒙

2022-01-12 14:45:26

鸿蒙HarmonyOS应用

2022-03-10 14:45:23

HarmonyAPI操作系统

2022-03-30 14:07:47

Harmony操作系统鸿蒙

2019-02-12 15:04:09

2020-11-13 08:30:57

Socket

2022-03-24 15:00:09

Harmony编解码鸿蒙

2021-11-25 09:54:54

鸿蒙HarmonyOS应用

2009-08-13 10:19:43

Linux系统Nagios网络管理模块

2011-10-24 09:47:37

RHEL网络管理器

2023-04-12 15:31:11

系统服务管理鸿蒙

2022-03-15 15:24:53

操作系统RTOSAT模块

2011-03-21 11:14:23

LinuxNagios

2022-04-06 11:27:05

harmonyeTS 开发NAPI开发

2011-03-21 11:14:25

LinuxNagios

2021-09-08 15:09:29

鸿蒙HarmonyOS应用

2012-12-11 10:15:02

Winform开发框架

2021-11-18 10:28:03

鸿蒙HarmonyOS应用

2021-12-14 14:45:38

鸿蒙HarmonyOS应用

2012-09-10 10:00:02

点赞
收藏

51CTO技术栈公众号