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劫持
相关推荐
明天也要努力学习7 小时前
HTTP 协议的基本格式和 fiddler 的用法
网络协议·http·fiddler
Tel1992530800413 小时前
温度湿度信号转WiFi/MQTT/Modbus TCP采集模块 无线传输
网络·网络协议·tcp/ip·工业自动化·工控设备
web打印社区16 小时前
Chrome-kiosk-printing能上生产吗
开发语言·前端·javascript·chrome·websocket
lazy H16 小时前
【计算机网络】第 13 篇:NAT 和端口映射是什么?为什么本地服务外网访问不了?
java·网络·网络协议·学习·计算机网络·408
低慢小uav17 小时前
(已解决)Zynq芯片lwip协议栈,使用tcp作为服务器Service,接收大量数据时,长度接收正常,接收对数据校验,校验和异常分析
网络协议·tcp/ip·fpga开发
雨季mo浅忆18 小时前
记录Node.js安全漏洞及其内置Brotli解码器问题
node.js·安全性测试
tommyjiatong18 小时前
资安底座连环雷:从 PHP2Shell 到 Node2Shell
node.js·php
|_⊙20 小时前
应用层协议HTTP与HTTPS
网络·网络协议·http
SEO_juper21 小时前
Bash 交叉验证 robots.txt 和 sitemap:两个文件自相矛盾,页面永远收录不了
开发语言·python·node.js·json·bash
byte轻骑兵1 天前
【PBAP】规范精讲[9]: 蓝牙PBAP的互联互通法则——GOEP互操作性要求深度解析
网络协议·人机交互·蓝牙协议·pbap