Node.js实现WebSocket教程

Node.js实现WebSocket教程

1. WebSocket简介

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

2. 技术选型

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

2.1 安装依赖
复制代码
# 创建项目目录
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)
复制代码
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)
复制代码
<!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)
复制代码
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. 运行项目

复制代码
# 启动服务器
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劫持
相关推荐
win x19 分钟前
深入理解HTTPS协议加密流程
网络协议·http·https
仙俊红32 分钟前
从 Filter / Interceptor 到 HTTPS
网络协议·http·https
全栈工程师修炼指南1 小时前
Nginx | stream 四层反向代理:SSL、PREREAD 阶段模块指令浅析与实践
运维·网络·网络协议·nginx·ssl
极新2 小时前
智启新篇,智创未来,“2026智造新IP:AI驱动品牌增长新周期”峰会暨北京电子商务协会第五届第三次会员代表大会成功举办
人工智能·网络协议·tcp/ip
M158227690552 小时前
TCP转LORA产品说明及应用案例
网络·网络协议·tcp/ip
2501_915106323 小时前
使用 Sniffmaster TCP 抓包和 Wireshark 网络分析
网络协议·tcp/ip·ios·小程序·uni-app·wireshark·iphone
我爱加班、、3 小时前
Websocket能携带token过去后端吗
前端·后端·websocket
那就回到过去3 小时前
MSTP路由协议简介
网络·网络协议·tcp/ip·ensp
阿钱真强道4 小时前
12 JetLinks MQTT直连设备事件上报实战(继电器场景)
linux·服务器·网络·数据库·网络协议
乾元4 小时前
终端安全(EDR):用深度学习识别未知勒索软件
运维·人工智能·网络协议·安全·网络安全·自动化·安全架构