WebSocket 是 HTML5 推出的全双工、长连接网络协议,解决了 HTTP "请求 - 响应" 模型下无法实现服务器主动推送数据的痛点(如实时聊天、股票行情、直播弹幕)。本文会从核心原理、HTTP 升级流程、代码示例、核心约束四个维度,完整拆解 WebSocket。
一、WebSocket 核心基础
1. 核心特性(对比 HTTP)
HTTP 是「半双工、短连接」:客户端发请求 → 服务器回响应,响应结束连接断开,服务器无法主动给客户端发数据;WebSocket 是「全双工、长连接」:连接建立后,客户端和服务器可同时、双向发送数据,连接持续保持,无需频繁重连。
| 特性 | HTTP | WebSocket |
|---|---|---|
| 通信方向 | 半双工(客户端主动) | 全双工(双向主动) |
| 连接方式 | 短连接(请求完断开) | 长连接(持续保持) |
| 数据格式 | 带完整 HTTP 头 | 轻量帧格式(无冗余头) |
| 服务器推送 | 不支持 | 原生支持 |
| 协议标识 | http/https | ws/wss(加密版) |
2. 应用场景
- 实时聊天(如微信网页版、在线客服);
- 实时数据推送(如股票行情、区块链价格);
- 多人协作(如在线文档、白板);
- 直播弹幕、游戏实时交互。
二、从 HTTP 升级到 WebSocket 的完整流程
WebSocket 连接的建立必须通过 HTTP 协议 "升级"(RFC 6455 规范),核心是客户端发送「升级请求」,服务器确认后切换协议,整个过程分为 7 步:
前置说明
- WebSocket 升级基于 TCP 连接(先完成 TCP 三次握手,再谈协议升级);
- 升级成功后,原 HTTP 连接变为 WebSocket 连接,协议标识从
http变为ws(加密版为wss,对应 HTTPS); - 核心升级头:
Upgrade: websocket+Connection: Upgrade(告知服务器 "要切换协议")。
完整升级流程(以 ws://example.com/chat 为例)

关键步骤拆解(核心字段说明)
步骤 2:客户端发送升级请求(核心头解析)
客户端的 HTTP 请求必须包含以下关键头,缺一不可:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket # 核心:请求升级为 WebSocket 协议
Connection: Upgrade # 核心:配合 Upgrade,标识连接要切换
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== # 随机字符串,用于验证(防伪造)
Sec-WebSocket-Version: 13 # 必须为 13(最新标准版本)
Sec-WebSocket-Protocol: chat, superchat # 可选:指定子协议(如聊天专用协议)
Sec-WebSocket-Extensions: permessage-deflate # 可选:压缩扩展
Sec-WebSocket-Key:客户端生成的随机 Base64 字符串,服务器需用它生成Sec-WebSocket-Accept,防止 "假升级请求";Sec-WebSocket-Version:13:唯一合法的版本号(老版本已废弃)。
步骤 3:服务器验证请求
服务器必须做 3 个核心验证,否则拒绝升级:
- 校验
Sec-WebSocket-Version是否为 13; - 校验
Upgrade: websocket和Connection: Upgrade是否存在; - 计算
Sec-WebSocket-Accept:- 拼接:
Sec-WebSocket-Key+ 固定字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11; - 哈希:对拼接结果做 SHA1 哈希;
- 编码:将哈希结果做 Base64 编码,得到
Sec-WebSocket-Accept。
- 拼接:
步骤 4:服务器返回 101 响应
验证通过后,服务器返回 101 Switching Protocols 响应(HTTP 状态码 101 表示 "协议切换"):
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= # 服务器计算后的结果
Sec-WebSocket-Protocol: chat # 可选:确认使用的子协议
步骤 5:客户端验证响应
客户端会用相同算法计算 Sec-WebSocket-Accept,对比服务器返回的值:
- 一致:验证通过,WebSocket 连接建立;
- 不一致:客户端断开连接,升级失败。
步骤 6:双向通信
连接建立后,双方不再使用 HTTP 报文,而是用 WebSocket 帧 传输数据:
- 帧格式轻量(仅包含操作码、长度、数据),无 HTTP 头的冗余开销;
- 支持文本、二进制数据传输;
- 可发送 "PING/PONG" 帧做心跳检测(保持连接)。
这两步是为了避免服务器误升级或者中间人伪造升级相应。
三、HTTP 升级到 WebSocket 的代码示例
以下是基于 Node.js + ws 库 (服务端)和 浏览器原生 WebSocket(客户端)的完整可运行示例:
1. 服务端代码(Node.js)
javascript
// 1. 安装依赖:npm install ws
const WebSocket = require('ws');
const http = require('http');
// 第一步:创建 HTTP 服务器(WebSocket 升级基于 HTTP 服务器)
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end('普通 HTTP 请求');
});
// 第二步:创建 WebSocket 服务器,挂载到 HTTP 服务器上
const wss = new WebSocket.Server({ server });
// 第三步:监听 WebSocket 连接
wss.on('connection', (ws) => {
console.log('客户端已建立 WebSocket 连接');
// 监听客户端发送的消息
ws.on('message', (data) => {
console.log('收到客户端消息:', data.toString());
// 服务器主动给客户端发消息(全双工)
ws.send(`服务器回复:${data.toString()}`);
});
// 监听连接关闭
ws.on('close', () => {
console.log('WebSocket 连接已关闭');
});
// 监听错误
ws.on('error', (err) => {
console.error('WebSocket 错误:', err);
});
});
// 启动服务器,监听 8080 端口
server.listen(8080, () => {
console.log('服务器运行在 http://localhost:8080');
console.log('WebSocket 地址:ws://localhost:8080');
});
2. 客户端代码(浏览器 HTML)
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket 示例</title>
</head>
<body>
<input type="text" id="msgInput" placeholder="输入消息">
<button onclick="sendMsg()">发送</button>
<div id="msgList"></div>
<script>
// 第一步:创建 WebSocket 实例(自动触发 HTTP 升级流程)
const ws = new WebSocket('ws://localhost:8080');
// 第二步:监听 WebSocket 事件
// 1. 连接成功事件
ws.onopen = () => {
console.log('WebSocket 连接已建立');
appendMsg('连接成功:可以发送消息了');
};
// 2. 接收服务器消息事件
ws.onmessage = (event) => {
appendMsg(`服务器:${event.data}`);
};
// 3. 连接关闭事件
ws.onclose = (event) => {
appendMsg(`连接关闭:${event.code} - ${event.reason}`);
};
// 4. 错误事件
ws.onerror = (error) => {
appendMsg(`错误:${error.message}`);
};
// 发送消息函数
function sendMsg() {
const input = document.getElementById('msgInput');
const msg = input.value.trim();
if (!msg) return;
// 客户端发送消息给服务器
ws.send(msg);
appendMsg(`客户端:${msg}`);
input.value = '';
}
// 辅助函数:追加消息到页面
function appendMsg(text) {
const msgList = document.getElementById('msgList');
const p = document.createElement('p');
p.textContent = text;
msgList.appendChild(p);
}
</script>
</body>
</html>
运行步骤
- 启动服务端:
node server.js; - 打开客户端 HTML 文件(直接双击或用浏览器打开);
- 在输入框输入消息,点击 "发送",即可看到客户端与服务器的双向实时通信。
四、WebSocket 的核心约束(使用注意事项)
1. 协议层面约束
- 必须通过 HTTP/HTTPS 升级:无法直接建立 WebSocket 连接,必须先发起 HTTP 升级请求,且仅支持 GET 方法;
- 版本强制 :仅
Sec-WebSocket-Version:13合法,老版本(如 8、12)已被废弃,服务器需拒绝非 13 版本的请求; - 跨域规则 :遵循与 HTTP 相同的跨域规则,若需跨域,服务器需配置
Access-Control-Allow-Origin头; - 加密版(wss) :生产环境必须用
wss://(对应 HTTPS 升级),ws://明文传输存在数据泄露风险,且部分浏览器 / 服务器会拦截。
2. 连接层面约束
- 长连接资源占用:每个 WebSocket 连接是长连接,服务器需控制最大连接数(避免内存耗尽);
- 心跳检测:网络波动可能导致连接 "假死",需定期发送 PING/PONG 帧(客户端发 PING,服务器回 PONG),超时则重连;
- 断开重连:连接可能因网络、服务器重启断开,客户端需实现自动重连逻辑;
- 帧大小限制:服务器通常限制单帧数据大小(如 1MB),避免大文件传输压垮服务器(大文件建议用 HTTP 分段传输)。
3. 服务器 / 环境约束
-
反向代理配置 :若前端通过 Nginx 反向代理访问 WebSocket,需配置 Nginx 支持 WebSocket 升级:
server { listen 80; server_name example.com; location /chat { proxy_pass http://localhost:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; # 关键:传递升级头 proxy_set_header Connection "upgrade"; # 关键:传递连接头 proxy_set_header Host $host; proxy_read_timeout 86400; # 延长超时时间(长连接) } } -
防火墙 / 负载均衡:需放行 WebSocket 端口,且负载均衡器需支持 "粘性会话"(确保同一客户端的连接始终路由到同一服务器)。
4. 数据传输约束
- 数据格式:支持文本(UTF-8)和二进制数据,但需双方约定格式(如 JSON 文本、Protobuf 二进制);
- 无内置重传:WebSocket 帧传输无重传机制,若数据丢失,需上层业务实现重传逻辑;
- 流量控制:全双工传输可能导致数据积压,需实现流量控制(如客户端 / 服务器缓存满时暂停发送)。
五、核心总结
- WebSocket 本质:基于 TCP 长连接的全双工协议,通过 HTTP 升级流程建立,解决了 HTTP 无法服务器主动推送的问题;
- 升级核心 :客户端发送
Upgrade: websocket头的 GET 请求,服务器验证后返回 101 响应,协议切换为 ws/wss; - 关键验证 :通过
Sec-WebSocket-Key/Sec-WebSocket-Accept防止伪造请求,确保升级合法性; - 核心约束:生产环境用 wss 加密、实现心跳检测和重连、配置反向代理支持升级、控制连接数和帧大小。