鸿蒙 韶非 UI 系列:WebSocket @ohos.net.webSocket,长连接、推送、实时通讯入门
写在前面
如果你写过鸿蒙 ArkUI 应用,大概率遇到过这个场景:
你写了个聊天应用,用 HTTP 轮询每 3 秒拉一次消息——结果电量血崩、消息延迟 3 秒、服务器被打爆。
你想「能不能服务器有消息就推过来」——这就是 WebSocket。
你查文档发现「鸿蒙有 @ohos.net.webSocket」——你点进去发现createWebSocket创建实例、connect(url)建连接、on("open"/"message"/"close"/"error")事件订阅、send(payload)发消息、close()断开——API 一脸懵。
这是「HTTP 轮询」和「WebSocket 长连接」的分水岭。鸿蒙给的实时通讯答案是 @ohos.net.webSocket——createWebSocket 拿实例、connect 建连接、on 订阅事件、send 发消息、close 断开。
本文就用一个真机可跑的「建连接 + 发消息 + 收消息 + 断开」demo,把 WebSocket 从「听名字一脸懵」讲到「下个项目直接抄」。代码托管在 AtomGit,文末有链接,真机实拍截图作证。这是韶非 UI 系列第六篇,接续上五篇 HTTP 网络栈 + 文件 IO + 能力调用 + 后台任务 + 关系数据库。
适合人群:写过鸿蒙应用、被「HTTP 轮询电量血崩」折磨过的同学。
不适合人群:还在学@State的同学——出门左转看我的入门篇。
一、先讲清楚:WebSocket 到底是啥
一句话:WebSocket 是鸿蒙给应用建「全双工长连接」的原生机制,管「建连接 + 收发消息 + 断开」全流程。
你之前写前端 new WebSocket(url) 是浏览器宿主 API——鸿蒙不是浏览器环境,没有这种。WebSocket 是鸿蒙专门给实时通讯的原生机制,底层是 TCP 长连接 + WebSocket 协议握手,能力对标前端的「new WebSocket + EventSource」但更精细可控。
核心 API 一览:
| API | 作用 | 一句话理解 |
|---|---|---|
webSocket.createWebSocket() |
创建 WebSocket 实例 | 「告诉系统我要用长连接」 |
ws.connect(url) |
建连接 | 「TCP 握手 + WebSocket 升级」 |
ws.on("open"/"message"/"close"/"error") |
事件订阅 | 「连接建立/收消息/关闭/错误」 |
ws.send(payload) |
发消息 | 「string 或 ArrayBuffer」 |
ws.close() |
主动断开 | 「客户端先关」 |
记住这五个,往下看。
二、动手:一个建连接 + 发消息 + 收消息 + 断开的 demo
2.1 import + 创建 WebSocket 实例
import webSocket from '@ohos.net.webSocket'
import common from '@ohos.app.ability.common'
@Entry
@Component
struct Index {
private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
private ws: webSocket.WebSocket | null = null
private wsUrl: string = 'wss://echo.websocket.org'
// ...
}
三个细节:
import webSocket from '@ohos.net.webSocket'——webSocket是 WebSocket 的入口模块ws: webSocket.WebSocket | null——存 WebSocket 实例,后续所有操作都走它wsUrl: string = 'wss://echo.websocket.org'——echo 服务器,发啥回啥,适合 demo
2.2 connect + on:建连接 + 事件订阅
async connectWs(): Promise<void> {
this.stateLog = '建 WebSocket 连接中...'
try {
// 创建 WebSocket 实例
this.ws = webSocket.createWebSocket()
// on('open') 连接建立回调
this.ws.on('open', () => {
this.connState = '已连接'
this.stateLog = `WebSocket 已连接,url = ${this.wsUrl}`
})
// on('message') 收消息回调
this.ws.on('message', (err, value) => {
this.recvCount++
// value 可能是 string 或 ArrayBuffer
const text: string = typeof value === 'string' ? value : '(二进制数据)'
this.totalRecv = this.recvCount
this.lastRecv = text
this.stateLog = `第 ${this.recvCount} 次收到消息`
})
// on('close') 连接关闭回调
this.ws.on('close', () => {
this.connState = '已断开'
this.stateLog = 'WebSocket 连接已关闭'
})
// on('error') 错误回调
this.ws.on('error', (err) => {
this.connState = '连接错误'
this.stateLog = `WebSocket 错误:${err?.message ?? err}`
})
// connect 发起连接,返回 Promise<boolean>
const ok: boolean = await this.ws.connect(this.wsUrl)
if (!ok) {
this.stateLog = 'WebSocket 连接失败(返回 false)'
this.ws = null
}
} catch (e) {
this.stateLog = `建连接失败:${e.message}`
this.ws = null
}
}
connect + on 三个关键点:
① on 订阅 4 类事件
ws.on('open', cb) // 连接建立
ws.on('message', cb) // 收消息
ws.on('close', cb) // 连接关闭
ws.on('error', cb) // 错误
事件回调是异步的——connect 返回成功只表示握手发起,真正「连上了」看 on('open')。
② on('message') value 可能是 string 或 ArrayBuffer
ws.on('message', (err, value) => {
const text: string = typeof value === 'string' ? value : '(二进制数据)'
// ...
})
文本消息是 string,二进制消息是 ArrayBuffer。新手最容易忘 typeof 判断,直接当 string 用,二进制消息就炸。
③ connect 返回 Promise<boolean>
const ok: boolean = await this.ws.connect(this.wsUrl)
ok = true 表示握手发起成功,不代表「连上了」——「连上了」看 on('open')。
2.3 send:发消息
async sendMsg(): Promise<void> {
if (!this.ws) {
this.stateLog = '尚未建连接,无法发消息'
return
}
try {
this.sendCount++
const payload: string = `hello-${this.sendCount}-${Date.now()}`
// send 发消息,返回 Promise<boolean>
const ok: boolean = await this.ws.send(payload)
if (ok) {
this.totalSent = this.sendCount
this.stateLog = `第 ${this.sendCount} 次发送成功:${payload}`
} else {
this.stateLog = `第 ${this.sendCount} 次发送失败(返回 false)`
}
} catch (e) {
this.stateLog = `发消息失败:${e.message}`
}
}
send 三个关键点:
① send(payload) 支持 string 或 ArrayBuffer
await this.ws.send('hello') // 文本
await this.ws.send(new ArrayBuffer(8)) // 二进制
② send 返回 Promise<boolean>
const ok: boolean = await this.ws.send(payload)
ok = true 表示发送成功(底层 TCP 缓冲区接收了)。
③ send 不等回消息
send 是单向的——发出去就完。回消息看 on('message') 异步到达。
2.4 close:断开连接
async closeWs(): Promise<void> {
if (!this.ws) {
this.stateLog = '尚未建连接,无需断开'
return
}
try {
// close 主动断开,返回 Promise<boolean>
const ok: boolean = await this.ws.close()
this.stateLog = `已断开(ok=${ok})`
this.connState = '已断开'
this.ws = null
} catch (e) {
this.stateLog = `断开失败:${e.message}`
}
}
close 三个关键点:
① close 返回 Promise<boolean>
const ok: boolean = await this.ws.close()
② close 后会触发 on('close')
主动 close 和服务端断开都会触发 on('close')——用这个回调统一处理「连接已关」。
③ close 后 ws 实例不能复用
close 后 ws 实例作废,要重连得重新 createWebSocket + connect。
三、真机实拍:建连接 + 发消息 + 收消息全跑通
我把这个 demo 装到真机上跑(鸿蒙 6.1.1.125, API 24),依次点 ① 建连接 + ② 发消息,下面两张都是真机实拍,没有任何 P 图。
初始态:WebSocket Demo 标题 + 连接状态「未连接」+ ① 建连接 / ② 发消息 / ③ 断开连接三按钮 + 总发送/总接收指标区 + 最近收到消息区 + 关键 API 说明区:

