AI实现在线聊天室--01

205 阅读3分钟

WebSocket 聊天室实现方案

下面是一个基于 WebSocket 的简单聊天室实现方案,包含前端和后端代码。

方案概述

  1. 后端:使用 Node.js 和 ws 库创建 WebSocket 服务器

  2. 前端:使用原生 JavaScript 实现 WebSocket 客户端

  3. 功能

    • 用户连接/断开通知
    • 发送和接收消息
    • 显示在线用户列表
    • 简单的用户名设置

完整实现代码

1. 后端代码 (server.js)

javascript

const WebSocket = require('ws');
const http = require('http');

// 创建HTTP服务器
const server = http.createServer();
const wss = new WebSocket.Server({ server });

// 存储所有连接的客户端和用户名
const clients = new Map();

wss.on('connection', (ws) => {
  console.log('New client connected');
  
  // 为新连接分配一个默认用户名
  const defaultUsername = `User${Math.floor(Math.random() * 1000)}`;
  clients.set(ws, { username: defaultUsername, color: getRandomColor() });
  
  // 通知所有客户端有新用户加入
  broadcastUserList();
  
  ws.on('message', (message) => {
    try {
      const data = JSON.parse(message);
      
      if (data.type === 'setUsername') {
        // 更新用户名
        const userData = clients.get(ws);
        userData.username = data.username;
        clients.set(ws, userData);
        broadcastUserList();
        
        // 发送欢迎消息
        const welcomeMsg = {
          type: 'message',
          username: '系统',
          content: `${data.username} 加入了聊天室`,
          timestamp: new Date().toISOString(),
          color: '#888'
        };
        broadcast(JSON.stringify(welcomeMsg));
      } else if (data.type === 'message') {
        // 广播普通消息
        const userData = clients.get(ws);
        const msg = {
          type: 'message',
          username: userData.username,
          content: data.content,
          timestamp: new Date().toISOString(),
          color: userData.color
        };
        broadcast(JSON.stringify(msg));
      }
    } catch (e) {
      console.error('Error parsing message:', e);
    }
  });
  
  ws.on('close', () => {
    console.log('Client disconnected');
    const userData = clients.get(ws);
    if (userData) {
      // 通知所有客户端有用户离开
      const leaveMsg = {
        type: 'message',
        username: '系统',
        content: `${userData.username} 离开了聊天室`,
        timestamp: new Date().toISOString(),
        color: '#888'
      };
      broadcast(JSON.stringify(leaveMsg));
      
      clients.delete(ws);
      broadcastUserList();
    }
  });
});

// 广播消息给所有客户端
function broadcast(message) {
  wss.clients.forEach(client => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(message);
    }
  });
}

// 广播用户列表给所有客户端
function broadcastUserList() {
  const userList = Array.from(clients.values()).map(user => ({
    username: user.username,
    color: user.color
  }));
  
  const message = {
    type: 'userList',
    users: userList
  };
  
  broadcast(JSON.stringify(message));
}

// 生成随机颜色
function getRandomColor() {
  const letters = '0123456789ABCDEF';
  let color = '#';
  for (let i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
}

// 启动服务器
const PORT = process.env.PORT || 8080;
server.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

2. 前端代码 (index.html)

html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket 聊天室</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        #chat-container {
            background-color: white;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            overflow: hidden;
        }
        #messages {
            height: 400px;
            overflow-y: auto;
            padding: 20px;
            border-bottom: 1px solid #eee;
        }
        .message {
            margin-bottom: 15px;
            line-height: 1.4;
        }
        .message .username {
            font-weight: bold;
            margin-right: 5px;
        }
        .message .timestamp {
            font-size: 0.8em;
            color: #999;
            margin-left: 5px;
        }
        #user-list {
            width: 200px;
            float: right;
            background-color: #f9f9f9;
            padding: 10px;
            border-left: 1px solid #eee;
            height: 400px;
            overflow-y: auto;
        }
        .user-item {
            margin-bottom: 5px;
            padding: 5px;
            border-radius: 4px;
        }
        #input-area {
            padding: 15px;
            background-color: #f9f9f9;
            display: flex;
        }
        #message-input {
            flex-grow: 1;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            margin-right: 10px;
        }
        #send-button {
            padding: 10px 20px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        #send-button:hover {
            background-color: #45a049;
        }
        #username-form {
            margin-bottom: 20px;
            padding: 15px;
            background-color: #f9f9f9;
            border-radius: 8px;
        }
        #username-input {
            padding: 8px;
            margin-right: 10px;
            width: 200px;
        }
        #set-username {
            padding: 8px 15px;
            background-color: #2196F3;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        #set-username:hover {
            background-color: #0b7dda;
        }
        .system-message {
            color: #888;
            font-style: italic;
        }
    </style>
