3

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

 2 years ago
source link: https://os.51cto.com/article/710001.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

OpenHarmony之 网络管理 Socket 模块的使用-51CTO.COM

OpenHarmony之 网络管理 Socket 模块的使用
作者:Buty9147 2022-05-26 15:28:03
本期将为您展示一下:如何 使用 Socket模块实现 DAYU200开发板 和 Windows PC (SocketTool 工具)的之间的数据传输。
e70eb6d594d0f83cba60805f35e97b84d0bbde.png

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

​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地址权限
      }
    ]

2、通过TCP协议方式实现

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

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

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

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

//tcp连接对象
let tcp = socket.constructTCPSocketInstance();
//目标地址和端口
let targetAddr = {
  address: '192.168.0.168',
  family: 1, 
  port: 0 //7001/8001
}
138bb6658bb3d2363f40639cb9b99c3f822f8e.png

(可选)订阅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;
    }
  }

绑定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
    });    
}

连接到指定的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)}`);
        })
      })
  }

发送数据。

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`);
        }
    })
}

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');
}

3、通过UDP协议方式实现

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

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

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

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

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

(可选)订阅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;
    }
  }

绑定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)}`);
    })    
}

发送数据。

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}`);
          })
      }
    })
  }

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');
}

三、效果展示

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

38dfe9c24de0aab4f50460c153f8bb8a620471.png
63d6069073ad000999f407d507eeae725d3a4e.png

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

e83828787e47d22962596805576622a9138e6d.png

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

41b501c008352dbad94959741de9d2982577f0.png

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

c52ad6e009573b80f379504781acbbf7e3c8d2.png

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

37d97b9614f75c4dcf9894f26e6e83f40c8c65.png

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

b7be8a681ed362dd0d014974d59aa6047cb11e.png
c349fe6019b76cfe8c91156c5942c54482c17f.png
f1a2aec99238703f4e3644bec4593d10098bcb.png
39d048179f60cd849413166760202eab20fa02.png
696656875f9582aa674049249efd3e83c4a99d.png

四、思考总结

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​​。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK