鸿蒙5.0开发【两模拟器实现网络通信】

本文概述
  • 问题背景:由于缺少HarmonyOS NEXT 真机,无法处理网络互通测试需求。因此,尝试探索模拟器互通

  • 环境准备:

    • 安装最新版DevEco Studio

      4

    • [申请最新版模拟器]

  • 新建两个模拟器

    3

  • 演示视频:工程结构 + 操作方法 + 运行效果

核心步骤
  • 由于两个模拟器均位于同一PC,其IP 及端口号均一致,因此,我需做端口转发即可

  • 使用hdc 查询模拟器id 与模拟器对应关系:需提前打开模拟器

    • 概述:

      • 红色框内即为模拟器id

      • 只开一个,并执行hdc list targets 即可进行区分

        2

    • 对应关系:

      • 127.0.0.1:5554 对应Huawei_Phone,将其用作客户端
      • 127.0.0.1:5557 对应Huawei_Phone_New,将其用作服务端
  • 端口转发:对服务端进行端口转发

    • 概述:

      • 假设模拟器实例我们指定为socket tcp服务端A:127.0.0.1:5555,监听端口为8088,客户端B:127.0.0.1:5557,监听端口为8089
      • 则在终端执行端口转发命令:hdc-t127.0.0.1:5555 fport tcp:8088 tcp:8089
      • 特别注意事项:模拟器中 socke(服务端代码监听的本机地址为:127.0.0.1.不是10.0.2.15,端口为8088,客户端socket本机配置地址:10.0.2.15:12340(端囗可随意),连接地址:10.0.2.2:8089
    • 执行命令

      hdc -t 127.0.0.1:5557 fport ls//查询端口转发任务
      hdc -t 127.0.0.1:5557 fport tcp:8089 tcp:8088//执行端口转发任务
      
    • 运行截图:

      • 第一条:此时无端口转发任务

      • 第二条:执行端口转发任务

      • 第三条:查询端口转发任务,佐证第二条是否执行成功

        1

代码运行步骤
  • 先运行服务端,再运行客户端
  • 详细步骤,请查阅演示视频
完整代码
  • 公共参数:InfoObject.ets

    export class InfoObject{
      messageNumber: number = 0//共计收到多少条消息
      receiveMsg: string = "默认值"//接收的消息的字面量
      ipAddress: string = "默认值"//通信对方的IP 地址
      portNumber: number = 0//通信对方的端口号
      protocolType: string = "默认值"//通信对方所采用的协议类型
      sizeMsg: number = 0//消息的字节数
    ​
      constructor(messageNumber: number,receiveMsg: string,ipAddress: string,portNumber: number,protocolType: string,sizeMsg: number) {
        this.messageNumber = messageNumber
        this.receiveMsg = receiveMsg
        this.ipAddress = ipAddress
        this.portNumber = portNumber
        this.protocolType = protocolType
        this.sizeMsg = sizeMsg
      }
    }
    
  • 客户端页面:TCPSocketClientPage.ets

    import { TCPSocketClientUtils } from './TCPSocketClientUtils';
    import { InfoObject } from './InfoObject'
    ​
    @Entry
    @Component
    struct TCPSocketClientPage {
    ​
      @State info: InfoObject = new InfoObject(0,"默认值","默认值",0,"默认值",0)
    ​
      tcpSocketClient: TCPSocketClientUtils = new TCPSocketClientUtils(this.info);
    ​
      build() {
        Row() {
          Column() {
            Text("此为客户端端模拟器")
              .fontSize(20)
              .fontWeight(FontWeight.Bold)
            Button("启动客户端")
              .fontSize(20)
              .onClick((event) => {
                this.tcpSocketClient.init();
              })
            Button("客户端向服务端发送数据")
              .fontSize(20)
              .onClick((event) => {
                this.tcpSocketClient.sendMsg();
              })
            Column(){
              Text("展示客户端接收的消息内容").fontColor(Color.Green)
              Text(`客户端共计收到${this.info.messageNumber} 条消息`)
              Text(`客户端接受到的NO.${this.info.messageNumber} 消息:${this.info.receiveMsg}`).fontColor(Color.Grey)
              Text(`与客户端通信的对方IP :${this.info.ipAddress}`).fontColor(Color.Orange)
              Text(`与客户端通信的对方端口号:${this.info.portNumber}`).fontColor(Color.Blue)
              Text(`与客户端通信的对方采用的协议:${this.info.protocolType}`).fontColor(Color.Pink)
            }.margin({top: 36})
          }
          .width('100%')
        }
        .height('100%')
      }
    ​
      aboutToDisappear() {
        this.tcpSocketClient.closeSocket('界面不可见');
      }
    }
    
  • 客户端工具类:TCPSocketClientUtils.ets

    import { socket } from '@kit.NetworkKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    import hilog from '@ohos.hilog';
    ​
    import { InfoObject } from "./InfoObject"
    ​
    const TAG: string = "WAsbry_TCPSocketClient";
    ​
    class SocketInfo {
      message: ArrayBuffer = new ArrayBuffer(1);
      remoteInfo: socket.SocketRemoteInfo = {} as socket.SocketRemoteInfo;
    }
    ​
    export class TCPSocketClientUtils {
    ​
      info: InfoObject
    ​
      tcp: socket.TCPSocket;//TCP链接实例
    ​
      constructor(info: InfoObject) {
        // 创建一个TCPSocket连接,返回一个TCPSocket对象。
        this.tcp = socket.constructTCPSocketInstance();
        this.info = info
      }
    ​
      init() {
        printLog(TAG,"进入客户端绑定环节")
        // 绑定本地IP地址和端口。
        let ipAddress: socket.NetAddress = {} as socket.NetAddress;
        ipAddress.address = "10.0.2.15";//客户端本机IP
        ipAddress.port = 12340;//指定客户端本地端口号
        this.tcp.bind(ipAddress, (err: BusinessError) => {//绑定IP地址和端口号。端口号可以指定,也可以由系统随机分配
          if (err) {
            printLog(TAG, `客户端绑定IP地址和端口号出现异常,err = ${err}`);
            return;
          }
          printLog(TAG, '客户端绑定IP地址和端口号成功');
    ​
          // 连接到指定的IP地址和端口。
          ipAddress.address = "10.0.2.2";
          ipAddress.port = 8089;
    ​
          let tcpConnect: socket.TCPConnectOptions = {} as socket.TCPConnectOptions;
          tcpConnect.address = ipAddress;
          tcpConnect.timeout = 30000;
    ​
          this.tcp.connect(tcpConnect).then(() => {
            printLog(TAG, '客户端建立到指定IP地址和端口号的连接,成功');
            // this.sendMsg();
          }).catch((err: BusinessError) => {
            printLog(TAG, '客户端建立到指定IP地址和端口号的连接,出现异常');
          });
        });
    ​
        this.tcp.on('message', (value: SocketInfo) => {
          printLog(TAG,"客户端 TCPSocketConnection成功接收到消息")
          let buffer = value.message;
          let dataView = new DataView(buffer);
          let str = "";
          for (let i = 0; i < dataView.byteLength; ++i) {
            str += String.fromCharCode(dataView.getUint8(i));
          }
    ​
          printLog(TAG, "客户端 received message--:" + str);//消息内容
          printLog(TAG, "客户端 received address--:" + value.remoteInfo.address);//绑定的IP地址。
          printLog(TAG, "客户端 received family--:" + value.remoteInfo.family);//网络协议类型。选项包括:IPv4、IPv6。
          printLog(TAG, "客户端 received port--:" + value.remoteInfo.port);//端口号。取值范围是0 ~ 65535。
          printLog(TAG, "客户端 received size--:" + value.remoteInfo.size);//服务器响应消息的长度,以字节为单位。
    ​
          this.info.messageNumber++
          this.info.receiveMsg = str
          this.info.ipAddress = value.remoteInfo.address
          this.info.protocolType = value.remoteInfo.family
          this.info.portNumber = value.remoteInfo.port
          this.info.sizeMsg = value.remoteInfo.size
        });
        this.tcp.on('connect', () => {
          printLog(TAG, "客户端TCPSocket连接建立");
        });
        this.tcp.on('close', () => {
          printLog(TAG, "客户端TCPSocket连接关闭");
        });
    ​
      }
    ​
      sendMsg(){
        let tcpSendOptions: socket.TCPSendOptions = {
          data: 'this data is from client'
        }
        this.tcp.send(tcpSendOptions).then(() => {
          printLog(TAG, '客户端向服务端发送数据成功');
        }).catch((err: BusinessError) => {
          printLog(TAG, '客户端向服务端发送数据失败');
        });
      }
    ​
      closeSocket(reason: string) {
        this.tcp.close().then(() => {
          printLog(TAG, reason + ' 客户端到服务端的链接关闭,成功');
        }).catch((err: BusinessError) => {
          printLog(TAG, reason + ' 客户端到服务端的链接关闭,出现异常');
        });
        this.tcp.off('message');
        this.tcp.off('connect');
        this.tcp.off('close');
      }
    }
    ​
    function printLog(tag: string,info: string){
      hilog.info(0xff00,tag,info)
    }
    
  • 服务端页面:TCPSocketServerPage.ets

    import { InfoObject } from './InfoObject';
    import { TCPSocketServerUtils } from './TCPSocketServerUtils';
    ​
    @Entry
    @Component
    struct TCPSocketServerPage {
    ​
      @State info: InfoObject = new InfoObject(0,"默认值","默认值",0,"默认值",0)
    ​
      tcpSocketServer: TCPSocketServerUtils = new TCPSocketServerUtils(this.info);//创建tcp 服务端工具类对象
    ​
      build() {
        Row() {
          Column() {
            Text("此为服务端模拟器")
              .fontSize(20)
            Button('启动服务端')
              .fontSize(20)
              .onClick((event) =>{
                this.tcpSocketServer.init();
              })
            Button('服务端向客户端发送数据')
              .fontSize(20)
              .onClick((event) =>{
                this.tcpSocketServer.sendMsg();
              })
            Column(){
              Text("展示服务端接收的消息内容").fontColor(Color.Green)
              Text(`服务端共计收到${this.info.messageNumber} 条消息`)
              Text(`服务端接受到的NO.${this.info.messageNumber} 消息:${this.info.receiveMsg}`).fontColor(Color.Grey)
              Text(`与服务端通信的对方IP :${this.info.ipAddress}`).fontColor(Color.Orange)
              Text(`与服务端通信的对方端口号:${this.info.portNumber}`).fontColor(Color.Blue)
              Text(`与服务端通信的对方采用的协议:${this.info.protocolType}`).fontColor(Color.Pink)
            }.margin({top: 36})
          }
          .width('100%')
        }
        .height('100%')
      }
    ​
      aboutToDisappear() {
        this.tcpSocketServer.closeClient();
        this.tcpSocketServer.cancelEventSub();
      }
    ​
    }
    
  • 服务端工具类:TCPSocketServerUtils.ets

    import { socket } from '@kit.NetworkKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    import hilog from '@ohos.hilog';
    import { InfoObject } from './InfoObject';
    ​
    const TAG: string = "WAsbry_TCPSocketServer";
    ​
    class SocketInfo {
      message: ArrayBuffer = new ArrayBuffer(1);
      remoteInfo: socket.SocketRemoteInfo = {} as socket.SocketRemoteInfo;
    }
    ​
    export class TCPSocketServerUtils {
      info: InfoObject//传参对象
      tcpServer:socket.TCPSocketServer;//TCP链接服务端实例
      clientRef?: socket.TCPSocketConnection;//TCP链接对象
    ​
      constructor(info: InfoObject) {
        // 创建一个TCPSocketServer连接,返回一个TCPSocketServer对象。
        this.tcpServer = socket.constructTCPSocketServerInstance();
        this.info = info
      }
    ​
      init() {
        let ipAddress: socket.NetAddress = {} as socket.NetAddress;
        ipAddress.address = "127.0.0.1";//服务端本机IP
        ipAddress.port = 8088;//服务端本地端口号
    ​
        // 绑定本地IP地址和端口,进行监听
        this.tcpServer.listen(ipAddress).then(() => {
          printLog(TAG, "服务端监听本地IP地址和端口成功");
        }).catch((err: BusinessError) => {
          printLog(TAG, '服务端监听本地IP地址和端口失败');
        });
        // 订阅TCPSocketServer的connect事件
        this.tcpServer.on("connect", (client: socket.TCPSocketConnection) => {
          this.clientRef = client;
          // 订阅TCPSocketConnection相关的事件
          client.on("close", () => {
            printLog(TAG, "TCPSocketConnection关闭");
          });
          client.on("message", (value: SocketInfo) => {
            printLog(TAG,"服务端TCPSocketConnection成功接收到消息")
            let buffer = value.message;
            let dataView = new DataView(buffer);
            let str = "";
            for (let i = 0; i < dataView.byteLength; ++i) {
              str += String.fromCharCode(dataView.getUint8(i));
            }
            printLog(TAG, "服务端 received message--:" + str);//消息内容
            printLog(TAG, "服务端 received address--:" + value.remoteInfo.address);//绑定的IP地址。
            printLog(TAG, "服务端 received family--:" + value.remoteInfo.family);//网络协议类型。选项包括:IPv4、IPv6。
            printLog(TAG, "服务端 received port--:" + value.remoteInfo.port);//端口号。取值范围是0 ~ 65535。
            printLog(TAG, "服务端 received size--:" + value.remoteInfo.size);//服务器响应消息的长度,以字节为单位。
    ​
            this.info.messageNumber++
            this.info.receiveMsg = str
            this.info.ipAddress = value.remoteInfo.address
            this.info.protocolType = value.remoteInfo.family
            this.info.portNumber = value.remoteInfo.port
            this.info.sizeMsg = value.remoteInfo.size
          });
        });
    ​
      }
    ​
      // 向客户端发送数据
      sendMsg() {
        let tcpSendOptions: socket.TCPSendOptions = {} as socket.TCPSendOptions;
        tcpSendOptions.data = 'this data is from server';
        this.clientRef?.send(tcpSendOptions).then(() => {
          printLog(TAG, '服务端向客户端发送数据成功');
        }).catch((err: Object) => {
          printLog(TAG,`服务端向客户端发送数据失败,info = ${JSON.stringify(err)}`);
        });
      }
    ​
      // 关闭与客户端的连接
      closeClient(){
        this.clientRef?.close().then(() => {
          printLog(TAG, '服务端主动关闭与客户端的链接,成功');
        }).catch((err: BusinessError) => {
          printLog(TAG, '服务端主动关闭与客户端的链接,失败');
        });
      }
    ​
      cancelEventSub(){
        // 取消TCPSocketConnection相关的事件订阅
        setTimeout(() => {
          this.clientRef?.off("message");
          this.clientRef?.off("close");
        }, 1 * 1000);
    ​
        // 取消TCPSocketServer相关的事件订阅
        setTimeout(() => {
          this.tcpServer.off("connect");
        }, 2 * 1000);
      }
    }
    ​
    function printLog(tag: string,info: string){
      hilog.info(0xff00,tag,info)
    }
    

3

在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
上面更多鸿蒙最新技术知识点,请前往作者博客:https://gitee.com/li-shizhen-skin/zhihu/blob/master/README.md

  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值