概览
本文将手把手教你如何从零编写一个可用于直播或在线聊天的 WSocket 类,依次实现连接建立、心跳检测、断线重连、消息收发以及资源清理等功能。我们将结合 WebSocket API 的标准用法、心跳保持 和 重连策略,并充分运用现代 JavaScript 语法(如类、解构、箭头函数)来确保代码既高效又易读。
1. 创建基础 WebSocket 连接
1.1 引入与初始化
首先,新建一个 WSocket
类,接受目标 URL 和回调函数作为构造参数:
javascript
class WSocket {
constructor({ url, onMessage, onDisconnect }) {
this.url = url;
this.onMessage = onMessage;
this.onDisconnect = onDisconnect;
this.ws = null;
this.reconnectCount = 0;
}
}
-
url
:WebSocket 服务器地址。 -
onMessage
/onDisconnect
:消息与断开回调。
1.2 建立连接
在类中添加 connect()
方法,使用原生 API 建立并绑定事件:
javascript
connect() {
this.ws = new WebSocket(this.url);
this.ws.binaryType = 'arraybuffer'; // 二进制类型
this.ws.onopen = () => this.handleOpen(); // 连接成功:contentReference[oaicite:0]{index=0}
this.ws.onmessage = e => this.handleMessage(e); // 收到消息
this.ws.onclose = e => this.handleClose(e); // 连接关闭
this.ws.onerror = e => this.handleError(e); // 错误处理
}
2. 实现心跳机制
2.1 为什么要心跳
许多网络设备会在连接空闲一定时长后断开,心跳可保持连接活跃并及时发现断线MDN Web Docs。
2.2 心跳代码
javascript
const DEFAULTS = { HEART_INTERVAL: 10000, STATUS_CHECK: 3000 };
handleOpen() {
this.clearTimers();
this.startHeartbeat();
}
startHeartbeat() {
this.heartbeatTimer = setInterval(() => {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type: 'PING' })); // 心跳包
}
}, DEFAULTS.HEART_INTERVAL);
this.statusCheckTimer = setInterval(() => {
if (this.ws.readyState !== WebSocket.OPEN) {
this.reconnect(); // 状态检查:contentReference[oaicite:3]{index=3}
}
}, DEFAULTS.STATUS_CHECK);
}
clearTimers() {
clearInterval(this.heartbeatTimer);
clearInterval(this.statusCheckTimer);
}
3. 自动重连策略
3.1 基本重连
在断线或错误时,调用 reconnect()
:
javascript
reconnect() {
if (this.reconnecting) return;
this.reconnecting = true;
this.clearTimers();
setTimeout(() => {
this.reconnectCount++;
if (this.reconnectCount <= 5) { // 最多重连5次
this.connect();
} else {
this.onDisconnect();
}
this.reconnecting = false;
}, 2000); // 延迟2秒重连:contentReference[oaicite:5]{index=5}
}
handleClose(e) {
console.warn('WebSocket closed:', e);
this.reconnect();
}
handleError(e) {
console.error('WebSocket error:', e);
this.reconnect();
}
4. 消息接收与分发
在 handleMessage
中解析并交给用户回调:
javascript
handleMessage(event) {
try {
const data = event.data instanceof ArrayBuffer
? JSON.parse(new TextDecoder().decode(event.data))
: JSON.parse(event.data);
this.onMessage(data);
} catch (err) {
console.error('Message parse error:', err);
}
}
-
兼容二进制/文本 :用
TextDecoder
解码二进制MDN Web Docs。 -
异常捕获 :防止单条消息解析失败导致整个连接中断Stack Overflow。
5. 完整示例代码
javascript
class WSocket {
constructor({ url, onMessage, onDisconnect }) {
this.url = url;
this.onMessage = onMessage;
this.onDisconnect = onDisconnect;
this.ws = null;
this.heartbeatTimer = null;
this.statusCheckTimer = null;
this.reconnecting = false;
this.reconnectCount = 0;
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.binaryType = 'arraybuffer';
this.ws.onopen = () => this.handleOpen();
this.ws.onmessage = e => this.handleMessage(e);
this.ws.onclose = e => this.handleClose(e);
this.ws.onerror = e => this.handleError(e);
}
handleOpen() {
this.reconnectCount = 0;
this.clearTimers();
this.startHeartbeat();
console.log('WebSocket connected');
}
startHeartbeat() {
this.heartbeatTimer = setInterval(() => {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type: 'PING' }));
}
}, DEFAULTS.HEART_INTERVAL);
this.statusCheckTimer = setInterval(() => {
if (this.ws.readyState !== WebSocket.OPEN) this.reconnect();
}, DEFAULTS.STATUS_CHECK);
}
clearTimers() {
clearInterval(this.heartbeatTimer);
clearInterval(this.statusCheckTimer);
}
reconnect() {
if (this.reconnecting) return;
this.reconnecting = true;
this.clearTimers();
setTimeout(() => {
this.reconnectCount++;
if (this.reconnectCount <= 5) {
this.connect();
} else {
this.onDisconnect();
}
this.reconnecting = false;
}, 2000);
}
handleMessage(event) {
try {
const raw = event.data instanceof ArrayBuffer
? new TextDecoder().decode(event.data)
: event.data;
const data = JSON.parse(raw);
this.onMessage(data);
} catch (err) {
console.error('Message parse error:', err);
}
}
handleClose(e) {
console.warn('WebSocket closed:', e);
this.reconnect();
}
handleError(e) {
console.error('WebSocket error:', e);
this.reconnect();
}
send(data) {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(typeof data === 'string' ? data : JSON.stringify(data));
}
}
disconnect() {
this.clearTimers();
this.ws?.close();
}
}
6. 使用示例
javascript
const ws = new WSocket({
url: 'wss://example.com/live',
onMessage: msg => console.log('Recv:', msg),
onDisconnect: () => alert('Connection lost')
});
ws.connect();
// 发送消息
ws.send({ type: 'CHAT', content: 'Hello world' });
// 手动断开
// ws.disconnect();