10-WebSocket 实时通信
·
WebSocket 实时通信
从 HTTP 轮询到 WebSocket:掌握浏览器与服务器的全双工实时通信技术
学习目标
读完本文,你将学会:
- 理解 WebSocket 与 HTTP 轮询的本质区别
- 掌握 WebSocket API 的完整使用方法
- 实现心跳检测、断线重连等生产级功能
- 对比 WebSocket、SSE、长轮询的适用场景
一、为什么需要 WebSocket
1.1 HTTP 的局限性
HTTP 是请求-响应模型:客户端发起请求,服务器才能响应。服务器无法主动向客户端推送数据。
客户端 ──请求──▶ 服务器
客户端 ◀──响应── 服务器
(服务器无法主动说"有新消息了")
1.2 实时通信方案的演进
| 方案 | 原理 | 延迟 | 资源消耗 | 适用场景 |
|---|---|---|---|---|
| 短轮询 | 客户端定时发送 HTTP 请求 | 取决于轮询间隔 | 高(大量无效请求) | 简单场景 |
| 长轮询 | 服务器挂起请求直到有数据 | 接近实时 | 中(连接保持) | 兼容旧浏览器 |
| SSE (Server-Sent Events) | HTTP 长连接,服务器单向推送 | 低 | 低 | 股票行情、新闻推送 |
| WebSocket | TCP 全双工连接 | 极低 | 低 | 聊天、游戏、协同编辑 |
短轮询: 请求→响应 请求→响应 请求→响应 (频繁往返)
长轮询: 请求→等待→响应 请求→等待→响应 (减少部分往返)
SSE: 连接建立→服务器持续推送→推送→推送 (单向)
WebSocket: 握手一次→双向自由通信 (全双工)
1.3 WebSocket 的优势
- 全双工通信:客户端和服务器可同时发送数据
- 低延迟:建立连接后无需重复握手
- 低开销:数据帧头仅 2-14 字节,远小于 HTTP 头
- 跨域支持:原生支持 CORS
二、WebSocket 协议基础
2.1 握手过程
WebSocket 连接始于一次 HTTP 升级请求:
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=
之后,TCP 连接从 HTTP 协议切换为 WebSocket 协议。
2.2 数据帧结构
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 ... |
+---------------------------------------------------------------+
FIN:是否为最后一帧opcode:操作码(1=文本,2=二进制,8=关闭,9=ping,10=pong)MASK:客户端发送的数据必须掩码Payload len:负载长度(0-125 直接表示,126 则用后续 2 字节,127 用 8 字节)
三、WebSocket API 详解
3.1 创建连接
const ws = new WebSocket('wss://echo.websocket.org/');
// 连接成功
ws.onopen = () => {
console.log('连接已建立');
ws.send('Hello Server!');
};
// 接收消息
ws.onmessage = (event) => {
console.log('收到:', event.data);
};
// 连接关闭
ws.onclose = (event) => {
console.log('连接关闭', event.code, event.reason);
};
// 发生错误
ws.onerror = (error) => {
console.error('WebSocket 错误:', error);
};
3.2 连接状态
console.log(ws.readyState);
// CONNECTING = 0 连接中
// OPEN = 1 已连接
// CLOSING = 2 关闭中
// CLOSED = 3 已关闭
3.3 发送数据
// 发送文本
ws.send('文本消息');
// 发送二进制(Blob)
const blob = new Blob(['二进制数据'], { type: 'text/plain' });
ws.send(blob);
// 发送 ArrayBuffer
const buffer = new ArrayBuffer(8);
ws.send(buffer);
3.4 关闭连接
// 正常关闭(code 1000 表示正常关闭)
ws.close(1000, '用户主动关闭');
常用关闭码:
| 状态码 | 含义 |
|---|---|
| 1000 | 正常关闭 |
| 1001 | 终端离开(如浏览器关闭) |
| 1006 | 异常关闭(连接意外断开) |
| 1008 | 策略违反 |
| 1011 | 服务器错误 |
四、生产级 WebSocket 封装
4.1 心跳检测
长时间无数据传输,中间代理可能断开连接。心跳机制保持连接活跃:
class HeartbeatWebSocket {
constructor(url) {
this.url = url;
this.ws = null;
this.heartbeatInterval = 30000; // 30 秒
this.heartbeatTimer = null;
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
this.startHeartbeat();
};
this.ws.onmessage = (event) => {
if (event.data === 'pong') return; // 忽略心跳响应
this.onMessage?.(event.data);
};
this.ws.onclose = () => {
this.stopHeartbeat();
};
}
startHeartbeat() {
this.heartbeatTimer = setInterval(() => {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send('ping');
}
}, this.heartbeatInterval);
}
stopHeartbeat() {
clearInterval(this.heartbeatTimer);
}
}
4.2 断线重连
class ReconnectWebSocket {
constructor(url, options = {}) {
this.url = url;
this.reconnectInterval = options.reconnectInterval || 3000;
this.maxReconnectAttempts = options.maxReconnectAttempts || 5;
this.attempts = 0;
this.ws = null;
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
this.attempts = 0;
console.log('连接成功');
};
this.ws.onclose = () => {
this.attemptReconnect();
};
this.ws.onerror = () => {
this.ws.close();
};
}
attemptReconnect() {
if (this.attempts >= this.maxReconnectAttempts) {
console.error('重连次数已达上限');
return;
}
this.attempts++;
console.log(`${this.attempts} 秒后尝试重连...`);
setTimeout(() => this.connect(), this.reconnectInterval);
}
}
完整代码见 CODE-ADVANCED/10-WebSocket实时通信/websocket-reconnect.js
五、实战案例:实时聊天室
5.1 需求分析
- 多用户实时收发消息
- 显示在线用户列表
- 消息历史记录
5.2 客户端实现
const chat = {
ws: null,
username: '',
connect(name) {
this.username = name;
this.ws = new WebSocket('wss://chat.example.com');
this.ws.onopen = () => {
this.send({ type: 'join', username: name });
};
this.ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case 'message': this.displayMessage(msg); break;
case 'userList': this.updateUserList(msg.users); break;
case 'system': this.displaySystem(msg.text); break;
}
};
},
send(data) {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
}
},
sendMessage(text) {
this.send({ type: 'message', text, from: this.username });
}
};
完整代码见 CODE-ADVANCED/10-WebSocket实时通信/websocket-chat.html
六、WebSocket vs SSE vs 长轮询
┌─────────────────┬──────────────┬──────────────┬──────────────┐
│ 特性 │ WebSocket │ SSE │ 长轮询 │
├─────────────────┼──────────────┼──────────────┼──────────────┤
│ 通信方向 │ 双向 │ 服务器→客户端 │ 双向(伪) │
│ 协议 │ TCP (ws/wss) │ HTTP │ HTTP │
│ 实时性 │ 极高 │ 高 │ 中 │
│ 浏览器兼容 │ IE10+ │ IE 不支持 │ 全部 │
│ 自动重连 │ 需手动实现 │ 原生支持 │ 需手动实现 │
│ 二进制数据 │ 支持 │ 不支持 │ 需 Base64 │
│ 多域名连接 │ 有限制 │ 无限制 │ 无限制 │
│ 防火墙穿透 │ 可能受阻 │ 易穿透 │ 易穿透 │
└─────────────────┴──────────────┴──────────────┴──────────────┘
选择建议:
- 需要双向实时(聊天、游戏)→ WebSocket
- 仅需服务器推送(股票、日志)→ SSE
- 需要兼容旧浏览器 → 长轮询
二、常见误区与注意点
| 误区 | 正确做法 |
|---|---|
| WebSocket 可以替代所有 HTTP | WebSocket 用于实时场景,REST API 仍适合 CRUD |
| 连接永不关闭 | 页面切换/网络变化时连接会断开,需处理重连 |
| 不需要考虑兼容性 | IE9 及以下不支持,需准备降级方案 |
| 所有数据都用 WebSocket | 文件上传等大流量操作仍用 HTTP 更合适 |
| 忽略错误处理 | 必须处理 onerror、onclose,实现重连策略 |
三、动手练习
练习 1:心跳检测实现
在基础 WebSocket 连接上添加心跳机制,如果 3 次心跳无响应则认为连接已断开,触发重连。
练习 2:简易协同白板
使用 WebSocket 实现多人实时绘图:鼠标移动时发送坐标,其他客户端实时绘制。
四、AI 辅助学习
4.1 本节知识点的 AI 提问模板
- “WebSocket 的 Sec-WebSocket-Key 是如何生成的?”
- “如何在 WebSocket 中实现消息确认机制(ACK)?”
- “WebSocket 连接数有浏览器限制吗?”
4.2 用 AI 验证你的理解
尝试向 AI 描述 WebSocket 握手过程,让它判断你的理解是否正确。
4.3 警惕 AI 的常见错误
- AI 可能混淆 WebSocket 和 Socket.IO(后者是库,不是协议)
- AI 可能忘记提到 WebSocket 的掩码机制
五、配套代码
本文示例代码位于:CODE-ADVANCED/10-WebSocket实时通信/
| 文件名 | 说明 |
|---|---|
websocket-basic.html |
WebSocket API 基础演示 |
websocket-chat.html |
实时聊天室完整实现 |
websocket-reconnect.js |
断线重连与心跳封装 |
六、本章小结
- WebSocket 通过 HTTP 升级握手建立 TCP 全双工连接
- 心跳检测和断线重连是生产环境的必备功能
- WebSocket 适合双向实时场景,SSE 适合单向推送
- 注意处理连接状态、错误情况和浏览器兼容性
如果本文对你有帮助,欢迎点赞、收藏、关注专栏。有任何问题可以在评论区交流!
更多推荐


所有评论(0)