WebSocket推送案例

wen java案例 2

本文目录导读:

WebSocket推送案例

  1. 服务端实现(Node.js + ws库)
  2. 客户端实现(HTML + JavaScript)
  3. 运行测试
  4. 高级功能扩展

我来为您提供一个完整的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

测试功能

  1. 打开浏览器访问 http://localhost:3000
  2. 打开多个浏览器窗口进行测试
  3. 测试功能:
    • 连接/断开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推送的核心功能,包括连接管理、消息广播、私聊、心跳检测、自动重连等,您可以根据自己的需求进行扩展和修改。

抱歉,评论功能暂时关闭!