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

本期将为您展示一下:如何 使用 Socket模块实现 DAYU200开发板 和 Windows PC
首页 新闻资讯 行业资讯 OpenHarmony之 网络管理 Socket 模块的使用

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

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;
    }
  }
  • 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。

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

11    2022-05-26 15:28:03    网络管理 Socket 模块