WebSocket 连接维护:从基础实现到生产级解决方案
在传统的HTTP协议中,客户端必须主动发起请求才能获取数据。而实时应用场景如在线聊天、股票行情、多人协作等,需要服务端能主动推送数据。WebSocket协议它具有:
- 双向通信:客户端和服务端可以同时发送消息
- 低延迟:建立连接后消息即时传输
- 高效:相比HTTP轮询节省大量带宽
这是一个原生的websocket封装实现主要用于了解底层的实现,实际生产中推荐用 Socket.IO、SockJS这种第三方插件去做。自己手写原生有开发成本,兼容性,维护成本高等缺点。
一、技术架构概述
要实现一个完整的websocket,需要一个websocket实例,链接方法,监听消息,发送消息,断开连接,心跳维护链接。这里主要讲前端的封装,后端可以用express实现一个简单的node服务。后端的方法和前端一样也是有链接,消息监听,发送消息,关闭连接,在加上后端的定时推送。
完整的 WebSocket 实现需要包含以下核心模块:
- WebSocket 实例管理
- 连接建立与状态维护
- 双向消息通信机制
- 心跳保活策略
- 异常恢复机制
本文主要聚焦前端实现,后端示例采用 Node.js + Express 构建。值得注意的是,生产环境的后端实现还需要考虑集群部署、消息广播等进阶功能。
二、前端基础实现
WebSocket 是浏览器原生支持的通信协议,以下是一个功能完备的基础实现:
<!DOCTYPE html>
<html>
<head>
<title>WebSocket 通信演示</title>
<style>
#output {
margin-top: 20px;
border: 1px solid #eee;
padding: 10px;
min-height: 100px;
}
</style>
</head>
<body>
<button onclick="connect()">建立连接</button>
<button onclick="sendMessage()">发送消息</button>
<div id="output"></div>
<script>
// WebSocket 实例与状态管理
let socket;
let heartbeatTimer;
const HEARTBEAT_INTERVAL = 30000; // 心跳间隔30秒
// 建立WebSocket连接
function connect() {
socket = new WebSocket('ws://localhost:3000');
// 连接成功回调
socket.onopen = () => {
log('连接已建立');
startHeartbeat();
};
// 消息接收处理
socket.onmessage = (event) => {
const data = parseMessage(event.data);
if (data.type !== 'heartbeat') {
log(`收到消息: ${event.data}`);
}
};
// 连接关闭处理
socket.onclose = () => {
clearInterval(heartbeatTimer);
log('连接已断开');
// 可在此处添加自动重连逻辑
};
// 错误处理
socket.onerror = (error) => {
log(`连接错误: ${error.message}`);
};
}
// 消息发送封装
function sendMessage() {
if (!socket || socket.readyState !== WebSocket.OPEN) {
return alert('请先建立连接');
}
const message = prompt('请输入消息内容');
if (message) {
socket.send(message);
log(`已发送: ${message}`);
}
}
// 心跳保活机制
function startHeartbeat() {
clearInterval(heartbeatTimer); // 清除旧定时器
heartbeatTimer = setInterval(() => {
if (socket.readyState === WebSocket.OPEN) {
const heartbeatMsg = JSON.stringify({
type: 'heartbeat',
timestamp: Date.now()
});
socket.send(heartbeatMsg);
}
}, HEARTBEAT_INTERVAL);
}
// 消息解析辅助函数
function parseMessage(data) {
try {
return JSON.parse(data);
} catch {
return { type: 'text', content: data };
}
}
// 日志输出
function log(message) {
const output = document.getElementById('output');
output.innerHTML += `<p>${new Date().toLocaleTimeString()}: ${message}</p>`;
output.scrollTop = output.scrollHeight;
}
</script>
</body>
</html>
关键实现说明:
- 心跳机制:通过定时发送特殊格式的心跳包,保持连接活跃并检测连接状态
- 状态管理:通过
readyState判断连接状态,避免在不可用状态下操作 - 错误处理:统一处理连接错误和异常情况
- 消息分类:通过消息类型字段区分心跳包和业务消息
二、初步封装
核心模块封装
- 连接管理机制
- 自动重连策略:实现指数退避重连算法,上限5次重连尝试,最大间隔10秒
- 连接状态隔离:通过闭包封装WebSocket实例,防止外部状态污染
- 统一连接入口:对外暴露connect方法支持DOM元素绑定和自动重连
- 消息保障体系
- 消息缓冲队列:在连接未就绪时暂存消息(pendingMessages)
- 消息可靠传输:通过flushPendingMessages方法确保连接建立后顺序发送缓冲消息
- 传输状态检测:实时校验readyState状态机,保证消息投递有效性
- 连接健康监测
- 心跳检测机制:每30秒发送心跳包维持长连接
- 异常断开处理:自动清除心跳定时器防止内存泄漏
- 连接状态同步:通过onopen/onclose事件维护重连计数器
- 数据渲染层
- 响应式数据更新:采用统一的数据接收处理器(onmessage)
- 数据格式化输出:使用JSON.stringify进行数据美化展示
- DOM智能更新:通过elment引用缓存实现精准内容更新
export default function ws() { let useWebsocket; // const receivedata = {} // let elment; // 存储 DOM 元素 let reconnectAttempts = 0; // 重连次数 const MAX_RECONNECTS = 5; // const pendingMessages = []; // 暂存消息的队列 function connect(el) { if (el) elment = el; if (useWebsocket) { console.warn('WebSocket 已存在,请勿重复连接'); return; } else { useWebsocket = new WebSocket('ws://localhost:3000'); } useWebsocket.onopen = () => { console.log('连接成功'); reconnectAttempts = 0; // 连接成功后发送所有暂存消息 flushPendingMessages(); startHeartbeat(); }; useWebsocket.onmessage = (event) => { console.log('收到消息: ' + event.data); const data = JSON.parse(event.data) receivedata[data.type] = data.data console.log('receivedata--', receivedata); changeInnerhtml(receivedata); // 直接渲染最新数据 }; useWebsocket.onclose = () => { console.log('连接关闭'); if (reconnectAttempts < MAX_RECONNECTS) { const delay = Math.min(1000 * (reconnectAttempts + 1), 10000); console.log(`连接断开,${delay}ms后尝试重连...`); setTimeout(() => { reconnectAttempts++; connect(); }, delay); } else { console.log('达到最大重连次数,停止尝试'); } }; } // 心跳配置 const HEARTBEAT_INTERVAL = 30000; // 30秒 let heartbeatTimer; function startHeartbeat() { heartbeatTimer = setInterval(() => { if (useWebsocket.readyState === WebSocket.OPEN) { useWebsocket.send(JSON.stringify({ type: 'heartbeat', timestamp: Date.now() })); } }, HEARTBEAT_INTERVAL); } //发送消息 /* function sendMessage(message) { if (useWebsocket.readyState === WebSocket.OPEN) { useWebsocket.send(JSON.stringify({ type: 'message', data: message })); } else { log('WebSocket 连接未建立,无法发送消息'); } } */ // 发送所有暂存消息 const flushPendingMessages = () => { if (useWebsocket.readyState !== WebSocket.OPEN) return; while (pendingMessages.length > 0) { const message = pendingMessages.shift(); if (message) { console.log('延迟发送的消息---', message); useWebsocket.send(message); } } }; // 发送消息(核心改进) const sendMessage = (message) => { console.log('准备发送消息---', message); // 统一通过readyState判断 if (useWebsocket.readyState === WebSocket.OPEN) { console.log('立即发送消息---', message); useWebsocket.send(message); } else { console.log('消息进入队列---', message); pendingMessages.push(message); } }; //关闭连接 function closeConnection() { if (useWebsocket) { useWebsocket.close(); useWebsocket = null; } } //在页面输出结果 function changeInnerhtml(data) { if (!elment) return; const text = JSON.stringify(data, null, 2); // 格式化 elment.innerHTML += `<p>${text}</p>`; } return { connect, sendMessage, closeConnection, receivedata } }
自动连接使用实例:
<!DOCTYPE html>
<html>
<head>
<title>WebSocket 测试</title>
</head>
<body>
<button id="connect-btn">连接</button>
<button id="send-btn">发送消息</button>
<div id="output"></div>
<script type="module">
//自动连接
import ws from './websocket.js';
const { connect, sendMessage } = ws();
const connectBtn = document.querySelector('#connect-btn');
const sendBtn = document.querySelector('#send-btn');
const output = document.querySelector('#output');
connect(output);
sendMessage('Hello, World!');
sendMessage('发送消息');
</script>
</body>
</html>
手动连接点击事件:
<!DOCTYPE html>
<html>
<head>
<title>WebSocket 测试</title>
</head>
<body>
<button id="connect-btn">连接</button>
<button id="send-btn">发送消息</button>
<div id="output"></div>
<script type="module">
import ws from './websocket.js';
const websocket = ws(); // 先调用函数初始化
const connectBtn = document.querySelector('#connect-btn');
const sendBtn = document.querySelector('#send-btn');
const output = document.querySelector('#output');
//建立连接
connectBtn.addEventListener('click', () => {
websocket.connect(output);
});
//发送消息
sendBtn.addEventListener('click', () => {
const message = prompt('输入要发送的消息');
if (message) websocket.sendMessage(message);
});
</script>
</body>
</html>
三、服务器架构设计
本方案采用 Express + ws 模块构建 WebSocket 服务器,具有以下特点:
- 双协议支持:同时支持 HTTP 和 WebSocket 协议
- 连接管理:实时跟踪客户端连接状态
- 消息广播:支持向所有客户端推送消息
- 类型化消息:结构化数据格式,便于前端处理
- 定时推送:服务端主动推送机制
const express = require('express');
const WebSocket = require('ws');
// 创建 Express 应用
const app = express();
// 直接使用 app.listen() 启动服务器并监听端口
const PORT = 3000;
const server = app.listen(PORT, () => {
console.log(`服务器运行在 http://localhost:${PORT}`);
console.log(`WebSocket 运行在 ws://localhost:${PORT}`);
});
// 创建 WebSocket 服务器,附加到 HTTP 服务器上
const wss = new WebSocket.Server({ server });
// 存储所有连接的客户端
const clients = new Set();
// WebSocket 连接处理
wss.on('connection', (ws) => {
console.log('新的客户端连接');
// 将新客户端添加到集合中
clients.add(ws);
// 当收到客户端消息时
ws.on('message', (message) => {
console.log(`收到消息: ${message}`);
console.log('发送消息给所有客户端clients---', clients);
// 广播消息给所有客户端
clients.forEach(client => {
console.log(client === ws, client.readyState === WebSocket.OPEN);
if (client.readyState === WebSocket.OPEN) {
client.send(`服务器转发: ${message}`);
}
});
});
// 当客户端断开连接时
ws.on('close', () => {
console.log('客户端断开连接');
clients.delete(ws);
});
// 1. 推送欢迎消息
ws.send(JSON.stringify({
type: 'welcome',
data: '连接服务器成功',
timestamp: Date.now()
}));
// 2. 推送系统状态
ws.send(JSON.stringify({
type: 'system_status',
data: { cpu: 45, memory: 80 },
timestamp: Date.now()
}));
// 3. 定时推送通知
setInterval(() => {
ws.send(JSON.stringify({
type: 'notification',
data: '您有1条新消息',
timestamp: Date.now()
}));
}, 5000);
});
// Express 路由
app.get('/', (req, res) => {
res.send(`
<h1>WebSocket 服务器</h1>
<p>打开浏览器控制台并运行以下代码测试:</p>
<pre>
const ws = new WebSocket('ws://localhost:3000');
ws.onmessage = (event) => console.log('收到消息:', event.data);
ws.onopen = () => ws.send('你好,服务器!');
</pre>
<p>当前连接数: ${clients.size}</p>
`);
});
这个实现提供了完整的 WebSocket 服务器解决方案,包括连接管理、消息处理、错误处理和性能监控等功能,可直接用于和前面前端代码进行联调。

浙公网安备 33010602011771号