Harmony os WebSocket 实战:在 HarmonyOS 里搞定双向实时通信(顺便记点踩坑)
本文介绍了在HarmonyOS中使用WebSocket实现双向实时通信的实践方法。文章首先对比了WebSocket与HTTP的区别,指出WebSocket适合聊天室、实时游戏等场景。然后详细讲解了HarmonyOS中WebSocket的客户端和服务端功能,包括连接管理、消息收发等核心API。实战部分提供了从零开始连接WebSocket服务器的完整代码示例,包括事件订阅、消息处理等关键环节。最后简要
Harmony os WebSocket 实战:在 HarmonyOS 里搞定双向实时通信(顺便记点踩坑)
鸿蒙第四期活动
这两天在看 HarmonyOS 的 Network Kit,顺手把 WebSocket 这一块啃了一遍,准备以后在“在线聊天 / 实时通知 / 小游戏联机”这些场景里直接用上。怕自己过两天忘了,就当写一篇学习笔记,也顺便留给以后来的自己查。
下面都是基于官方最新文档 + 我自己理解整理的,有不严谨的地方,以实际 API 文档为准。developer.huawei.com
一、先搞清楚:为什么是 WebSocket,而不是 HTTP?
简单粗暴地说:
- HTTP:
- 请求 → 响应,完事就断开
- 适合:一次性拿数据,比如首屏加载、详情页拉接口
- WebSocket:
- 一次握手,后面连接常驻
- 全双工:客户端、服务端都能主动发消息
- 适合:
- 聊天室
- 实时游戏
- 实时行情 / 实时通知
- 在线协作编辑
在 HarmonyOS 里,WebSocket 是通过 @kit.NetworkKit 提供的,既有 client 也有 server 能力(注意:服务端目前只支持智慧屏,而且是从 API version 19 开始才有服务端)。developer.huawei.com
二、HarmonyOS WebSocket 能力总览(我自己先给它捋一遍)
官方帮我们准备好了两套东西:
- 客户端能力(大部分应用都会用到的)
createWebSocket():创建客户端对象connect(url, cb):连接服务器on('open' | 'message' | 'close' | 'error'):订阅各种事件send():发消息(文本 / 二进制)close():主动断开连接- 支持心跳机制:
pingInterval+pongTimeout,不响应自动断开developer.huawei.com+1
- 服务端能力(目前 only 智慧屏)
createWebSocketServer():创建服务端start(config):设置端口、最大连接数等on('connect' | 'messageReceive' | 'close' | 'error')send():给某个 client 发消息listAllConnections():列出所有连接close(connection):关掉某个客户端连接stop():停服
一句话总结:
客户端这一套跟浏览器 WebSocket 很像,只是多了 BusinessError;
服务端则是“轻量 WebSocket server”,适合跑在智慧屏上做一些本地小服务。
三、客户端开发:从 0 到连上一个 WebSocket 服务器
1. 导入模块
import { webSocket } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
这两个应该已经很眼熟了,NetworkKit 负责网络,BusinessError 负责标准错误结构。
2. 创建 WebSocket 对象 & 准备 URL
let ws = webSocket.createWebSocket();
// 这里替换成你自己的 ws 地址
const WS_URL = 'ws://your.server.com:8080/ws';
后面所有操作都是基于这个 ws 来玩的。
3. 订阅几个关键事件(这是 WebSocket 的灵魂)
我一般是按这几个顺序写:open → message → close → error。
// 1)连接成功
ws.on('open', (err: BusinessError, value: Object) => {
if (err) {
console.error('WebSocket open failed: ' + JSON.stringify(err));
return;
}
console.info('WebSocket opened: ' + JSON.stringify(value));
// 连接成功后先发一条测试消息
ws.send('Hello, server!', (err: BusinessError, ok: boolean) => {
if (err) {
console.error('send failed: ' + JSON.stringify(err));
} else {
console.info('send ok: ' + ok);
}
});
});
// 2)收到消息
ws.on('message', (err: BusinessError, value: string | ArrayBuffer) => {
if (err) {
console.error('on message error: ' + JSON.stringify(err));
return;
}
console.info('收到消息:' + value);
// 假设约定收到 "bye" 就主动断开
if (value === 'bye') {
ws.close((err: BusinessError, ok: boolean) => {
if (err) {
console.error('close failed: ' + JSON.stringify(err));
} else {
console.info('close ok: ' + ok);
}
});
}
});
// 3)连接关闭
ws.on('close', (err: BusinessError, result: webSocket.CloseResult) => {
if (err) {
console.error('on close error: ' + JSON.stringify(err));
return;
}
console.info(`连接关闭,code=${result.code}, reason=${result.reason}`);
});
// 4)异常
ws.on('error', (err: BusinessError) => {
console.error('WebSocket error: ' + JSON.stringify(err));
});
这里有几个小点我自己记了一下:
message的value可能是string也可能是ArrayBuffer,二进制就要自己转。close回调里的CloseResult里面有code和reason,做调试特别好用。error事件最好统一打日志,不然有时候你根本不知道连接在哪一步又挂了。
4. 正式发起连接
ws.connect(WS_URL, (err: BusinessError, ok: boolean) => {
if (err) {
console.error('连接失败:' + JSON.stringify(err));
return;
}
console.info('连接结果:' + ok);
});
到这一步,如果服务端正常,日志里就会先看到 open,然后你发的 Hello, server! 就应该在对端收到了。
四、服务端开发:在智慧屏上跑一个轻量 WebSocket Server
这一块目前只支持智慧屏,而且是从 API version 19 开始。也就是说,你在手机上是没法直接开这个 Server 的。developer.huawei.com
1. 导入模块 & 配置参数
import { webSocket } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
let localServer: webSocket.WebSocketServer;
const config: webSocket.WebSocketServerConfig = {
serverPort: 8080, // 监听端口
maxConcurrentClientsNumber: 10, // 最大并发客户端数
maxConnectionsForOneClient: 10 // 单 client 最大连接数
};
这里就已经能看出来,它更偏向本地小规模实时场景,不是拿来顶一个大规模公网服务的。
2. 创建 WebSocketServer 并监听事件
localServer = webSocket.createWebSocketServer();
// 新客户端连接
localServer.on('connect', async (connection: webSocket.WebSocketConnection) => {
console.info(`新客户端连上了,ip=${connection.clientIP}, port=${connection.clientPort}`);
// 主动给它打个招呼
localServer.send("Hello, I'm server!", connection)
.then((ok: boolean) => {
console.info('send to client: ' + ok);
})
.catch((err: BusinessError) => {
console.error(`send failed, code=${err.code}, msg=${err.message}`);
});
// 看一下当前总共有多少连接(可选)
try {
const all = await localServer.listAllConnections();
console.info(`当前连接数量:${all.length}`);
} catch (error) {
console.error(`listAllConnections failed: ${JSON.stringify(error)}`);
}
});
// 收到客户端消息
localServer.on('messageReceive', (message: webSocket.WebSocketMessage) => {
try {
console.info(`收到消息,data=${message.data}`);
// 简单 echo 一下
localServer.send(`echo: ${message.data}`, message.clientConnection);
// 如果对方发 "bye",就把这个连接关掉
if (message.data === 'bye') {
localServer.close(message.clientConnection)
.then((ok: boolean) => {
console.info('关闭客户端连接结果:' + ok);
})
.catch((err: BusinessError) => {
console.error('关闭客户端失败:' + JSON.stringify(err));
});
}
} catch (error) {
console.error(`messageReceive 处理失败: ${JSON.stringify(error)}`);
}
});
// 有客户端断开
localServer.on('close', (clientConn: webSocket.WebSocketConnection, reason: webSocket.CloseResult) => {
console.info(`客户端断开:code=${reason.code}, reason=${reason.reason}`);
});
// 服务端异常
localServer.on('error', (error: BusinessError) => {
console.error(`server error, code=${error.code}, msg=${error.message}`);
});
3. 启动 & 停止服务
// 启动
localServer.start(config)
.then((ok: boolean) => {
console.info('WebSocket server 启动结果:' + ok);
})
.catch((err: BusinessError) => {
console.error(`server 启动失败:code=${err.code}, msg=${err.message}`);
});
// 不再需要时停止
localServer.stop()
.then((ok: boolean) => {
console.info('server 停止结果:' + ok);
})
.catch((err: BusinessError) => {
console.error(`server 停止失败:${JSON.stringify(err)}`);
});
一般我自己的习惯是:
- 在 Ability 销毁前确保
stop()了 - 不然容易有意料之外的端口占用 / 资源没释放干净的情况。
五、心跳机制:pingInterval & pongTimeout 怎么理解?
这个机制说白了就是防“假死”连接:developer.huawei.com+1
- 建立连接后,客户端会按
pingInterval秒发送 Ping 帧 给服务端 - 如果服务端支持 WebSocket 协议,收到 Ping 会自动回复 Pong 帧
- 如果在
pongTimeout秒内没收到 Pong,客户端就会认为连接异常,主动断开
官方还多补了两个点:developer.huawei.com
pongTimeout必须 ≤pingInterval- 两者都有最大 / 最小值限制
实际项目里可以这么玩:
- 你的业务对“实时性”的容忍度高一点,比如聊天消息 10 秒内能重连回来就行:
pingInterval可以设长一点,比如 30s- 减少电量 & 流量消耗
- 对实时性要求比较高,比如在线对战小游戏:
pingInterval可以设短一点,比如 5s- 这样掉线能更快感知到,然后做重连
小建议:
- 心跳 + 手动重连配合使用效果更好
- 重连记得做“指数退避”,别一秒钟疯狂重连把服务器打爆
六、我自己踩过/想到的一些注意点
最后列几个我觉得容易忽略的点,当备忘录用:
- URL 必须是 ws:// 或 wss://
写成 http:// 就直接拒绝你,别问为什么,协议都不对。 - 服务端只跑在智慧屏上
不能指望在手机上开 WebSocketServer 然后当总后台用,它的应用场景更偏“本地小服务”。 - 权限 & 配置别忘了
- 网络访问相关的权限、配置要在工程里配好
- 不然后台抓半天包都连不上,其实根本没权限出网
- 处理好二进制数据
message回调的value可能是ArrayBuffer,这时候要自己用DataView/Uint8Array解析,别简单当 string 打印。 - 日志一定要写全
open / message / close / error每个环节都打日志- 线上问题 80% 靠日志排
- 跟后端提前约定好协议内容
比如这篇文档里用bye作为断开连接的示例字段,这种东西一定要跟后端提前统一,不然一个写 “bye” 一个写 “BYE” 就是永远收不到。
七、总结一句:WebSocket 在 HarmonyOS 里用起来并不难
整体用下来,我的感受是:
- 如果你写过浏览器里的
new WebSocket(url),上手 HarmonyOS 的 WebSocket 客户端几乎没学习成本; - 服务端这一块,适合以后做一些“智慧屏 + 本地设备”的联动,比如:
- 智慧屏做控制中心
- 手机/IoT 设备当客户端连过来
更多推荐
所有评论(0)