本文概述
-
问题背景:由于缺少HarmonyOS NEXT 真机,无法处理网络互通测试需求。因此,尝试探索模拟器互通
-
环境准备:
-
安装最新版DevEco Studio
-
[申请最新版模拟器]
-
-
新建两个模拟器
-
演示视频:工程结构 + 操作方法 + 运行效果
核心步骤
-
由于两个模拟器均位于同一
PC
,其IP
及端口号均一致,因此,我需做端口转发即可 -
使用
hdc
查询模拟器id 与模拟器对应关系:需提前打开模拟器-
概述:
-
红色框内即为模拟器id
-
只开一个,并执行
hdc list targets
即可进行区分
-
-
对应关系:
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//执行端口转发任务
-
运行截图:
-
第一条:此时无端口转发任务
-
第二条:执行端口转发任务
-
第三条:查询端口转发任务,佐证第二条是否执行成功
-
-
代码运行步骤
- 先运行服务端,再运行客户端
- 详细步骤,请查阅演示视频
完整代码
-
公共参数:
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) }
在最后
如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
上面更多鸿蒙最新技术知识点,请前往作者博客:https://gitee.com/li-shizhen-skin/zhihu/blob/master/README.md