当前位置: 首页 > news >正文

WebSocket 详解

WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,实现了浏览器与服务器之间的实时双向数据传输。

一、WebSocket 基础概念

1. 为什么需要 WebSocket?

传统 HTTP 的问题:​

  • 单向通信(只能客户端发起请求)

  • 实时性差(需要轮询)

  • 头部信息冗余(每次请求都包含完整的 HTTP 头部)

WebSocket 的优势:​

  • 真正的双向通信

  • 低延迟(建立连接后持续通信)

  • 减少带宽占用(连接建立后头部信息很少)

2. WebSocket 协议特点

  • 协议标识:​ws://(非加密)或 wss://(加密,类似 HTTPS)

  • 握手过程:​​ 基于 HTTP 协议升级

  • 持久连接:​​ 一旦建立,保持连接状态

  • 跨域支持:​​ 内置支持跨域通信

二、WebSocket 客户端 API

1. 基本使用

// 创建 WebSocket 连接
const socket = new WebSocket('ws://localhost:8080');// 连接建立时触发
socket.onopen = function(event) {console.log('连接已建立');// 发送消息socket.send('Hello Server!');
};// 接收消息时触发
socket.onmessage = function(event) {console.log('收到消息:', event.data);const message = JSON.parse(event.data);handleMessage(message);
};// 连接关闭时触发
socket.onclose = function(event) {console.log('连接关闭:', event.code, event.reason);if (event.wasClean) {console.log('连接正常关闭');} else {console.log('连接异常断开');}
};// 发生错误时触发
socket.onerror = function(error) {console.error('WebSocket 错误:', error);
};

2. 完整的 WebSocket 客户端类

