本文目录导读:

我来为您提供一个完整的WebSocket推送案例,包括服务端和客户端的实现。
服务端实现(Node.js + ws库)
安装依赖
npm init -y npm install ws express
服务端代码 (server.js)
const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });
// 存储所有连接的客户端
const clients = new Map();
// 处理WebSocket连接
wss.on('connection', (ws, req) => {
// 为每个连接生成唯一ID
const clientId = generateClientId();
clients.set(clientId, ws);
console.log(`客户端 ${clientId} 已连接,当前连接数: ${clients.size}`);
// 发送欢迎消息
ws.send(JSON.stringify({
type: 'system',
message: '连接成功',
clientId: clientId,
timestamp: new Date().toISOString()
}));
// 接收客户端消息
ws.on('message', (data) => {
try {
const message = JSON.parse(data);
console.log(`收到客户端的消息:`, message);
// 根据消息类型处理
handleMessage(clientId, ws, message);
} catch (error) {
ws.send(JSON.stringify({
type: 'error',
message: '消息格式不正确'
}));
}
});
// 客户端断开连接
ws.on('close', () => {
clients.delete(clientId);
console.log(`客户端 ${clientId} 已断开,当前连接数: ${clients.size}`);
// 广播用户断开消息
broadcastToAll({
type: 'system',
message: `用户 ${clientId} 已下线`,
timestamp: new Date().toISOString()
});
});
// 错误处理
ws.on('error', (error) => {
console.error(`客户端 ${clientId} 发生错误:`, error);
clients.delete(clientId);
});
});
// 生成客户端ID
function generateClientId() {
return 'client_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
// 处理不同类型的消息
function handleMessage(clientId, ws, message) {
switch (message.type) {
case 'chat':
// 广播聊天消息给所有客户端
broadcastToAll({
type: 'chat',
sender: clientId,
content: message.content,
timestamp: new Date().toISOString()
});
break;
case 'private':
// 发送私聊消息给指定客户端
sendToClient(message.targetClientId, {
type: 'private',
sender: clientId,
content: message.content,
timestamp: new Date().toISOString()
});
break;
case 'heartbeat':
// 心跳检测,返回pong
ws.send(JSON.stringify({
type: 'heartbeat',
status: 'pong',
timestamp: new Date().toISOString()
}));
break;
default:
ws.send(JSON.stringify({
type: 'system',
message: '未知消息类型',
timestamp: new Date().toISOString()
}));
}
}
// 广播给所有客户端
function broadcastToAll(message) {
const data = JSON.stringify(message);
clients.forEach((client, id) => {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
}
// 发送给指定客户端
function sendToClient(clientId, message) {
const client = clients.get(clientId);
if (client && client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(message));
return true;
}
return false;
}
// 定时推送示例(每5秒推送一条消息)
setInterval(() => {
const randomData = {
type: 'notification',
content: `最新数据更新: ${Math.random().toFixed(2)}`,
temperature: 20 + Math.random() * 10,
timestamp: new Date().toISOString()
};
broadcastToAll(randomData);
console.log('定时推送消息:', randomData);
}, 5000);
// 静态页面
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
// 启动服务器
const PORT = 3000;
server.listen(PORT, () => {
console.log(`WebSocket服务器已启动: http://localhost:${PORT}`);
});
客户端实现(HTML + JavaScript)
客户端页面 (index.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">WebSocket推送案例</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
border-bottom: 2px solid #007bff;
padding-bottom: 10px;
}
.status {
background-color: #e7f3ff;
border-left: 4px solid #3498db;
padding: 10px;
margin-bottom: 20px;
}
.messages {
background-color: #f8f9fa;
border-radius: 4px;
padding: 15px;
height: 300px;
overflow-y: auto;
margin-bottom: 20px;
border: 1px solid #dee2e6;
}
.message {
margin-bottom: 10px;
padding: 10px;
border-radius: 4px;
background-color: white;
border-left: 3px solid #007bff;
}
.message.system { border-left-color: #6c757d; background-color: #f1f0f0; }
.message.notification { border-left-color: #28a745; }
.message.error { border-left-color: #dc3545; }
.message.private { border-left-color: #ffc107; }
.controls {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
input, select {
flex: 1;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
padding: 8px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #0056b3;
}
button:disabled {
background-color: #6c757d;
cursor: not-allowed;
}
.stats {
font-size: 14px;
color: #666;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="container">
<h1>📡 WebSocket 实时推送演示</h1>
<div class="status" id="statusBar">
<strong>连接状态:</strong> <span id="connectionStatus">未连接</span>
</div>
<div class="controls">
<input type="text" id="messageInput" placeholder="输入消息内容...">
<select id="messageType">
<option value="chat">发送到群聊</option>
<option value="private">发送私聊</option>
</select>
<input type="text" id="targetClient" placeholder="目标客户端ID(私聊时填写)">
<button onclick="sendMessage()">发送消息</button>
</div>
<div class="messages" id="messagesContainer">
<div class="message system">欢迎使用WebSocket推送演示系统</div>
</div>
<div class="stats">
<span>当前客户端ID: <strong id="myClientId">未知</strong></span>
<span> | 在线用户数: <strong id="onlineUsers">0</strong></span>
</div>
<button id="connectBtn" onclick="connect()">连接WebSocket</button>
<button id="disconnectBtn" onclick="disconnect()" style="background-color: #dc3545;">断开连接</button>
</div>
<script>
// WebSocket客户端变量
let ws = null;
let heartbeatInterval = null;
let reconnectAttempts = 0;
const MAX_RECONNECT_ATTEMPTS = 5;
// 连接WebSocket
function connect() {
if (ws && ws.readyState === WebSocket.OPEN) {
alert('已经连接WebSocket服务器');
return;
}
const url = 'ws://localhost:3000';
ws = new WebSocket(url);
// 连接打开
ws.onopen = function(event) {
updateStatus('已连接', '#28a745');
document.getElementById('connectBtn').disabled = true;
document.getElementById('disconnectBtn').disabled = false;
reconnectAttempts = 0;
// 启动心跳检测
startHeartbeat();
addMessage('系统', '连接成功,等待服务器响应...', 'system');
};
// 接收消息
ws.onmessage = function(event) {
try {
const data = JSON.parse(event.data);
handleServerMessage(data);
} catch (error) {
console.error('解析消息错误:', error);
}
};
// 连接关闭
ws.onclose = function(event) {
updateStatus('已断开', '#dc3545');
document.getElementById('connectBtn').disabled = false;
document.getElementById('disconnectBtn').disabled = true;
stopHeartbeat();
addMessage('系统', '连接已关闭', 'error');
// 自动重连
if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
setTimeout(() => {
reconnectAttempts++;
addMessage('系统', `正在尝试重连 (第${reconnectAttempts}次)...`, 'system');
connect();
}, 3000);
} else {
addMessage('系统', '重连次数过多,请手动连接', 'error');
}
};
// 错误处理
ws.onerror = function(error) {
console.error('WebSocket错误:', error);
addMessage('错误', 'WebSocket连接发生错误', 'error');
};
}
// 断开连接
function disconnect() {
if (ws) {
ws.close();
ws = null;
updateStatus('已断开', '#dc3545');
}
}
// 更新连接状态显示
function updateStatus(text, color) {
document.getElementById('connectionStatus').textContent = text;
document.getElementById('connectionStatus').style.color = color;
}
// 添加消息到容器
function addMessage(sender, content, type = 'chat') {
const container = document.getElementById('messagesContainer');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${type}`;
const time = new Date().toLocaleTimeString();
messageDiv.innerHTML = `<strong>[${time}] ${sender}:</strong> ${content}`;
container.appendChild(messageDiv);
container.scrollTop = container.scrollHeight;
}
// 发送消息
function sendMessage() {
if (!ws || ws.readyState !== WebSocket.OPEN) {
addMessage('系统', '请先连接WebSocket服务器', 'error');
return;
}
const content = document.getElementById('messageInput').value;
if (!content.trim()) {
alert('请输入消息内容');
return;
}
const type = document.getElementById('messageType').value;
const message = {
type: type,
content: content
};
// 如果是私聊消息,添加目标客户端ID
if (type === 'private') {
const targetClient = document.getElementById('targetClient').value;
if (!targetClient) {
alert('请输入目标客户端ID');
return;
}
message.targetClientId = targetClient;
}
ws.send(JSON.stringify(message));
document.getElementById('messageInput').value = '';
addMessage('我', content, type);
}
// 处理服务器消息
function handleServerMessage(data) {
switch (data.type) {
case 'system':
if (data.clientId) {
document.getElementById('myClientId').textContent = data.clientId;
addMessage('系统', data.message, 'system');
} else {
addMessage('系统', data.message, 'system');
}
break;
case 'chat':
addMessage(data.sender, data.content, 'chat');
break;
case 'private':
addMessage(data.sender, data.content, 'private');
break;
case 'notification':
addMessage('服务端推送', data.content, 'notification');
break;
case 'heartbeat':
// 心跳响应
break;
case 'error':
addMessage('错误', data.message, 'error');
break;
default:
addMessage('系统', JSON.stringify(data), 'system');
}
}
// 心跳检测
function startHeartbeat() {
heartbeatInterval = setInterval(() => {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'heartbeat', timestamp: Date.now() }));
}
}, 30000);
}
function stopHeartbeat() {
if (heartbeatInterval) {
clearInterval(heartbeatInterval);
heartbeatInterval = null;
}
}
// 页面加载时自动连接
window.addEventListener('load', () => {
connect();
});
// 窗口关闭时断开连接
window.addEventListener('beforeunload', () => {
disconnect();
});
// 回车键发送消息
document.getElementById('messageInput').addEventListener('keypress', (event) => {
if (event.key === 'Enter') {
sendMessage();
}
});
</script>
</body>
</html>
运行测试
启动服务端
node server.js
测试功能
- 打开浏览器访问
http://localhost:3000 - 打开多个浏览器窗口进行测试
- 测试功能:
- 连接/断开WebSocket
- 群聊消息广播
- 私聊消息发送
- 定时推送通知
- 自动重连机制
- 心跳检测
高级功能扩展
房间/频道支持
// 在服务端添加房间功能
const rooms = new Map();
// 加入房间
function joinRoom(clientId, roomId) {
if (!rooms.has(roomId)) {
rooms.set(roomId, new Set());
}
rooms.get(roomId).add(clientId);
}
// 向房间广播
function broadcastToRoom(roomId, message) {
const room = rooms.get(roomId);
if (room) {
room.forEach(clientId => {
const client = clients.get(clientId);
if (client && client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(message));
}
});
}
}
认证与授权
// 认证中间件
function authenticate(ws, req) {
const token = req.url.split('?')[1]?.split('=')[1];
// 验证token逻辑
if (!isValidToken(token)) {
ws.close(1008, '认证失败');
return false;
}
return true;
}
wss.on('connection', (ws, req) => {
if (!authenticate(ws, req)) {
return;
}
// 处理连接...
});
这个案例包含了WebSocket推送的核心功能,包括连接管理、消息广播、私聊、心跳检测、自动重连等,您可以根据自己的需求进行扩展和修改。