在过去很长一段时间,我都非常希望能够将OpenHarmony与HarmonyOS设备进行一个联动,但是碍于一些底层接口未完善一直无法实现。但是在前几个月,OpenHarmony3.1 带来了更多可能。本次,我将分享如何在搭载HarmonyOS的手机和搭载OpenHarmony的开发板上,实现socket对话!
一、效果演示
二、HarmonyOS侧
1、新建一个JAVA工程,编写简单的测试页面
- ability_main.xml (主页面)。
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:alignment="center"
ohos:orientation="vertical">
<TextField
ohos:id="$+id:sendfield"
ohos:height="200px"
ohos:width="800px"
ohos:bottom_margin="32vp"
ohos:text_size="32fp"
ohos:hint="请输入发送信息"
ohos:background_element="$graphic:tfbg"
/>
<Button
ohos:id="$+id:btn"
ohos:height="120px"
ohos:width="300px"
ohos:text="启动"
ohos:text_size="32fp"
ohos:background_element="$graphic:btn"
/>
<Text
ohos:id="$+id:text"
ohos:top_margin="50vp"
ohos:height="match_content"
ohos:width="match_content"
ohos:text_size="32fp"
ohos:text="暂无消息"
/>
<Text
ohos:id="$+id:localip"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="本机IP"
ohos:top_margin="30vp"
ohos:text_size="32fp"
/>
<TextField
ohos:id="$+id:textfield"
ohos:height="200px"
ohos:width="800px"
ohos:top_margin="50vp"
ohos:text_size="32fp"
ohos:hint="请输入IP地址"
ohos:background_element="$graphic:tfbg"
/>
</DirectionalLayout>
这是我自己编写的一个测试页面,但不是最重要的。
2、编写socket功能
目前,鸿蒙的Socket通信功能只能在JAVA侧实现,并且官网对相关的功能解析不够全面,但是足够实现UDP通信。
这里直接参考官网,可以快速实现Socket功能,传送门:JAVA-socket。
在MainAbilitySlice中编写两个主要函数,并结合个人情况绑定到测试按键上即可。
- StartServer()。
HiLog.info(LABEL_LOG, "StartServer run");
NetManager netManager = NetManager.getInstance(null);
if (!netManager.hasDefaultNet()) {
HiLog.error(LABEL_LOG,
"netManager.hasDefaultNet() failed");
return;
}
NetHandle netHandle = netManager.getDefaultNet();
DatagramSocket socket = null;
// 通过Socket绑定来进行数据传输
try {
HiLog.info(LABEL_LOG, "wait receive data");
//通过getLocalIpAddress()快速获取本机的IP地址
InetAddress address = netHandle.getByName(getLocalIpAddress());
//端口号+主机地址
socket = new DatagramSocket(10006, address);
netHandle.bindSocket(socket);
/*至此绑定Socket结束*/
HiLog.info(LABEL_LOG, "绑定成功");
/*监听函数*/
byte[] buffer = new byte[1024];
DatagramPacket response = new DatagramPacket(buffer,
buffer.length);
while (true) {
//接收数据
socket.receive(response);
int len = response.getLength();
HiLog.info(LABEL_LOG, "接收成功");
//将数据打印到屏幕上
s = new String(buffer, StandardCharsets.UTF_8).substring(0,
len);
//一个textfield组件
text.setText(s);
HiLog.info(LABEL_LOG, "receive data: " + s);
}
} catch (IOException e) {
HiLog.error(LABEL_LOG, "rev IOException: ");
}
}
- sendMessage()。
NetManager netManager = NetManager.getInstance(null);
if (!netManager.hasDefaultNet()) {
HiLog.error(LABEL_LOG,
"netManager.hasDefaultNet() failed");
return;
}
NetHandle netHandle = netManager.getDefaultNet();
// 通过Socket绑定来进行数据传输
DatagramSocket socket = null;
try {
//从textfield组件获取用户输入的对端ip地址
HOST = iptf.getText();
InetAddress address = netHandle.getByName(HOST);
socket = new DatagramSocket();
netHandle.bindSocket(socket);
/*至此 已绑定对端Socket*/
//从一个textfield组件获取用户输入的要发送的信息。
String data = new String(sendtf.getText());
//这里默认还是发送至对端的10006端口
DatagramPacket request = new DatagramPacket(data.getBytes(
StandardCharsets.UTF_8), data.length(),
address, PORT);
// buffer赋值
// 发送数据
socket.send(request);
HiLog.info(LABEL_LOG, "send data: " + data);
} catch (IOException e) {
HiLog.error(LABEL_LOG, "send IOException: ");
} finally {
if (null != socket) {
}
}
- getlocalip()。
private String getLocalIpAddress() {
WifiDevice wifiDevice = WifiDevice.getInstance(getContext());
Optional<IpInfo> ipInfo = wifiDevice.getIpInfo();
int ip = ipInfo.get().getIpAddress();
return (ip & 0xFF) + "." + ((ip >> 8) & 0xFF) + "." + ((ip >> 16) & 0xFF) + "." + (ip >> 24 & 0xFF);
}
至此,接收信息和发送信息的函数都编写完了。但并没有结束,这样的函数并不能跑在UI线程上,我们必须让其在其他线程上运作,那必须请出我们最爱的EventRunner.
3、编写Mythread类
package com.example.hoop.util;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
public class Mythread {
public static void inUI(Runnable runnable) {
//返回主线程
EventRunner runner = EventRunner.getMainEventRunner();
EventHandler eventHandler = new EventHandler(runner);
eventHandler.postSyncTask(runnable);
}
public static void inBG(Runnable runnable) {
EventRunner runner = EventRunner.create(true);
EventHandler eventHandler = new EventHandler(runner);
//投递任务,我们的Socket函数应该投递于此。
eventHandler.postTask(runnable, 0, EventHandler.Priority.IMMEDIATE);
}
}
- 将任务投递给EventRunner。
只需要将刚刚的两个主要函数投递就行了。
private void sendMessage() {
Mythread.inBG(new Runnable() {
@Override
public void run() {
NetManager netManager = NetManager.getInstance(null);
if (!netManager.hasDefaultNet()) {
HiLog.error(LABEL_LOG,"netManager.hasDefaultNet() failed");
return;
}
NetHandle netHandle = netManager.getDefaultNet();
// 通过Socket绑定来进行数据传输
DatagramSocket socket = null;
try {
//从textfield组件获取用户输入的对端ip地址
HOST=iptf.getText();
InetAddress address = netHandle.getByName(HOST);
socket = new DatagramSocket();
netHandle.bindSocket(socket);
/*至此 已绑定对端Socket*/
//从一个textfield组件获取用户输入的要发送的信息。
String data = new String(sendtf.getText());
//这里默认还是发送至对端的10006端口
DatagramPacket request = new DatagramPacket(data.getBytes(StandardCharsets.UTF_8), data.length(), address, PORT);
// buffer赋值
// 发送数据
socket.send(request);
HiLog.info(LABEL_LOG,"send data: " + data);
} catch (IOException e) {
HiLog.error(LABEL_LOG,"send IOException: ");
} finally {
if (null != socket) {
}
}
}
});
}
private void StartServer() {
Mythread.inBG(new Runnable() {
@Override
public void run() {
HiLog.info(LABEL_LOG,"StartServer run");
NetManager netManager = NetManager.getInstance(null);
if (!netManager.hasDefaultNet()) {
HiLog.error(LABEL_LOG,"netManager.hasDefaultNet() failed");
return;
}
NetHandle netHandle = netManager.getDefaultNet();
DatagramSocket socket = null;
// 通过Socket绑定来进行数据传输
try {
HiLog.info(LABEL_LOG,"wait receive data");
//通过getLocalIpAddress()快速获取本机的IP地址
InetAddress address = netHandle.getByName(getLocalIpAddress());
//端口号+主机地址
socket = new DatagramSocket(10006, address);
netHandle.bindSocket(socket);
/*至此绑定Socket结束*/
HiLog.info(LABEL_LOG,"绑定成功");
/*监听函数*/
byte[] buffer = new byte[1024];
DatagramPacket response = new DatagramPacket(buffer, buffer.length);
while(true){
//接收数据
socket.receive(response);
int len = response.getLength();
HiLog.info(LABEL_LOG,"接收成功");
//将数据打印到屏幕上
s = new String(buffer, StandardCharsets.UTF_8).substring(0, len);
Mythread.inUI(new Runnable() {
@Override
public void run() {
//一个textfield组件
text.setText(s);
}
});
HiLog.info(LABEL_LOG,"receive data: " + s);
}
} catch (IOException e) {
HiLog.error(LABEL_LOG,"rev IOException: " );
}
}
});
}
三、OpenHarmony侧
1、新建工程,编写测试页面
- index.hml。
<div class="container">
<text class="title">
{{ title }}
</text>
<!-- <div style="width: 30%;height: 30%;background-color: blueviolet;">-->
<!-- </div>-->
<!-- <input type="text" id="localip" placeholder="输入本机IP"-->
<!-- @change="newlocalip">-->
<!-- </input>-->
<text style="width: 300px;height: 70px;">
本机IP{{getIpAddress()}}
</text>
<button type="capsule" style="width: 300px;height: 70px;" onclick="creatScoket"> 点击创建 </button>
<input id="remoteip" placeholder="输入服务器IP" @change="newremoteip" />
<button type="capsule" style="width: 300px;height: 70px;margin-top: 5%;" onclick="sendMessage"> 点击连接 </button>
<input placeholder="输入需要发送的信息" type="text" enterkeytype="done" @change="newMessage" />
</div>
2、编写Socket功能
import socket from '@ohos.net.socket';
import wifi from '@ohos.wifi';
export default {
data: {
title: "等待新的消息...",
tcp: socket.constructTCPSocketInstance(),
remoteip:'none',
localip:'none',
msg:"A NEW WORLD",
udp:socket.constructUDPSocketInstance(),
},
onInit() {
this.getIpAddress();
this.creatScoket();
this.title="暂无新消息"
},
//创建udpSocket 默认端口10006
creatScoket: async function(){
this.udp.bind({address: this.getIpAddress(), port: 10006, family: 1}, err => {
if (err) {
console.info('bind fail');
this.title='bind fail'
return;
}
this.title='bind success';
console.info('bind success');
})
// this.tcp.bind({address: this.localip, port: 10006,family: 1}, err => {
// if (err) {
// console.log('bind fail');
// this.title='bind fail';
// return;
// }
// this.title='bind success';
// console.log('bind success');
// })
//监听收到的信息 打印到屏幕上
this.udp.on('message', value => {
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))
}
this.title =str;
});
},
sendMessage: async function(){
// let promise1 = this.udp.connect({ address: {address: this.remoteip, port: 10006, family: 1} , timeout: 6000});
// promise1.then(() => {
// console.log('connect success');
// let promise2 =this.udp.send({
// data:this.msg
// });
// promise2.then(() => {
// console.log('send success');
// }).catch(err => {
// console.log('send fail');
// });
// }).catch(err => {
// this.title='connect fail';
// console.log('connect fail');
// });
//发送信息
let promise = this.udp.send({
data:this.msg,
address: {
address:this.remoteip,
port:10006,
family:1
}
});
promise.then(() => {
this.title='send success';
console.info('send success');
}).catch(err => {
this.title='send fail'
console.info('send fail');
});
},
newlocalip(e){
this.localip = e.value;
this.title = e.value;
},
newremoteip(e){
this.remoteip = e.value;
},
newMessage(e){
this.msg=e.value;
this.title=e.value;
},
//获取本机ip地址
getIpAddress(){
let ip=wifi.getIpInfo().ipAddress;
this.localip = (ip >> 24 & 0xFF)+"."+ ((ip >> 16) & 0xFF)+"."+((ip >> 8) & 0xFF)+"."+(ip & 0xFF);
},
}
四、测试
这里为了便于测试,我们最好下载一个网络调试助手.
1、结果
- OpenHarmony侧
- HarmonyOS侧
- OpenHarmony&HarmonyOS 联动
五、结语
实现了OpenHarmony与HarmonyOS的简单通讯后,应用开发,软硬件联动都有了更多的可能性。目前可能存在一些稳定性,不完整性问题,未来两侧基础功能不断完善之后,将会变得更有趣也更好用。如有明显错误,希望读者能够积极提出,我会尽力完善。
© 版权声明
文章版权归作者所有,未经允许请勿转载。