class WebSocketClient {constructor(url, options = {}) {this.url = url;this.socket = null;this.reconnectAttempts = 0;this.maxReconnectAttempts = options.maxReconnectAttempts || 5;this.reconnectInterval = options.reconnectInterval || 3000;this.messageHandlers = new Map();this.isConnected = false;this.setupEventHandlers();this.connect();}connect() {try {this.socket = new WebSocket(this.url);this.setupSocketEvents();} catch (error) {console.error('创建 WebSocket 连接失败:', error);this.handleReconnection();}}setupSocketEvents() {this.socket.onopen = (event) => {console.log('WebSocket 连接已建立');this.isConnected = true;this.reconnectAttempts = 0;this.emit('connected', event);};this.socket.onmessage = (event) => {try {const data = JSON.parse(event.data);this.handleMessage(data);} catch (error) {console.error('消息解析错误:', error);}};this.socket.onclose = (event) => {console.log('WebSocket 连接关闭');this.isConnected = false;this.emit('disconnected', event);this.handleReconnection();};this.socket.onerror = (error) => {console.error('WebSocket 错误:', error);this.emit('error', error);};}handleMessage(data) {const { type, payload } = data;// 调用特定类型的处理器if (this.messageHandlers.has(type)) {this.messageHandlers.get(type).forEach(handler => {handler(payload);});}// 调用全局处理器this.emit('message', data);}send(type, payload) {if (this.isConnected && this.socket) {const message = JSON.stringify({ type, payload, timestamp: Date.now() });this.socket.send(message);} else {console.warn('WebSocket 未连接,消息发送失败');}}on(event, handler) {if (!this.messageHandlers.has(event)) {this.messageHandlers.set(event, []);}this.messageHandlers.get(event).push(handler);}emit(event, data) {if (this.messageHandlers.has(event)) {this.messageHandlers.get(event).forEach(handler => {handler(data);});}}handleReconnection() {if (this.reconnectAttempts < this.maxReconnectAttempts) {this.reconnectAttempts++;console.log(`尝试重新连接... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);setTimeout(() => {this.connect();}, this.reconnectInterval);} else {console.error('达到最大重连次数,停止重连');this.emit('reconnect_failed');}}close() {if (this.socket) {this.socket.close(1000, '正常关闭');}}
}

3. 在 React 中使用 WebSocket

import React, { useState, useEffect, useRef } from 'react';function ChatApp() {const [messages, setMessages] = useState([]);const [isConnected, setIsConnected] = useState(false);const socketRef = useRef();useEffect(() => {// 创建 WebSocket 连接socketRef.current = new WebSocketClient('ws://localhost:8080/chat');// 注册消息处理器socketRef.current.on('connected', () => {setIsConnected(true);});socketRef.current.on('disconnected', () => {setIsConnected(false);});socketRef.current.on('chat_message', (message) => {setMessages(prev => [...prev, message]);});socketRef.current.on('user_joined', (user) => {setMessages(prev => [...prev, {type: 'system',content: `${user.username} 加入了聊天室`}]);});// 清理函数return () => {if (socketRef.current) {socketRef.current.close();}};}, []);const sendMessage = (content) => {if (socketRef.current) {socketRef.current.send('chat_message', {content,userId: 'current-user-id',timestamp: Date.now()});}};return (<div className="chat-app"><div className={`status ${isConnected ? 'connected' : 'disconnected'}`}>{isConnected ? '已连接' : '连接中...'}</div><MessageList messages={messages} /><MessageInput onSendMessage={sendMessage} /></div>);
}

三、WebSocket 服务器端实现

1. Node.js + ws 库实现

const WebSocket = require('ws');
const http = require('http');
const url = require('url');// 创建 HTTP 服务器
const server = http.createServer((req, res) => {res.writeHead(200, { 'Content-Type': 'text/plain' });res.end('WebSocket Server');
});// 创建 WebSocket 服务器
const wss = new WebSocket.Server({ server,// 验证连接verifyClient: (info, callback) => {const { query } = url.parse(info.req.url, true);// 简单的 token 验证if (query.token && isValidToken(query.token)) {callback(true);} else {callback(false, 401, 'Unauthorized');}}
});// 存储连接的客户端
const clients = new Map();function isValidToken(token) {// 实现 token 验证逻辑return token === 'valid-token';
}wss.on('connection', (ws, req) => {const { query } = url.parse(req.url, true);const userId = query.userId;console.log(`客户端连接: ${userId}`);// 存储客户端连接clients.set(userId, ws);// 广播用户上线通知broadcast({type: 'user_online',userId: userId,timestamp: Date.now()}, userId);// 处理消息ws.on('message', (data) => {try {const message = JSON.parse(data);handleMessage(ws, message, userId);} catch (error) {console.error('消息解析错误:', error);sendError(ws, '消息格式错误');}});// 处理连接关闭ws.on('close', (code, reason) => {console.log(`客户端断开: ${userId}, 代码: ${code}, 原因: ${reason}`);clients.delete(userId);// 广播用户离线通知broadcast({type: 'user_offline',userId: userId,timestamp: Date.now()});});// 处理错误ws.on('error', (error) => {console.error(`客户端错误 (${userId}):`, error);});// 发送欢迎消息ws.send(JSON.stringify({type: 'welcome',message: '连接成功',userId: userId,timestamp: Date.now()}));
});function handleMessage(ws, message, userId) {const { type, payload } = message;switch (type) {case 'chat_message':// 广播聊天消息broadcast({type: 'chat_message',payload: {...payload,userId: userId,messageId: generateMessageId(),timestamp: Date.now()}});break;case 'private_message':// 私聊消息const targetUserId = payload.targetUserId;const targetClient = clients.get(targetUserId);if (targetClient && targetClient.readyState === WebSocket.OPEN) {targetClient.send(JSON.stringify({type: 'private_message',payload: {...payload,fromUserId: userId,timestamp: Date.now()}}));}break;case 'typing':// 广播输入状态broadcast({type: 'user_typing',userId: userId,isTyping: payload.isTyping}, userId);break;default:sendError(ws, `未知的消息类型: ${type}`);}
}function broadcast(message, excludeUserId = null) {const data = JSON.stringify(message);clients.forEach((client, userId) => {if (userId !== excludeUserId && client.readyState === WebSocket.OPEN) {client.send(data);}});
}function sendError(ws, errorMessage) {ws.send(JSON.stringify({type: 'error',message: errorMessage,timestamp: Date.now()}));
}function generateMessageId() {return Date.now().toString(36) + Math.random().toString(36).substr(2);
}// 启动服务器
server.listen(8080, () => {console.log('WebSocket 服务器运行在端口 8080');
});

2. 心跳检测实现

// 心跳检测机制
function setupHeartbeat(ws) {let isAlive = true;let heartbeatInterval;const heartbeat = () => {if (!isAlive) {return ws.terminate();}isAlive = false;ws.ping();};ws.on('pong', () => {isAlive = true;});ws.on('close', () => {clearInterval(heartbeatInterval);});// 每 30 秒发送一次心跳检测heartbeatInterval = setInterval(heartbeat, 30000);// 初始心跳检测heartbeat();
}

四、WebSocket 协议详解

1. 握手过程

客户端请求:​

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

服务器响应:​

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

2. 数据帧格式

WebSocket 消息由多个帧组成:

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+

五、WebSocket 与 Socket.IO 对比

特性

WebSocket

Socket.IO

协议支持

纯 WebSocket 协议

WebSocket + HTTP 长轮询降级

浏览器兼容

现代浏览器

所有浏览器(包括旧版)

自动重连

需要手动实现

内置自动重连机制

房间支持

需要手动实现

内置房间和命名空间

二进制数据

原生支持

原生支持

体积

轻量(浏览器内置)

较大(需要引入库)

六、生产环境最佳实践

1. 安全性考虑

// 1. 使用 WSS
const socket = new WebSocket('wss://yourdomain.com/ws');// 2. 验证 Origin
wss.on('headers', (headers, req) => {const origin = req.headers.origin;if (!isAllowedOrigin(origin)) {throw new Error('Origin not allowed');}
});// 3. 限制消息大小
ws.on('message', (data) => {if (data.length > MAX_MESSAGE_SIZE) {ws.close(1009, 'Message too large');return;}// 处理消息...
});

2. 性能优化

// 1. 消息压缩
function compressMessage(message) {// 实现消息压缩逻辑return JSON.stringify(message);
}// 2. 批量发送
function sendBatch(messages) {const batch = {type: 'batch',messages: messages,timestamp: Date.now()};socket.send(compressMessage(batch));
}// 3. 连接池管理
class ConnectionPool {constructor(maxConnections = 100) {this.maxConnections = maxConnections;this.connections = new Set();}addConnection(ws) {if (this.connections.size >= this.maxConnections) {// 关闭最旧的连接const oldest = this.getOldestConnection();oldest.close(1000, 'Connection pool full');}this.connections.add(ws);}
}

3. 监控和日志

// 监控连接状态
function setupMonitoring(wss) {let connections = 0;let messagesReceived = 0;wss.on('connection', (ws) => {connections++;console.log(`活跃连接数: ${connections}`);ws.on('close', () => {connections--;console.log(`活跃连接数: ${connections}`);});ws.on('message', () => {messagesReceived++;});});// 定期报告指标setInterval(() => {console.log(`统计 - 连接数: ${connections}, 消息数: ${messagesReceived}`);}, 60000);
}

七、常见应用场景

  1. 实时聊天应用

  2. 在线游戏

  3. 股票行情推送

  4. 协同编辑工具

  5. 实时通知系统

  6. 物联网设备监控

WebSocket 是现代 Web 应用中实现实时通信的核心技术,正确使用它可以构建出高性能的实时应用程序。

http://www.dtcms.com/a/537244.html

相关文章:

  • SPR 实验笔记:从原理到实操,解锁天然产物筛选、靶点验证与膜蛋白互作的“金标准”技术
  • 发布会回顾|袋鼠云发布多模态数据中台,重构AI时代的数据底座
  • AOI在PCB制造领域的核心应用
  • 网站建设系统规划seo信息优化
  • 建筑公司网站设计思路静态网站怎么样
  • python在Linux服务器中安装
  • 排序算法解析
  • 餐饮 网站建设互联斗士网站建站
  • 民营医院网站建设视频网站点击链接怎么做
  • Java 大视界 -- Java 大数据机器学习模型在游戏用户行为分析与游戏平衡优化中的应用
  • 微信小程序-智慧社区项目开发完整技术文档(上)
  • 2025年10月主流工程项目管理软件推荐
  • 设计模版网站一级a做爰片365网站
  • 计算机网络自顶向下方法7——应用层 HTTP概述及其连接方式
  • 网站建设贵不贵wordpress站文章显示时分秒
  • 【编译原理笔记】3.4 Tokens Recognization
  • day19_添加修改删除
  • 【Linux】ps -ef 和 ps -aux的区别
  • OpenFeign与Sentinel集成的原理
  • window系统下利用anaconda安装labelImag
  • Windows开机启动命令
  • LocalDream 2.1.2 |在手机设备上运行SD模型,支持文本到图像生成和图像重绘,无任何限制
  • 招聘网站建设维护求个网站直接能看的
  • 辽宁pc网站建设开发网站建设登记表
  • 网站 目录结构想注册一家公司怎么注册
  • 玩转前端图标系统:从零搭建一套完整的图标选择器组件
  • 卡尔费休滴定法微量水分测定仪:高精度水分分析的核心技术解析
  • 【重庆政务服务网-注册_登录安全分析报告】
  • 大型网站开发的主流语言网站的标题优化怎么做
  • 3.Xposed框架入门指南:深入解析Hook内部类与匿名类的实现技巧