Node.js实现WebSocket教程

Node.js实现WebSocket教程

1. WebSocket简介

WebSocket是一种在单个TCP连接上提供全双工通信的协议,允许服务器和客户端之间进行实时、双向通信。本教程将详细讲解如何在Node.js中实现WebSocket。

2. 技术选型

我们将使用ws库来实现WebSocket服务器,并结合express创建Web应用。

2.1 安装依赖

bash 复制代码
# 创建项目目录
mkdir nodejs-websocket-demo
cd nodejs-websocket-demo

# 初始化项目
npm init -y

# 安装依赖
npm install express ws uuid

依赖说明:

  • express: Web应用框架
  • ws: WebSocket服务器实现
  • uuid: 生成唯一标识符

3. 项目结构

复制代码
nodejs-websocket-demo/
│
├── public/
│   ├── index.html
│   └── client.js
├── server.js
└── package.json

4. 详细实现

4.1 WebSocket服务器 (server.js)

javascript 复制代码
const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const { v4: uuidv4 } = require('uuid');

const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });

// 存储客户端连接
const clients = new Map();

// 静态文件服务
app.use(express.static('public'));

// WebSocket连接处理
wss.on('connection', (ws) => {
  // 为每个客户端分配唯一ID
  const clientId = uuidv4();
  
  // 存储客户端连接
  clients.set(clientId, ws);

  // 发送客户端ID
  ws.send(JSON.stringify({
    type: 'connection',
    clientId: clientId
  }));

  // 广播新用户连接
  broadcast({
    type: 'userJoined',
    clientId: clientId,
    message: `用户 ${clientId} 已加入聊天`
  }, clientId);

  // 消息处理
  ws.on('message', (message) => {
    try {
      const parsedMessage = JSON.parse(message);
      
      switch(parsedMessage.type) {
        case 'chat':
          handleChatMessage(clientId, parsedMessage);
          break;
        case 'typing':
          handleTypingNotification(clientId, parsedMessage);
          break;
        default:
          console.log('未知消息类型:', parsedMessage.type);
      }
    } catch (error) {
      console.error('消息解析错误:', error);
    }
  });

  // 连接关闭处理
  ws.on('close', () => {
    // 移除客户端
    clients.delete(clientId);

    // 广播用户离开
    broadcast({
      type: 'userLeft',
      clientId: clientId,
      message: `用户 ${clientId} 已离开聊天`
    }, clientId);
  });
});

// 聊天消息处理
function handleChatMessage(senderId, message) {
  broadcast({
    type: 'chat',
    clientId: senderId,
    message: message.message
  }, senderId);
}

// 输入状态通知
function handleTypingNotification(senderId, message) {
  broadcast({
    type: 'typing',
    clientId: senderId,
    isTyping: message.isTyping
  }, senderId);
}

// 广播消息
function broadcast(message, excludeClientId = null) {
  clients.forEach((client, clientId) => {
    if (client.readyState === WebSocket.OPEN && clientId !== excludeClientId) {
      client.send(JSON.stringify(message));
    }
  });
}

// 服务器启动
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`WebSocket服务器运行在 ${PORT} 端口`);
});

4.2 客户端HTML (public/index.html)

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>WebSocket聊天室</title>
    <style>
        body { 
            font-family: Arial, sans-serif; 
            max-width: 600px; 
            margin: 0 auto; 
            padding: 20px; 
        }
        #chat-messages {
            height: 300px;
            overflow-y: scroll;
            border: 1px solid #ccc;
            padding: 10px;
            margin-bottom: 10px;
        }
    </style>
</head>
<body>
    <div id="chat-messages"></div>
    <input type="text" id="message-input" placeholder="输入消息">
    <button id="send-btn">发送</button>

    <script src="client.js"></script>
</body>
</html>

4.3 客户端JavaScript (public/client.js)

javascript 复制代码
const socket = new WebSocket('ws://localhost:3000');
let clientId = null;

const chatMessages = document.getElementById('chat-messages');
const messageInput = document.getElementById('message-input');
const sendBtn = document.getElementById('send-btn');

// WebSocket事件处理
socket.addEventListener('open', (event) => {
  appendMessage('系统', '连接成功');
});

socket.addEventListener('message', (event) => {
  const message = JSON.parse(event.data);

  switch(message.type) {
    case 'connection':
      clientId = message.clientId;
      appendMessage('系统', `您的ID是: ${clientId}`);
      break;
    case 'chat':
      appendMessage(message.clientId, message.message);
      break;
    case 'userJoined':
    case 'userLeft':
      appendMessage('系统', message.message);
      break;
    case 'typing':
      handleTypingNotification(message);
      break;
  }
});

socket.addEventListener('close', (event) => {
  appendMessage('系统', '连接已关闭');
});

// 发送消息
sendBtn.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: 'chat',
      message: message
    }));
    appendMessage(clientId, message);
    messageInput.value = '';
  }
}

// 消息追加到聊天窗口
function appendMessage(sender, text) {
  const messageEl = document.createElement('div');
  messageEl.innerHTML = `<strong>${sender}:</strong> ${text}`;
  chatMessages.appendChild(messageEl);
  chatMessages.scrollTop = chatMessages.scrollHeight;
}

// 处理打字状态通知
function handleTypingNotification(message) {
  // 可以在这里实现打字状态提示
}

5. 运行项目

bash 复制代码
# 启动服务器
node server.js

# 访问 http://localhost:3000

6. 功能特点

  1. 实时双向通信
  2. 唯一客户端标识
  3. 广播消息机制
  4. 连接/断开事件处理
  5. 聊天消息和系统通知

7. 性能与扩展建议

  1. 生产环境考虑使用Redis等外部存储管理WebSocket连接
  2. 增加身份验证机制
  3. 实现消息持久化
  4. 使用负载均衡

8. 安全注意事项

  1. 使用WSS(WebSocket Secure)
  2. 实现连接速率限制
  3. 验证和过滤消息内容
  4. 防止跨站WebSocket劫持
相关推荐
ssshooter20 小时前
看完就懂 useSyncExternalStore
前端·javascript·react.js
Live000001 天前
在鸿蒙中使用 Repeat 渲染嵌套列表,修改内层列表的一个元素,页面不会更新
前端·javascript·react native
柳杉1 天前
使用Ai从零开发智慧水利态势感知大屏(开源)
前端·javascript·数据可视化
球球pick小樱花1 天前
游戏官网前端工具库:海内外案例解析
前端·javascript·css
喝水的长颈鹿1 天前
【大白话前端 02】网页从解析到绘制的全流程
前端·javascript
用户14536981458781 天前
VersionCheck.js - 让前端版本更新变得简单优雅
前端·javascript
codingWhat1 天前
整理「祖传」代码,就是在开发脚手架?
前端·javascript·node.js
码路飞1 天前
写了个 AI 聊天页面,被 5 种流式格式折腾了一整天 😭
javascript·python
Lee川1 天前
优雅进化的JavaScript:从ES6+新特性看现代前端开发范式
javascript·面试
ServBay1 天前
Node.js、Bun 与 Deno,2026 年后端运行时选择指南
node.js·deno·bun