WebSocket Pro Client

高性能 WebSocket 客户端,专为现代 Web 应用设计,内置自动重连、心跳、消息优先级调度、连接池管理,以及可配置的消息 ACK 与序列号机制。
特性一览
- 🚀 自动重连 + 指数退避算法
- 💓 心跳检测(支持自定义心跳内容与超时处理)
- 🎯 消息优先级调度 & 最大并发控制
- 📦 连接池管理(同一 url/protocol 只维护一个连接实例)
- 🔄 运行时更新配置(心跳、重连策略等)
- ✅ 内置消息 ACK 机制(默认实现 + 完全可自定义)
- 🔢 消息序列号支持(默认自增,可自定义包装与解析)
- 🔍 完整 TypeScript 类型定义
安装
npm install websocket-pro-client
yarn add websocket-pro-client
快速开始
import {
createWebSocketManager,
HeartbeatTimerMode,
} from "websocket-pro-client"
const manager = createWebSocketManager({
maxReconnectAttempts: 5,
})
const client = manager.connect("wss://api.example.com")
client.on("message", (data) => {
console.log("Received:", data)
})
client.send({ type: "ping" })
API 说明
1. 顶层方法
2. WebSocketManager
IWebSocketManager 接口:
export interface IWebSocketManager {
connect(url: string, protocols?: string[]): IWebSocketClient
closeAll(code?: number, reason?: string): void
on(event: WebSocketEvent, listener: (data: any) => void): void
}
connect(url, protocols?)
- 返回一个
IWebSocketClient 实例。
- 同一
url + protocols 会复用同一个底层连接。
closeAll(code?, reason?)
on(event, listener)
- 监听所有客户端转发上来的事件(
open/message/close/error 等),回调中会携带 { url, protocols, data }。
示例:
import { WebSocketEvent } from "websocket-pro-client"
manager.on(WebSocketEvent.Error, ({ url, data }) => {
console.error("ws error:", url, data)
})
3. WebSocketClient
IWebSocketClient 接口:
export interface IWebSocketClient {
send(data: any, priority?: number): Promise<void>
sendWithAck(data: any, priority?: number): Promise<void>
getLastInboundSeq(): string | number | undefined
updateLastInboundSeq(seq: string | number): void
close(code?: number, reason?: string): void
reconnect(): void
on(event: WebSocketEvent, listener: (data: any) => void): void
off(event: WebSocketEvent, listener: (data: any) => void): void
}
import { WebSocketEvent } from "websocket-pro-client"
client.on(WebSocketEvent.Open, () => {
console.log("ws open")
})
client.on(WebSocketEvent.Message, (data) => {
console.log("message:", data)
})
client.on(WebSocketEvent.Close, (event) => {
console.log("closed:", event)
})
client.on(WebSocketEvent.Error, (err) => {
console.error("ws error:", err)
})
配置说明(WebSocketConfig)
export interface WebSocketConfig {
maxReconnectAttempts?: number
reconnectDelay?: number
reconnectExponent?: number
maxReconnectDelay?: number
connectionPoolSize?: number
maxConcurrent?: number
defaultPriority?: number
enableCompression?: boolean
serializer?: Serializer
isNeedHeartbeat?: boolean
heartbeat?: HeartbeatConfig
ack?: AckStrategy
sequence?: SequenceStrategy
}
1. 心跳配置 HeartbeatConfig
export type HeartbeatConfig = {
interval?: number
timeout?: number
pingMessage?: any
getPing?: () => any
pongMessage?: any
isPong?: (raw: any, parsed: any) => boolean
timerMode?: "auto" | "main" | "worker"
onTimeout?: () => void
}
默认情况下,客户端会周期性发送心跳消息,并在超时时自动触发重连。
浏览器后台与心跳稳定性
当页面被最小化/切到后台时,浏览器可能会对 setTimeout/setInterval 降频或合并触发,导致定时任务不再“准点”。本库的心跳实现会用:
- 递归
setTimeout + 漂移修正:避免 setInterval 在后台堆积触发带来的状态错乱
- 基于真实时间差的超时判断(
Date.now()):即使回调延迟,也不会把“应该超时”的连接误当成健康连接
timerMode(可选:Web Worker 计时)
为了提升后台计时稳定性,你可以让心跳的计时器运行在 Web Worker 中(部分浏览器/环境可能不支持,库会自动回退到主线程计时器)。
auto(默认):优先使用 Worker,不可用则回退主线程
main:强制主线程
worker:强制 Worker(不可用仍会回退主线程,并输出 warn)
PONG 识别(兼容自定义协议)
默认情况下库会把 "PONG" 识别为心跳响应并调用 recordPong()。如果你的服务端返回的不是字符串 "PONG"(例如返回 JSON),可以通过下面两种方式配置:
pongMessage:简单场景,配置一个值即可(会同时与 raw/parsed 做严格相等判断)
isPong(raw, parsed):复杂场景,自行判断是否为 PONG(优先级更高)
示例(服务端返回 { type: 'pong' }):
const manager = createWebSocketManager({
heartbeat: {
getPing: () => ({ type: "ping" }),
isPong: (_raw, parsed) => parsed && parsed.type === "pong",
},
})
2. 消息 ACK 配置 AckStrategy
export type AckStrategy = {
enabled?: boolean
timeout?: number
maxRetries?: number
generateId?: () => string | number
wrapOutbound?: (id: string | number, data: any) => any
extractAckId?: (message: any) => string | number | null
}
默认实现(不配置时):
enabled: true
timeout: 5000
maxRetries: 2
generateId: 使用浏览器 window 上的自增计数。
wrapOutbound: ({ id, payload })
extractAckId: 从 message.ackId 中提取 ACK 对应的 ID。
如需和现有服务端协议对齐,只需要重写 wrapOutbound 和 extractAckId 即可。
自定义 ACK 协议示例
后端规定:
- 出站:
{ msgId, body }
- ACK 消息:
{ type: 'ACK', msgId }
对应配置:
const manager = createWebSocketManager({
ack: {
enabled: true,
wrapOutbound: (id, data) => ({ msgId: id, body: data }),
extractAckId: (msg) =>
msg && msg.type === "ACK" && msg.msgId != null ? msg.msgId : null,
},
})
3. 消息序列号配置 SequenceStrategy
export type SequenceStrategy = {
enabled?: boolean
generateSeq?: () => string | number
wrapOutbound?: (seq: string | number, data: any) => any
extractInboundSeq?: (message: any) => string | number | null
}
默认实现:
enabled: true
generateSeq: 使用浏览器 window 上的自增计数。
wrapOutbound: ({ seq, payload })
extractInboundSeq: 从 message.seq 中提取序列号。
库内部只负责“生成/包装/解析”序列号,不做强制的乱序丢弃;你可以在 message 监听回调中结合 seq 做业务上的顺序控制。
使用 ACK 与序列号的完整示例
import { createWebSocketManager, WebSocketEvent } from "websocket-pro-client"
const manager = createWebSocketManager({
ack: {
enabled: true,
timeout: 3000,
maxRetries: 1,
},
})
const client = manager.connect("wss://api.example.com")
client.on(WebSocketEvent.Open, async () => {
await client.send({ type: "ping" })
try {
await client.sendWithAck({ type: "update", payload: { id: 1 } })
console.log("update confirmed by server")
} catch (e) {
console.error("update failed (no ACK):", e)
}
})
client.on(WebSocketEvent.Message, (msg) => {
console.log("inbound message:", msg)
})
与“补拉接口”配合示例(推荐)
浏览器在切到后台、网络波动等场景可能出现 WebSocket 断开/重连。推荐结合服务端的消息存储能力,提供一个补拉接口(例如 GET /messages?sinceSeq=xxx)。
import { WebSocketEvent, createWebSocketManager } from "websocket-pro-client"
const manager = createWebSocketManager({
heartbeat: {
timerMode: "auto",
},
})
const client = manager.connect("wss://api.example.com")
async function fetchMissedMessages(sinceSeq?: string | number) {
const res = await fetch(`/messages?sinceSeq=${sinceSeq ?? ""}`)
return res.json()
}
client.on(WebSocketEvent.Open, async () => {
const sinceSeq = client.getLastInboundSeq()
const missed = await fetchMissedMessages(sinceSeq)
console.log("missed messages:", missed)
})
开发调试
npm test
npm run build
npm run demo
贡献指南
- Fork 仓库
- 创建分支 (
git checkout -b dev/feature/fix-xxx)
- 提交更改 (
git commit -am 'feat/fix xxx')
- 推送到分支 (
git push origin dev/feature/fix-xxx)
- 创建 Pull Request
许可证
MIT © 2023 BetaCatPro