@bdky/websocket-client
v1.0.2
Published
轻量级 WebSocket 客户端 SDK,基于事件驱动架构,支持自动重连与消息队列管理
Downloads
234
Readme
@bdky/websocket-client
功能丰富的浏览器端 WebSocket 客户端,内置心跳检测、指数退避自动重连、消息缓冲和 ACK/NACK 投递确认。
特性
- 心跳检测 (Ping/Pong) — 可配置的 keep-alive 机制,自动识别超时断线
- 自动重连 — 指数退避策略,支持最大重试次数、首次立即重连
- 消息缓冲 — 断线期间发送的消息自动入队,重连后统一刷新发送
- ACK/NACK — 可选的应用层投递确认,超时未 ACK 自动触发 NACK 事件
- 连接超时 — 自动终止长时间无响应的连接尝试
- 类型安全 — 完整 TypeScript 支持,基于 Emittery 的强类型事件系统
- 轻量级 — 唯一外部依赖为 emittery,支持 ESM + CJS 双格式 tree-shaking
安装
npm install @bdky/websocket-client
# 或
yarn add @bdky/websocket-client快速开始
import {WebSocketClient} from '@bdky/websocket-client';
const client = new WebSocketClient({
url: 'wss://example.com/ws',
});
client.emitter.on('open', () => {
console.log('已连接');
client.send('Hello, server!');
});
client.emitter.on('message', ({data}) => {
console.log('收到消息:', data);
});
client.emitter.on('close', ({code, reason}) => {
console.log(`连接关闭: ${code} ${reason}`);
});
client.connect();API
new WebSocketClient(options)
创建一个新的 WebSocket 客户端实例。
配置项
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| url | string | 必填 | WebSocket 服务端地址 |
| protocols | string \| string[] | undefined | 子协议 |
| connectionTimeout | number | 10000 | 连接超时时间 (ms) |
| backoff | Partial<IBackoffOptions> | 见下文 | 重连退避策略 |
| heartbeat | Partial<IHeartbeatOptions> \| null | 见下文 | 心跳配置,传 null 可禁用 |
| ack | Partial<IAckOptions> | 见下文 | ACK/NACK 配置 |
| binaryType | BinaryType | 'blob' | 二进制数据类型 |
属性
| 属性 | 类型 | 说明 |
|------|------|------|
| readyState | WebSocketState | 当前连接状态:'idle' | 'connecting' | 'open' | 'closing' | 'closed' | 'reconnecting' |
| url | string | WebSocket 地址 |
| bufferedAmount | number | 发送缓冲队列中待发送的消息数 |
| emitter | Emittery<IWebSocketEvents> | 强类型事件发射器 |
方法
| 方法 | 说明 |
|------|------|
| connect() | 发起 WebSocket 连接 |
| send(data) | 发送数据;未连接时自动缓冲 |
| close(code?, reason?) | 主动关闭连接(不触发重连) |
| dispose() | 销毁实例,释放所有资源 |
事件
通过 client.emitter.on(event, handler) 订阅:
| 事件 | 载荷 | 说明 |
|------|------|------|
| open | {event} | 连接建立 |
| close | {code, reason, wasClean} | 连接关闭 |
| error | {event} | 发生 WebSocket 错误 |
| message | {data, event} | 收到消息(已排除 pong 和 ack) |
| reconnecting | {attempt, delay} | 即将重连 |
| reconnected | {attempt} | 重连成功 |
| heartbeat_timeout | undefined | 心跳超时,未收到 pong |
| ack | {messageId} | 服务端确认收到消息 |
| nack | {messageId} | ACK 超时,消息未被确认 |
心跳检测
自动发送 ping 并等待 pong 响应,及早发现死连接。
const client = new WebSocketClient({
url: 'wss://example.com/ws',
heartbeat: {
interval: 30_000, // 每 30 秒发送 ping
timeout: 5_000, // 等待 pong 的超时时间
pingMessage: 'ping', // ping 消息内容(字符串或 () => string)
isPong: (data) => data === 'pong', // 判断是否为 pong 响应
},
});传入 heartbeat: null 可完全禁用心跳。
默认值:
| 参数 | 默认值 |
|------|--------|
| interval | 30000 |
| timeout | 5000 |
| pingMessage | 'ping' |
| isPong | (data) => data === 'pong' |
自动重连
指数退避重连策略,连接断开后自动尝试恢复。
const client = new WebSocketClient({
url: 'wss://example.com/ws',
backoff: {
initialDelay: 1_000, // 首次重连延迟 1 秒
maxDelay: 30_000, // 最大延迟上限 30 秒
maxRetries: 10, // 最多重试 10 次(0 = 无限重试)
instantReconnect: false, // 首次重连是否跳过延迟
},
});
client.emitter.on('reconnecting', ({attempt, delay}) => {
console.log(`第 ${attempt} 次重连,延迟 ${delay}ms`);
});
client.emitter.on('reconnected', ({attempt}) => {
console.log(`重连成功,共尝试 ${attempt} 次`);
});默认值:
| 参数 | 默认值 |
|------|--------|
| initialDelay | 1000 |
| maxDelay | 30000 |
| maxRetries | 0(无限重试) |
| instantReconnect | false |
延迟计算公式:min(initialDelay * 2^attempt, maxDelay)
消息缓冲
断线期间发送的消息不会丢失,而是自动入队。连接(重新)建立后,缓冲区中的消息会按顺序发出。
const client = new WebSocketClient({url: 'wss://example.com/ws'});
// 这些消息会被缓冲,不会丢失
client.send('message-1');
client.send('message-2');
console.log(client.bufferedAmount); // 2
// 连接建立后自动发送缓冲消息
client.connect();ACK / NACK
应用层消息投递确认机制。发送消息后,在指定时间内未收到服务端 ACK 将触发 NACK 事件。
const client = new WebSocketClient({
url: 'wss://example.com/ws',
ack: {
enabled: true,
timeout: 5_000,
getMessageId: (data) => {
const msg = JSON.parse(data as string);
return msg.id;
},
parseAck: (data) => {
const msg = JSON.parse(data as string);
return msg.type === 'ack' ? msg.id : null;
},
},
});
client.emitter.on('ack', ({messageId}) => {
console.log(`消息 ${messageId} 已确认`);
});
client.emitter.on('nack', ({messageId}) => {
console.log(`消息 ${messageId} 超时未确认`);
});JSON 协议示例
以下是一个贴近实际业务的完整示例,展示心跳 + 重连 + 消息处理的组合用法:
import {WebSocketClient} from '@bdky/websocket-client';
interface ServerMessage {
type: 'data' | 'pong' | 'ack';
payload?: unknown;
id?: string;
}
const client = new WebSocketClient({
url: 'wss://api.example.com/ws',
heartbeat: {
interval: 25_000,
timeout: 5_000,
pingMessage: () => JSON.stringify({type: 'ping'}),
isPong: (data) => {
if (typeof data !== 'string') {
return false;
}
try {
const msg: ServerMessage = JSON.parse(data);
return msg.type === 'pong';
}
catch {
return false;
}
},
},
backoff: {
initialDelay: 1_000,
maxDelay: 30_000,
maxRetries: 5,
},
});
client.emitter.on('message', ({data}) => {
if (typeof data === 'string') {
const msg: ServerMessage = JSON.parse(data);
console.log('服务端消息:', msg);
}
});
client.emitter.on('error', () => {
console.error('连接异常');
});
client.connect();导出一览
// 类
export {WebSocketClient, ExponentialBackoff, ArrayQueue};
// 类型
export type {
IWebSocketClientOptions,
IBackoffOptions,
IHeartbeatOptions,
IAckOptions,
IWebSocketEvents,
IResolvedOptions,
WebSocketState,
WebSocketEmitter,
BufferData,
};
// 默认值常量
export {
DEFAULT_CONNECTION_TIMEOUT,
DEFAULT_BACKOFF,
DEFAULT_HEARTBEAT,
DEFAULT_ACK,
};许可证
MIT
