目录
[1 协议介绍](#1 协议介绍)
[1.1 协议基础](#1.1 协议基础)
[1.2 协议握手流程(核心)](#1.2 协议握手流程(核心))
[2 核心:WebSocket 数据帧格式](#2 核心:WebSocket 数据帧格式)
[2.1 帧结构](#2.1 帧结构)
[2.2 关键字段详解](#2.2 关键字段详解)
[2.3 帧传输规则](#2.3 帧传输规则)
[3 连接和传输管理](#3 连接和传输管理)
[3.1 连接管理](#3.1 连接管理)
[3.2 协议安全与扩展](#3.2 协议安全与扩展)
[4 WebSocket 与 HTTP 协议的核心区别](#4 WebSocket 与 HTTP 协议的核心区别)
[5 总结](#5 总结)
概述
WebSocket 是由 IETF 标准化的全双工、低开销、基于 TCP 的应用层通信协议 ,标准文档为 RFC 6455 ,其核心目标是解决 HTTP 协议 "客户端单向请求、服务器无法主动推送" 的痛点,实现客户端与服务器的实时双向持久通信。以下从协议底层机制、核心流程、帧结构等维度进行深度拆解。
1 协议介绍
1.1 协议基础
1) 核心特性
- 全双工通信:连接建立后,客户端和服务器可同时收发数据,无需等待对方响应。
- 持久连接:一次握手后保持 TCP 连接,避免 HTTP 短连接的频繁三次握手开销。
- 低数据开销:数据传输阶段无需携带 HTTP 头,仅需少量帧协议开销(相比 HTTP 1.1 的请求头体积大幅降低)。
- 跨域支持:原生支持跨域通信,依赖服务器配置跨域规则即可。
- 二进制支持:可直接传输文本(UTF-8)和二进制数据(如文件、音视频流)
2) 协议架构
WebSocket 协议分为两层:
- 握手层:基于 HTTP 1.1 协议完成 "协议升级",是连接建立的桥梁。
- 数据传输层:定义独立的帧格式,用于封装和传输数据。
1.2 协议握手流程(核心)
WebSocket 连接建立的核心是 HTTP 协议升级 ,整个过程为单次往返(客户端发请求,服务器回响应),无需额外交互。
1) 客户端发起升级请求
客户端向服务器发送 HTTP GET 请求 ,请求头必须包含以下关键字段,且请求方法只能是 GET,HTTP 版本至少为 1.1。
| 字段名 | 取值 | 作用 |
|---|---|---|
Upgrade |
websocket |
声明要升级的目标协议 |
Connection |
Upgrade |
标识这是一个协议升级请求 |
Sec-WebSocket-Key |
16 字节随机值的 Base64 编码 | 用于验证服务器合法性,防止伪造响应 |
Sec-WebSocket-Version |
13 |
指定协议版本(RFC 6455 对应版本,主流浏览器均支持) |
Sec-WebSocket-Extensions(可选) |
如 permessage-deflate |
协商协议扩展(如数据压缩) |
Sec-WebSocket-Protocol(可选) |
如 chat |
协商应用层子协议(服务器可选择支持的子协议) |
请求示例:
bash
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Protocol: chat, superchat
2) 服务器验证并返回响应
服务器收到请求后,需完成 3 个核心验证:
- 检查
Upgrade和Connection字段是否为websocket和Upgrade;- 检查
Sec-WebSocket-Version是否为13(不支持则返回426 Upgrade Required);- 验证
Sec-WebSocket-Key并生成Sec-WebSocket-Accept。
--1) Sec-WebSocket-Accept 计算规则
服务器执行以下步骤生成响应密钥:
- 将客户端的
Sec-WebSocket-Key与固定字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接;- 对拼接后的字符串做 SHA-1 哈希计算(得到 20 字节二进制数据);
- 将哈希结果做 Base64 编码 ,得到
Sec-WebSocket-Accept值。
计算示例:
若客户端
Sec-WebSocket-Key为dGhlIHNhbXBsZSBub25jZQ==,拼接固定字符串后哈希编码,最终得到s3pPLMBiTxaQ9kYGzzhZRbK+xOo=。
--2) 服务器响应(HTTP 101 状态码)
验证通过后,服务器返回 HTTP 101 Switching Protocols 响应,响应头包含以下字段:
bash
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Protocol: chat
101状态码:表示服务器同意切换协议;Sec-WebSocket-Accept:上述计算结果,客户端会验证该值是否正确;- 其他字段:返回协商成功的扩展和子协议。
3) 握手成功后的状态切换
客户端验证 Sec-WebSocket-Accept 无误后,HTTP 连接正式切换为 WebSocket 连接,后续通信不再使用 HTTP 协议,而是基于 WebSocket 帧格式传输数据。
2 核心:WebSocket 数据帧格式
2.1 帧结构
WebSocket 数据传输的最小单位是帧(Frame),所有数据(文本、二进制、控制指令)都必须封装成帧发送。RFC 6455 定义的帧结构如下(单位:比特):
| 字段 | 比特数 | 作用 |
|---|---|---|
| FIN | 1 | 标识是否为消息的最后一帧:1 = 最后一帧,0 = 后续还有帧(分片传输) |
| RSV1~RSV3 | 各 1 | 保留位,仅在使用协议扩展时非 0,否则必须为 0(否则连接关闭) |
| Opcode | 4 | 帧类型标识,核心取值见下表 |
| Mask | 1 | 标识负载数据是否加掩码:1 = 加掩码(客户端→服务器必须为 1),0 = 无掩码(服务器→客户端必须为 0) |
| Payload length | 7 / 7+16 / 7+64 | 负载数据长度,分三种编码方式 |
| Masking-key | 0 / 32 | 掩码密钥,仅当 Mask=1 时存在,共 4 字节 |
| Payload data | 可变 | 实际传输的数据:负载数据 + 扩展数据(可选) |
2.2 关键字段详解
1) Opcode(帧类型): 这是帧的核心标识,决定了帧的用途,主流取值:
| Opcode 值 | 帧类型 | 说明 |
|---|---|---|
0x00 |
延续帧 | 用于分片传输的非第一帧,需配合 FIN 位使用 |
0x01 |
文本帧 | 负载数据为 UTF-8 编码的文本 |
0x02 |
二进制帧 | 负载数据为任意二进制数据 |
0x08 |
关闭帧 | 用于关闭连接,负载数据包含关闭状态码和原因 |
0x09 |
Ping 帧 | 心跳检测请求,服务器收到后必须回复 Pong 帧 |
0x0A |
Pong 帧 | 心跳检测响应,对应 Ping 帧 |
(2)Payload length(负载长度编码)
为了节省空间,长度字段采用可变长度编码:
- 若长度 ≤ 125:直接用 7 比特表示,取值即长度;
- 若 126 ≤ 长度 ≤ 65535:7 比特设为 126,后续 16 比特表示实际长度;
- 若长度 > 65535:7 比特设为 127,后续 64 比特表示实际长度。
(3)Masking-key(掩码机制)
- 强制要求 :客户端发送给服务器的所有帧必须加掩码(
Mask=1),服务器发送的帧必须不加掩码;- 作用:防止恶意客户端构造特定数据,导致中间设备(如代理)缓存污染;
- 解码规则 :负载数据的每个字节与掩码密钥的 4 字节循环异或,公式:
decoded_byte = encoded_byte XOR masking_key[index % 4]。
2.3 帧传输规则
- 分片传输 :大消息可拆分为多个帧,仅最后一帧的
FIN=1,其余帧FIN=0且Opcode=0x00;- 控制帧优先级 :Ping/Pong/ 关闭帧必须是单帧(
FIN=1),且不能被分片,需优先传输;- 数据顺序:帧的传输顺序与接收顺序一致,服务器和客户端需按序拼接分片。
3 连接和传输管理
3.1 连接管理
1) 连接状态机
WebSocket 连接生命周期分为 3 个状态:
- CONNECTING:握手进行中,尚未完成协议升级;
- OPEN:连接已建立,可正常收发数据;
- CLOSED:连接已关闭,无法传输数据。
2) 连接关闭流程
WebSocket 关闭连接需遵循双向握手机制,避免数据丢失,流程如下:
- 发起方(客户端 / 服务器)发送 关闭帧(Opcode=0x08) ,负载数据包含关闭状态码 (2 字节)和原因字符串(UTF-8 可选);
- 接收方收到关闭帧后,必须回复相同的关闭帧;
- 双方均发送并收到关闭帧后,关闭底层 TCP 连接
常见关闭状态码(RFC 6455 定义):
| 状态码 | 含义 | 场景 |
|---|---|---|
1000 |
正常关闭 | 连接完成预期目的后关闭 |
1001 |
端点离开 | 客户端关闭页面 / 服务器重启 |
1002 |
协议错误 | 收到非法帧或不符合协议的操作 |
1003 |
不支持的数据类型 | 收到不支持的 Opcode 帧 |
1006 |
异常关闭 | 未发送关闭帧直接断开 TCP 连接 |
3)心跳机制(Ping/Pong)
为了检测连接是否存活,WebSocket 定义了 Ping/Pong 心跳机制:
- 一方发送 Ping 帧(Opcode=0x09),负载数据可选;
- 另一方必须在合理时间内回复 Pong 帧(Opcode=0x0A),且 Pong 帧的负载数据必须与 Ping 帧一致;
- 若未收到 Pong 响应,可判定连接异常,触发重连逻辑。
3.2 协议安全与扩展
1) 安全传输:WSS 协议
WebSocket 有两种传输模式:
ws://:明文传输,基于 TCP,端口通常为 80;wss://:加密传输,基于 TCP + TLS/SSL,端口通常为 443。
生产环境必须使用 wss://,原因:
- 防止数据被中间人窃听、篡改;
- 避免部分网络环境(如防火墙)拦截
ws://连接;- 与 HTTPS 一致的安全级别,符合浏览器的安全策略。
2) 协议扩展
RFC 6455 支持通过 Sec-WebSocket-Extensions 协商扩展功能,主流扩展:
- permessage-deflate:对整个消息进行压缩,降低传输带宽;
- x-webkit-deflate-frame:早期浏览器的压缩扩展,已被 permessage-deflate 取代。
4 WebSocket 与 HTTP 协议的核心区别
| 特性 | WebSocket | HTTP 1.1 |
|---|---|---|
| 通信方向 | 全双工 | 半双工(客户端请求→服务器响应) |
| 连接类型 | 持久连接 | 短连接(默认)/ 长连接(Connection: keep-alive) |
| 数据开销 | 帧头开销小(几字节) | 每次请求需携带完整 HTTP 头(数百字节) |
| 服务器推送 | 支持主动推送 | 不支持,需依赖轮询 / 长轮询模拟 |
| 协议升级 | 基于 HTTP 升级 | 无升级机制 |
| 状态码 | 关闭帧状态码(1000+) | 响应状态码(200/404/500 等) |
5 总结
WebSocket 协议的核心设计思路是 "借 HTTP 握手,建 TCP 长连,用轻量帧传输",它不是替代 HTTP,而是对 HTTP 实时通信能力的补充。其帧格式的轻量化、全双工通信的高效性,使其成为实时聊天、物联网数据推送、在线游戏等场景的首选协议。