文档
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
预期效果
- 每个窗口输入用户名后加入
- 消息实时推送给所有在线用户
- 用户离开时全网广播
- 新接入用户收到最近历史消息