WebSocket 实时聊天室 — ws 库实战

知识库
知识库文档
/tech-stacks/nodejs/examples/WebSocket 实时聊天室 — ws 库实战.md

文档

Node.js WebSocket 实时聊天室

目标

使用 ws 库构建多人在线聊天室,演示 Node.js 的事件驱动模型在实时通信中的优势。

完整代码

// server.js
const WebSocket = require('ws');
const http = require('http');
const fs = require('fs');
const path = require('path');

const server = http.createServer((req, res) => {
  if (req.url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.end(fs.readFileSync(path.join(__dirname, 'index.html'), 'utf-8'));
  }
});

const wss = new WebSocket.Server({ server });
const clients = new Map(); // ws -> username
let messageHistory = [];   // 保留最近 50 条

wss.on('connection', (ws, req) => {
  const ip = req.socket.remoteAddress;
  console.log(`🔗 新连接: ${ip}`);

  ws.on('message', (raw) => {
    const msg = JSON.parse(raw);

    switch (msg.type) {
      case 'join':
        clients.set(ws, msg.username);
        broadcast({ type: 'system', text: `${msg.username} 加入了聊天室`, time: now() });
        broadcast({ type: 'users', users: [...clients.values()] });
        // 发送历史消息
        ws.send(JSON.stringify({ type: 'history', messages: messageHistory }));
        break;

      case 'message':
        const username = clients.get(ws);
        if (username) {
          const chatMsg = { type: 'message', username, text: msg.text, time: now() };
          messageHistory.push(chatMsg);
          if (messageHistory.length > 50) messageHistory.shift();
          broadcast(chatMsg);
        }
        break;
    }
  });

  ws.on('close', () => {
    const username = clients.get(ws);
    if (username) {
      clients.delete(ws);
      broadcast({ type: 'system', text: `${username} 离开了`, time: now() });
      broadcast({ type: 'users', users: [...clients.values()] });
    }
  });
});

function broadcast(msg) {
  const data = JSON.stringify(msg);
  wss.clients.forEach((client) => {
    if (client.readyState === WebSocket.OPEN) client.send(data);
  });
}

function now() {
  return new Date().toLocaleTimeString('zh-CN', { hour12: false });
}

server.listen(3000, () => console.log('💬 聊天室: http://localhost:3000'));
<!-- index.html -->
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>聊天室</title>
<style>
  * { margin:0; padding:0; box-sizing:border-box; }
  body { font-family: system-ui; max-width:600px; margin:20px auto; }
  #messages { border:1px solid #ddd; height:400px; overflow-y:auto; padding:10px; }
  .system { color:#999; text-align:center; margin:5px 0; }
  .msg { margin:4px 0; } .msg strong { color:#2563eb; }
  input,button { padding:8px; margin-top:8px; }
  input { width:70%; } button { width:25%; }
</style></head><body>
<h2>💬 在线聊天室</h2>
<div id="messages"></div>
<input id="msgInput" placeholder="输入消息..." autofocus>
<button onclick="sendMsg()">发送</button>

<script>
  const username = '用户' + Math.floor(Math.random()*1000);
  const ws = new WebSocket(`ws://${location.host}`);
  const messages = document.getElementById('messages');
  const msgInput = document.getElementById('msgInput');

  ws.onopen = () => ws.send(JSON.stringify({ type:'join', username }));

  ws.onmessage = (e) => {
    const msg = JSON.parse(e.data);
    if (msg.type === 'system') {
      messages.innerHTML += `<div class="system">${msg.text} <small>${msg.time}</small></div>`;
    } else if (msg.type === 'message') {
      messages.innerHTML += `<div class="msg"><strong>${msg.username}</strong>: ${msg.text} <small>${msg.time}</small></div>`;
    }
    messages.scrollTop = messages.scrollHeight;
  };

  function sendMsg() {
    const text = msgInput.value.trim();
    if (text) {
      ws.send(JSON.stringify({ type:'message', text }));
      msgInput.value = '';
    }
  }
  msgInput.addEventListener('keydown', (e) => { if(e.key==='Enter') sendMsg(); });
</script></body></html>

运行步骤

npm install ws
node server.js
# 打开多个浏览器窗口访问 http://localhost:3000

预期效果

  • 每个窗口输入用户名后加入
  • 消息实时推送给所有在线用户
  • 用户离开时全网广播
  • 新接入用户收到最近历史消息

信息

路径
/tech-stacks/nodejs/examples/WebSocket 实时聊天室 — ws 库实战.md
更新时间
2026/5/30