</head>
<body>
    <h1>WebSocket 聊天室</h1>
    
    <div id="username-form">
        <input type="text" id="username-input" placeholder="输入用户名">
        <button id="set-username">设置用户名</button>
    </div>
    
    <div id="chat-container">
        <div id="user-list">
            <h3>在线用户</h3>
            <div id="users"></div>
        </div>
        <div id="messages"></div>
        <div id="input-area">
            <input type="text" id="message-input" placeholder="输入消息..." disabled>
            <button id="send-button" disabled>发送</button>
        </div>
    </div>

    <script>
        // 连接WebSocket服务器
        const socket = new WebSocket('ws://localhost:8080');
        let username = '';
        
        // DOM元素
        const messagesEl = document.getElementById('messages');
        const messageInput = document.getElementById('message-input');
        const sendButton = document.getElementById('send-button');
        const usernameInput = document.getElementById('username-input');
        const setUsernameButton = document.getElementById('set-username');
        const usersEl = document.getElementById('users');
        
        // WebSocket连接打开时
        socket.addEventListener('open', (event) => {
            console.log('Connected to WebSocket server');
        });
        
        // 接收消息
        socket.addEventListener('message', (event) => {
            const data = JSON.parse(event.data);
            
            if (data.type === 'message') {
                // 显示消息
                const messageEl = document.createElement('div');
                messageEl.className = 'message';
                
                if (data.username === '系统') {
                    messageEl.classList.add('system-message');
                }
                
                const timestamp = new Date(data.timestamp).toLocaleTimeString();
                messageEl.innerHTML = `
                    <span class="username" style="color: ${data.color}">${data.username}</span>
                    <span class="content">${data.content}</span>
                    <span class="timestamp">${timestamp}</span>
                `;
                
                messagesEl.appendChild(messageEl);
                messagesEl.scrollTop = messagesEl.scrollHeight;
            } else if (data.type === 'userList') {
                // 更新用户列表
                usersEl.innerHTML = '';
                data.users.forEach(user => {
                    const userEl = document.createElement('div');
                    userEl.className = 'user-item';
                    userEl.innerHTML = `
                        <span class="username" style="color: ${user.color}">${user.username}</span>
                    `;
                    usersEl.appendChild(userEl);
                });
            }
        });
        
        // 设置用户名
        setUsernameButton.addEventListener('click', () => {
            const newUsername = usernameInput.value.trim();
            if (newUsername) {
                username = newUsername;
                socket.send(JSON.stringify({
                    type: 'setUsername',
                    username: newUsername
                }));
                
                // 启用消息输入
                messageInput.disabled = false;
                sendButton.disabled = false;
                
                // 禁用用户名设置
                usernameInput.disabled = true;
                setUsernameButton.disabled = true;
            }
        });
        
        // 发送消息
        sendButton.addEventListener('click', sendMessage);
        messageInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') {
                sendMessage();
            }
        });
        
        function sendMessage() {
            const message = messageInput.value.trim();
            if (message) {
                socket.send(JSON.stringify({
                    type: 'message',
                    content: message
                }));
                messageInput.value = '';
            }
        }
        
        // 连接关闭时
        socket.addEventListener('close', (event) => {
            console.log('Disconnected from WebSocket server');
            messageInput.disabled = true;
            sendButton.disabled = true;
            
            // 显示断开连接消息
            const messageEl = document.createElement('div');
            messageEl.className = 'message system-message';
            messageEl.textContent = '与服务器的连接已断开';
            messagesEl.appendChild(messageEl);
        });
    </script>
</body>
</html>

部署和使用说明

  1. 安装依赖

    bash

    npm install ws
    
  2. 启动服务器

    bash

    node server.js
    
  3. 访问客户端

    • 在浏览器中打开 index.html 文件
    • 或者将 HTML 文件放在静态服务器上访问
  4. 使用聊天室

    • 首先设置用户名
    • 然后可以开始发送和接收消息
    • 右侧会显示当前在线的用户列表

功能扩展建议

  1. 消息持久化:将聊天消息存储在数据库中
  2. 私聊功能:允许用户之间发送私密消息
  3. 房间功能:支持创建和加入不同的聊天室
  4. 表情支持:添加表情选择器
  5. 文件分享:支持发送图片或其他文件
  6. 用户认证:添加登录系统

这个实现提供了一个基础的 WebSocket 聊天室功能,可以根据需要进一步扩展和完善。

页面效果

image.png