OpenHarmony之 网络管理 Socket 模块的使用 原创 精华
OpenHarmony之 网络管理 Socket 模块
@toc
1.介绍
Socket 模块可以用来进行数据传输,支持TCP和UDP两种协议。
本期将为您展示一下:
如何 使用 Socket模块实现 DAYU200开发板 和 Windows PC (SocketTool 工具)的之间的数据传输。
演示环境:
DAYU200 软件版本:OpenHarmony 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
样例使用OpenHarmony SDK版本:
“compileSdkVersion”: 8,
“compatibleSdkVersion”: 8,
2.开发步骤
2.1 权限声明
创建项目后,打开项目config.json 配置文件,在module内添加权限声明
"reqPermissions": [
{
"name": "ohos.permission.INTERNET" //socket权限
},
{
"name": "ohos.permission.GET_WIFI_INFO" //获取wifi地址权限
}
]
2.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', //要通信的 PC地址,CMD--->ipconfig查看
family: 1,
port: 0 //UDP:7001/TCP:8001
}
-
(可选)订阅TCPSocket相关的订阅事件。
订阅了connect 、message、close 事件。message事件的回调中可以获取接收到的数据,但是一个ArrayBuffer,需要通过resolveArrayBuffer函数进行进一步解析。
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');
}
2.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'); }
3.效果展示
1). 把DAYU200开发板连上wifi(demo会显示获取的地址),注意和电脑连接的wifi相同。
2). windows系统上打开SocketTool 工具(附件已上传),分别创建TCP Server、UDP Server,监听端口分别为8001、7001
3). 点击,DAYU200开发板应用上 “连接” 按钮,Socket工具显示已连接。
4). 点击 DAYU200开发板应用上 “发送TCP消息” 按钮,Socket工具显示接收到的消息。
5). Socket工具上输入要回复的消息,点击 “发送数据” 按钮
6). 点击 DAYU200开发板应用上 “发送UDP消息” 按钮,Socket工具UDP Server 栏 显示接收到的消息。
7). Socket工具上输入要回复的消息 ,点击 “发送数据” 按钮。
4.思考总结
-
TCP是面向连接的协议,在收发数据前必须和对方建立可靠的连接,UDP是一个面向无连接的协议,数据传输前,源端和终端不建立连接,所以TCP的方式比UDP多一个connect的过程。
-
getState接口的状态值有3个,isBound、isClose 是udp/tcp 都会用,isConnected是给tcp用的 。{“isBound”:true,“isClose”:false,“isConnected”:true}
-
用Socket 工具回复消息后,DAYU200侧如果要显示接收到的消息,需要再点发送消息一次(该问题已通过升级DAYU200 软件版本 OpenHarmony 3.2.2.3+ 得到解决)。
5.参考资料
1.HarmonyOS API参考
2.OpenHarmony Gitee 样例指导
很实用的讲解,收藏了
nice!您好,这是在3.1release 运行通过的么? 我记得之前测试这个只能bind和连接上ip,收发数据没反应。😂
您好,可以看了一下这个文档(https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/connectivity/socket-connection.md)是在哪个分支上的,文档是在最新master分支上的,3.1release分支上没有这个文档。应该不是在3.1release上运行通过的
是。
如果把DAYU200 系统升级到 OpenHarmony_3.2.2.3(每日构建下载),“用Socket 工具回复消息后,DAYU200侧如果要显示接收到的消息,需要再点发送消息” 这个问题也得到了解决。
好嘞,谢谢啦,我去试试。
好的,我去仔细看下版本区别。
确认了,这是master分支,不是release上的。release网络与连接文档只有RPC通信。
厉害了,之前研究了半天,刷了3.2才能收发。Socket目前把广播禁止了,比较的坑
但是我发现通过每日构建下载的OpenHarmony_3.2.2.3,升级后,查看系统版本是3.2.2.5 😂
我也发现这个问题了,昨天安装26号编译的3.2.2.2版本,设置里看是3.2.3.2.
另外我看您这个帖子ets源码后确实3.1release能发数据,推理可知js也可以。所以今天我退回到了3.1release,发现socket可以发送数据的(js实现),官方的示例代码不可直接放在一个函数里使用,把tcp.send拆开就可以发送了。