点 ① 建连接 + ② 发消息后状态:连接状态「已连接」+ 状态日志「WebSocket 已连接」+ 总发送/总接收指标更新 + 最近收到消息显示 echo 服务器返回内容:

重点看第二张:连接状态「已连接」+ 总发送/总接收指标更新——
createWebSocket+connect+on+send全跑通了。最近收到消息显示 echo 服务器返回内容——on('message')真收到消息了。这是 WebSocket 五大 API 全跑通的真机证明。
四、@ohos.net.webSocket vs 前端 new WebSocket:啥差异
新手最容易纠结的问题:既然前端 new WebSocket(url) 那么标准,鸿蒙为啥要造自己的 WebSocket?
| 维度 | 前端 new WebSocket |
@ohos.net.webSocket |
|---|---|---|
| 运行环境 | 浏览器宿主 | 鸿蒙原生运行环境 |
| 实例创建 | new WebSocket(url) |
webSocket.createWebSocket() |
| 建连接 | 构造函数内自动连 | await ws.connect(url) 显式 |
| 事件订阅 | ws.onopen = cb |
ws.on('open', cb) |
| 收消息 | ws.onmessage = cb |
ws.on('message', cb) |
| 发消息 | ws.send(data) 同步 void |
await ws.send(data) Promise |
| 安全模型 | 同源策略 + wss | 鸿蒙沙箱 + 网络权限 |
一句话决策:鸿蒙应用实时通讯必须用 @ohos.net.webSocket,不能用 new WebSocket(不存在)。鸿蒙不是浏览器,这套原生 WebSocket 封装更安全可控。
五、常见坑(都是血泪)
| 坑 | 症状 | 解法 |
|---|---|---|
用 new WebSocket |
编译报错「找不到 WebSocket」 | 鸿蒙用 @ohos.net.webSocket,没浏览器宿主 API |
on('message') value 直接当 string |
二进制消息炸 | typeof value === 'string' 判断 |
connect 成功就以为「连上了」 |
on('open') 还没触发就发消息 |
看 on('open') 才算真连上 |
close 后复用 ws 实例 |
报错「实例已关」 | 重新 createWebSocket + connect |
| 忘了网络权限 | 建连接报权限错 | module.json5 加 ohos.permission.INTERNET |
| 长连接不心跳 | 运营商 NAT 超时断开 | 30-60s 发一次心跳包 |
收消息改 @State 不在主线程 |
UI 不更新 | setTimeout 切主线程再改 |
六、webSocket 安全模型
鸿蒙 WebSocket 受安全约束——网络权限 + 沙箱隔离:
| 安全机制 | 含义 |
|---|---|
| 网络权限 | 需 ohos.permission.INTERNET 才能建连接 |
| 沙箱隔离 | 应用级 WebSocket,其他应用默认访问不到 |
| wss 协议 | 加密 WebSocket,生产环境必用 |
| 证书校验 | wss 自签证书需 on('message') 校验 |
这是鸿蒙安全模型的硬约束——比浏览器同源策略严,但比 iOS ATS 松(鸿蒙允许 ws 明文 + 自签证书,生产环境建议 wss)。
七、完整代码仓库
本文所有代码都已托管到 AtomGit,欢迎 clone、提 issue、点 star:
🔗 仓库地址:https://atomgit.com/JaneConan/arkui-websocket
仓库包含:
- 完整的「建连接 + 发消息 + 收消息 + 断开」demo 工程
Index.ets主页面(createWebSocket+connect+on+send+close五姿势)on('open'/'message'/'close'/'error')事件订阅 + string/ArrayBuffer 类型判断- 可直接用 DevEco Studio 打开运行(真机装普通应用必能跑,需配网络权限)
八、下一步该学什么?
跑通这个 demo 之后,你的鸿蒙实时通讯就入门了。这是韶非 UI 系列第六篇,后续按这个顺序往下:
- 媒体访问
@ohos.file.photoAccessHelper(下一篇):访问相册、扫描媒体文件,应用调系统相册必学 - 推送通知
@ohos.notificationManager:通知栏展示、点击拉起,离线触达必学 - 相机
@ohos.multimedia.camera:预览、拍照、录像,相机应用必学 - 动画
@ohos.arkui.animation:属性动画、转场动画,UI 进阶必学 - 其他:
@ohos.multimedia.audio录音播放、@ohos.bluetooth蓝牙、@ohos.sensor传感器,应用领域专属
写在最后
@ohos.net.webSocket 的本质,是**「鸿蒙给应用建全双工长连接的原生机制」**——不是浏览器 new WebSocket,是鸿蒙专门给实时通讯的原生机制,能力对标「new WebSocket + EventSource」但更安全可控。代价是 createWebSocket 多一步、on 事件订阅多一步。
一旦你开始用 WebSocket 思维写实时应用,你会发现大部分「聊天应用收推」「股票应用实时报价」「协同编辑应用同步」的需求,都是 createWebSocket + connect + on + send 的自然结果。代码量比 HTTP 轮询少一半,实时性高九成。
代码已经给你了,仓库链接在上面。现在关掉这篇文章,打开 DevEco Studio,把 demo 跑起来,亲手点建连接发消息感受下全双工长连接。
跑通了,回来评论区打个「1」,我看看有多少人真的动手了。🚀
作者:JaneConan
仓库:https://atomgit.com/JaneConan/arkui-websocket
协议:Apache-2.0,随便用,别告我
更多推荐



所有评论(0)