更新记录
1.0.0(2026-10-31)
初始版本:WebSocket / TCP / UDP 多会话(Android / iOS / 鸿蒙)
- 事件:onOpen / onMessage / onClose / onError;文本 utf8 + 二进制 base64
- Android:OkHttp + Socket + DatagramSocket;iOS:URLSessionWebSocket + NWConnection;鸿蒙:
@kit.NetworkKit(含 ohos.permission.INTERNET)
- JS 托管
createManagedSocket:帧协议(length2 / length4 / delimiter)、心跳、空闲超时、自动重连、状态机、发送队列
- TCP 原生收包统一 base64;组件
<zv-socket>
平台兼容性
uni-app(4.41)
| Vue2 |
Vue3 |
Chrome |
Safari |
app-vue |
app-nvue |
Android |
iOS |
鸿蒙 |
| √ |
√ |
- |
- |
- |
- |
- |
- |
- |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| - |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
zv-socket
原生 Socket:WebSocket + TCP + UDP(Android / iOS / 鸿蒙)+ JS 托管层(帧协议 / 心跳 / 重连 / 发送队列)。
本人设备有限,没有做全部测试,请先试用,确认正常后再购买
| 类型 |
Android |
iOS |
鸿蒙 |
说明 |
ws |
✅ OkHttp |
✅ URLSessionWebSocket |
✅ @kit.NetworkKit webSocket |
文本为主;二进制用 base64 |
tcp |
✅ java.net.Socket |
✅ NWConnection |
✅ socket.TCPSocket |
收包 base64 字节流;托管层可拆帧 |
udp |
✅ DatagramSocket |
✅ NWConnection(UDP) |
✅ socket.UDPSocket |
每包独立 |
H5 为桩(9030001)。App 需 自定义调试基座 / 云打包。
推荐:托管会话 createManagedSocket
import { createManagedSocket } from '@/uni_modules/zv-socket/js_sdk'
const sock = createManagedSocket({
type: 'tcp',
host: '192.168.1.10',
port: 9000,
// 帧:none | length2 | length4 | delimiter
frameMode: 'length4',
delimiter: '\n',
heartbeatInterval: 15000,
heartbeatPayload: 'ping',
idleTimeout: 45000,
autoReconnect: true,
reconnectDelay: 1000,
reconnectMaxDelay: 30000,
maxRetry: 10, // -1 无限
maxQueue: 200,
onState: (s) => console.log(s), // connecting|connected|reconnecting|closed
onOpen: (e) => {},
onMessage: (e) => console.log(e.data, e.framed),
onClose: (e) => console.log(e.active),
onError: (e) => {},
onReconnect: ({ attempt, delay }) => {}
})
sock.connect()
sock.send('hello') // 未连上先入队,连上后冲刷
sock.close() // 主动关闭,不再重连
参数说明
| 字段 |
类型 |
默认 |
含义 |
type |
'tcp' \| 'udp' \| 'ws' |
'tcp' |
协议类型。UDP 不会自动重连、不启用心跳/空闲超时。 |
host |
string |
'' |
TCP/UDP 对端 IP 或域名(type 为 tcp/udp 时必填)。 |
port |
number |
0 |
TCP/UDP 对端端口。 |
url |
string |
'' |
WebSocket 完整地址,如 ws://host:8080/path(type:'ws' 时用这个,不用 host/port)。 |
localPort |
number |
不传 |
仅 UDP:本机绑定端口;0 或不传表示系统分配。 |
timeout |
number |
10000 |
连接超时(毫秒)。超时走失败/关闭逻辑,可触发重连。 |
帧协议(主要对 TCP;粘包/拆包)
| 字段 |
类型 |
默认 |
含义 |
frameMode |
'none' \| 'length2' \| 'length4' \| 'delimiter' |
'none' |
收发如何组帧。仅 TCP 生效;ws/udp 按消息/数据报原样回调。 |
delimiter |
string |
'\n' |
仅 frameMode:'delimiter':帧结束分隔符(UTF-8)。发送时自动追加,接收按分隔符切开。 |
maxFrameSize |
number |
1048576(1MB) |
单帧最大字节;缓冲超限会报错(9030004)。 |
frameMode 取值:
none:不拆帧。TCP 底层收包是 base64 字节流,托管层会尽量解成 utf8 文本块 再 onMessage;发送也不加长度头。适合「对方发的就是文本块」或你自己在业务里拼。
length2:每帧前 2 字节 大端 无符号长度(payload 最长 65535),再跟正文。收发都会按此封/拆。
length4:每帧前 4 字节 大端 无符号长度 + 正文。适合二进制/变长协议。
delimiter:按 delimiter 切帧(如一行一条 \n)。发送时自动在末尾补分隔符。
示例里写 frameMode:'length4' 同时又写 delimiter:只有改成 'delimiter' 时 delimiter 才有用;length4 下可省略 delimiter。
心跳 / 空闲
| 字段 |
类型 |
默认 |
含义 |
heartbeatInterval |
number |
0(关闭) |
已连接后每隔多少 毫秒 自动 send(heartbeatPayload)。≤0 不心跳。UDP 忽略。 |
heartbeatPayload |
string |
'ping' |
心跳内容。会走与业务相同的封帧规则(TCP + 非 none 时也会封帧)。 |
idleTimeout |
number |
0(关闭) |
距上次 收到 数据超过该毫秒数则主动断开(错误码偏 9030006),若开启自动重连会再连。≤0 不检测。UDP 忽略。 |
说明:心跳只负责「定时往外发」;是否判定对方活着,通常靠 idleTimeout(对方有回包才会刷新空闲计时)。
自动重连 / 发送队列
| 字段 |
类型 |
默认 |
含义 |
autoReconnect |
boolean |
true(UDP 强制不重连) |
意外断开(对端关、超时、网络失败等)是否自动重连。调用 close() / destroy() 为主动关闭,一律不重连。 |
reconnectDelay |
number |
1000 |
首次重连等待毫秒;之后指数退避:min(reconnectMaxDelay, reconnectDelay * 2^attempt)。 |
reconnectMaxDelay |
number |
30000 |
单次重连等待上限(毫秒),避免退避无限变大。 |
maxRetry |
number |
-1 |
最多重连次数。-1 无限;10 表示最多再试 10 次,用尽后 closed 并 onError(含 maxRetry reached)。 |
maxQueue |
number |
200 |
未连接时 send 入队上限;超了 丢掉最旧 再入队。连上后在 onOpen 里按序冲刷。 |
回调
| 字段 |
签名 |
含义 |
onState |
(state, sock) => void |
状态变化:connecting 首次连 / connected 已通 / reconnecting 退避等待或重连中 / closed 结束。 |
onOpen |
(e) => void |
底层连接成功(含重连成功)。此时会启心跳、刷发送队列。 |
onMessage |
(e) => void |
收到业务数据。e.data 为内容;e.framed === true 表示经帧解析拆出;e.encoding 多为 utf8/base64;TCP 拆帧时还有 e.raw。 |
onClose |
(e) => void |
关闭。e.active === true 表示你调了 close();false 表示对端/网络断开(之后可能重连)。 |
onError |
(e) => void |
错误:e.errCode / e.errMsg(含连接失败、发送失败、拆帧失败、重连次数用尽等)。 |
onReconnect |
({ attempt, delay, maxRetry }) => void |
即将重连前触发。attempt 从 1 起;delay 为本次等待毫秒。 |
实例方法
| 方法 |
说明 |
connect() |
开始连接;已在 connecting/connected 时直接返回。 |
send(data, sendOpts?) |
发数据。未连接则入队,返回 { queued:true, queueSize };已连接返回 { queued:false }。sendOpts.skipFrame:true 可跳过 TCP 封帧;UDP 可传 { host, port } 指定对端。 |
close(reason?) |
主动关闭:清定时器、清空队列、不再重连。 |
destroy() |
同 close('destroy')。 |
getState() / isConnected() |
读当前状态 / 是否 connected。 |
组件:<zv-socket>(easycom),slot 暴露 state / connected / lastMessage 等,方法 connect / send / close。
底层 API(原始)
import {
createSocket, sendSocket, closeSocket,
onSocket, offSocket, closeAllSockets
} from '@/uni_modules/zv-socket'
onSocket({
onOpen: (e) => {},
onMessage: (e) => console.log(e.encoding, e.data), // TCP 为 base64
onClose: (e) => {},
onError: (e) => {}
})
createSocket({ type: 'tcp', host: '192.168.1.10', port: 9000 })
createSocket({ type: 'udp', host: '…', port: 9001, localPort: 0 })
createSocket({ type: 'ws', url: 'ws://192.168.1.10:8080/path' })
sendSocket({ socketId, data: 'hello', encoding: 'utf8' })
sendSocket({ socketId, data: uni.arrayBufferToBase64(buf), encoding: 'base64' })
readyState(底层)
0 连接中 · 1 已连接/已绑定 · 2 关闭中 · 3 已关闭
错误码
9030001 平台不支持 · 9030002 参数错误 · 9030003 连接失败 · 9030004 发送失败 · 9030005 会话不存在 · 9030006 已关闭/空闲超时
注意
- TCP 收包为
encoding:'base64'(保字节流);文本业务请用 createManagedSocket(frameMode:'none' 会解成 utf8)。
- 鸿蒙需
ohos.permission.INTERNET(插件已声明)。
- UDP
onMessage 带 remoteHost / remotePort。
- iOS 明文
ws:// / TCP 可能需 ATS 例外。
- 页面销毁请
close() / closeAllSockets()。
与 Android 行为对照(鸿蒙)
| 项 |
对齐情况 |
| API / 错误码 / 会话生命周期 |
✅ 同 interface.uts |
| TCP 收包 base64、发送 utf8/base64 |
✅ |
| UDP utf8 + remoteHost/Port;未 bind 先发自动 bind |
✅ |
WS 文本 utf8;连接超时 timeout |
✅(鸿蒙用定时器补齐) |
TCP TCPNoDelay、域名/IPv6 地址 |
✅(域名需 API 18+ family=3) |
| WS 二进制帧 |
Android / 鸿蒙 ✅ 收包 encoding:'base64' |
| 回调线程 |
Android 强制主线程;鸿蒙为 NetworkKit 异步回调(一般可直接更新 UI) |
明文 ws:// |
两端均可;生产建议 wss:// |