WebSocket 协议概述
WebSocket 是 HTML5 提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议,基于 RFC 6455 标准。
核心工作机制
WebSocket 基于 TCP 传输协议,并复用 HTTP 的握手通道。浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。
WebSocket 的出现解决了半双工通信的弊端,其最大特点是:服务器可以向客户端主动推送消息,客户端也可以主动向服务器推送消息。
WebSocket 原理
客户端向 WebSocket 服务器通知一个带有所有接收者 ID 的事件,服务器接收后立即通知所有活跃的客户端,只有 ID 在接收者 ID 序列中的客户端才会处理这个事件。
WebSocket 核心特性
- 支持双向通信,实时性更强:客户端和服务器可以同时发送和接收数据。
- 可以发送文本,也可以发送二进制数据:支持多种数据类型传输。
- 建立在 TCP 协议之上,服务端的实现比较容易:基于成熟的 TCP 协议栈。
- 数据格式比较轻量,性能开销小,通信高效:最小头部仅 2 字节,传输效率高。
- 没有同源限制,客户端可以与任意服务器通信:跨域通信能力(需服务器允许)。
- 协议标识符是 ws(如果加密,则为 wss),服务器网址就是 URL:统一的 URL 标识方案。
- 与 HTTP 协议有着良好的兼容性:默认端口也是 80/443,握手阶段使用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
WebSocket 握手过程
客户端请求
WebSocket 握手基于 HTTP 协议升级机制,客户端发送升级请求:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Version: 13
服务器响应
服务器验证请求后返回协议切换响应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Accept 的生成算法为:base64(sha1(Sec-WebSocket-Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
WebSocket 数据帧格式
WebSocket 协议的核心是轻量级的数据帧格式:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-------+-+-------------+
|F|R|R|R| opcode|M| Payload Len |
|I|S|S|S| (4) |A| (7) |
|N|V|V|V| |S| |
| |1|2|3| |K| |
+-+-+-+-+-------+-+-------------+
关键字段说明
- FIN(1bit):标识是否为消息的最后一帧
- Opcode(4bit):帧类型标识
- 0x0:延续帧(分片消息的中间帧)
- 0x1:文本帧(UTF-8编码)
- 0x2:二进制帧
- 0x8:关闭连接帧
- 0x9:Ping帧(心跳检测)
- 0xA:Pong帧(心跳响应)
- Mask(1bit):是否使用掩码(客户端到服务器必须为1)
- Payload Len:数据负载长度
WebSocket 心跳机制
为了保持连接活跃,WebSocket 协议定义了 Ping/Pong 帧用于心跳检测:
// 服务器定时发送心跳
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.ping('heartbeat');
}
}, 25000);
// 客户端自动回复
ws.on('pong', (data) => {
console.log('连接状态正常');
});
推荐的心跳间隔为 25-30 秒,避免与常见的网络超时设置冲突。
WebSocket 客户端实现
基本使用
// 创建WebSocket连接
const ws = new WebSocket('wss://api.example.com/chat');
// 连接建立事件
ws.onopen = (event) => {
console.log('WebSocket连接已建立');
ws.send(JSON.stringify({type: 'join', room: 'general'}));
};
// 消息接收事件
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('收到消息:', data);
};
// 错误处理
ws.onerror = (error) => {
console.error('连接错误:', error);
};
// 连接关闭事件
ws.onclose = (event) => {
console.log(`连接关闭: ${event.code} - ${event.reason}`);
};
连接状态管理
WebSocket 连接有4种状态:
- CONNECTING(0):连接正在建立
- OPEN(1):连接已建立,可以通信
- CLOSING(2):连接正在关闭
- CLOSED(3):连接已关闭
即时通讯技术对比分析
短轮询(Short Polling)
基本思路:浏览器每隔一段时间向服务器发送 HTTP 请求,服务器在收到请求后,不论是否有数据更新,都直接进行响应。
工作机制:这种方式实现的即时通信,本质上还是浏览器发送请求、服务器接受请求的一个过程,通过让客户端不断地进行请求,使得客户端能够模拟实时地收到服务器端的数据的变化。
优点:实现简单,易于理解,兼容性最好。
缺点:由于需要不断的建立 HTTP 连接,严重浪费了客户端和服务器端的资源。当用户增加时,服务器端的压力就会变大。
长轮询(Long Polling)
基本思路:首先由客户端向服务器发起请求,当服务器收到客户端发来的请求后不会直接进行响应,而是先将这个请求挂起,然后判断数据是否有更新。如果有更新,则进行响应;如果一直没有数据,则到达一定的时间限制才返回。客户端 JavaScript 响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。
优点:长轮询和短轮询比起来,明显减少了很多不必要的 HTTP 请求次数,相比之下节约了资源。
缺点:连接挂起也会导致资源的浪费。
服务器发送事件(SSE)
基本思路:服务器使用流信息向客户端推送信息。严格地说,HTTP 协议无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息。也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。SSE 就是利用这种机制,使用流信息向客户端推送信息。
优点:SSE 相对于前面两种方式来说,不需要建立过多的 HTTP 请求,相比之下节约了资源。
限制:SSE 基于 HTTP 协议,目前除了 IE/Edge,其他浏览器都支持。SSE 是单向通信的,只能由服务器向客户端推送信息,如果客户端需要发送信息就是属于下一个 HTTP 请求了。
WebSocket
基本思路:WebSocket 是 HTML5 定义的一个新协议,与传统的 HTTP 协议不同,该协议允许由服务器主动向客户端推送信息。WebSocket 是一个全双工的协议,也就是通信双方是平等的,可以相互发送消息。
优点:真正的实时双向通信,延迟最低,性能最优。
缺点:在服务器端的配置比较复杂,兼容性相对较差。
四种通信协议深度对比
协议基础对比
| 对比维度 | 短轮询 | 长轮询 | SSE | WebSocket |
|---|---|---|---|---|
| 协议基础 | HTTP | HTTP | HTTP | TCP |
| 通信方式 | 客户端主动查询 | 服务器挂起响应 | 服务器单向推送 | 全双工双向 |
| 连接建立 | 每次请求新建 | 每次请求新建 | 一次连接持久 | 一次握手持久 |
| 数据流向 | 双向(请求-响应) | 双向(请求-响应) | 单向(服务器→客户端) | 双向同时 |
性能对比分析
| 性能指标 | 短轮询 | 长轮询 | SSE | WebSocket |
|---|---|---|---|---|
| 实时性 | 低(取决于轮询间隔) | 中(服务器有数据立即响应) | 高(服务器主动推送) | 最高(真正实时) |
| 带宽效率 | 低(大量无效请求) | 中(减少无效请求) | 高(流式传输) | 最高(轻量级帧) |
| 服务器负载 | 高(频繁建立连接) | 中(连接挂起消耗) | 低(持久连接) | 最低(一次连接) |
| 客户端资源 | 高(频繁请求) | 中(连接管理复杂) | 低(自动处理) | 最低(事件驱动) |
兼容性与适用场景
| 方案 | 浏览器兼容性 | 适用场景 | 不适用场景 |
|---|---|---|---|
| 短轮询 | 完美兼容所有浏览器 | 兼容性要求极高、数据更新频率低 | 高频实时通信 |
| 长轮询 | 很好兼容 | 中等实时性要求、无法使用 WebSocket | 需要真正实时双向通信 |
| SSE | 良好(除IE/Edge) | 服务器向客户端单向推送、新闻推送、监控 | 需要客户端主动发送数据 |
| WebSocket | 良好(现代浏览器) | 聊天、游戏、实时交易、高频数据交换 | 兼容性要求极高、服务器不支持 |
选择依据总结
选择 WebSocket,当:
- 需要真正的实时双向通信
- 数据交换频率高(每秒多次)
- 对延迟敏感的应用
- 需要传输二进制数据
选择其他方案,当:
- 兼容性要求极高 → 短轮询
- 服务器环境不支持 WebSocket → 长轮询
- 只需要服务器向客户端的单向推送 → SSE
技术选型指南
从性能的角度来看:WebSocket > 长连接(SSE) > 长轮询 > 短轮询
如果考虑浏览器的兼容性问题:短轮询 > 长轮询 > 长连接(SSE) > WebSocket
要根据具体的使用场景来判断使用哪种方式:
- 对于实时性要求极高的场景(如在线游戏、金融交易),优先选择 WebSocket
- 对于只需要服务器推送的场景(如新闻推送、监控),SSE 是更好的选择
- 在兼容性要求极高的环境中,短轮询或长轮询是必要的备选方案
总结
WebSocket 协议通过一次握手建立持久连接,实现了真正的全双工实时通信。其轻量级的数据帧格式和内置的心跳机制,使其在高频数据交换场景中具有显著优势。
在实际应用中,需要根据具体的业务需求、性能要求和兼容性约束选择最合适的实时通信方案。WebSocket 特别适合需要低延迟、高频双向通信的场景,而其他方案在特定场景下也有各自的优势。
通过深入理解各种通信协议的特点和适用场景,可以做出更合理的技术选型决策,为用户提供优质的实时交互体验。