WebSocket协议是一种基于TCP的、全双工的、持久的网络通信协议,旨在在单个 TCP 连接上提供客户端(通常是 Web 浏览器)和服务器之间的双向、低延迟、实时通信通道。它解决了传统 HTTP 轮询或长轮询在实现实时应用(如聊天、在线游戏)时,效率低下和延迟高的问题。
一、轮询与长轮询
轮询和长轮询是早期实现“伪实时”通信的技术,用于在 HTTP 协议(本身是请求-响应模型)上模拟服务器向客户端的推送。它们本质上是客户端主动、反复询问服务器是否有新数据。
1.轮询
原理:客户端定期(例如每隔 1 秒、5 秒、30 秒)向服务器发送一个标准的 HTTP 请求,询问是否有新数据。无论服务器是否有新数据,都会立即返回一个响应。
- 优点:
- 实现非常简单。
- 概念清晰易懂。
- 缺点:
- 高延迟: 新数据产生后,客户端最多需要等待一个完整的轮询间隔才能收到(平均延迟 = 间隔时间 / 2)。实时性差。
- 高网络开销: 大量请求是无效的(服务器无数据时也要响应),浪费带宽和服务器资源(处理请求、建立/关闭连接)。
- 高服务器负载: 即使没有更新,服务器也要不断处理大量“空查询”请求。
- 电池消耗 (移动端): 频繁的网络请求和唤醒设备会显著消耗电池。
2.长轮询
原理:客户端发起一个请求,但服务器不会立即响应。服务器将这个请求“挂起”,直到:(1) 有新数据可用,或者;(2)达到一个预设的超时时间。此时服务器才返回响应。客户端收到响应后(无论是否有新数据),立即发起下一个长轮询请求。
- 优点 (相比基础轮询):
- 显著降低延迟: 一旦服务器有新数据,就能立即推送给等待中的客户端,无需等待下一个轮询间隔。平均延迟更低,接近实时。
- 减少无效请求: 服务器只在有数据或超时时才响应,减少了大量“空转”的请求和响应。
- 降低网络开销和服务器负载 (相比基础轮询): 无效请求/响应的数量大幅减少。
- 缺点:
- 实现更复杂: 服务器需要维护挂起的请求(连接),涉及连接管理和状态跟踪。对服务器框架有要求(需要支持异步、非阻塞 I/O 或类似机制)。
- 连接管理开销: 每次长轮询结束(无论是否有数据),都需要关闭当前 HTTP 连接,客户端立即建立一个新的连接。频繁的连接建立/关闭仍有开销(TCP 握手、TLS 握手)。
- 超时问题: 需要合理设置服务器端的超时时间。太短会导致过多“超时空响应”和频繁重建连接;太长可能导致客户端连接因代理、防火墙或浏览器限制而意外中断。
- 服务器资源占用: 挂起的连接仍然消耗服务器资源(内存、文件描述符)。高并发时,大量挂起的连接可能导致服务器资源耗尽。
- 消息顺序保证: 在客户端处理上一个响应并发出下一个请求的短暂间隙,如果服务器有新数据到达,可能会丢失或延迟,需要额外机制保证。
- 不是真正的推送: 仍然是客户端发起的请求,服务器只是“延迟”了响应。
二、WebSocket 协议特点
- 双向通信:客户端和服务端可以随时独立地向对方发送数据,无需先请求。
- 低延迟:建立连接后,数据帧直接在 TCP 层传输,避免了 HTTP 请求/响应的开销。
- 低开销:数据帧头非常精简(最小仅 2 字节),显著减少了带宽消耗。
- 基于 TCP:提供可靠、有序的字节流传输。
- 与 HTTP 协同工作:握手阶段兼容 HTTP,便于通过防火墙和代理,并共享默认端口(ws:80、wss:443)。
- 支持扩展和子协议:允许定义扩展(如压缩)和子协议(如STOMP、WAMP)以满足特定应用需求。
三、工作流程
1.握手
- 客户端发起一个特殊的 HTTP Upgrade 请求。
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
- 请求头包含:
- Connection:Upgrade
- Upgrade:websocket
- Sec-WebSocket-Key:一个随机的 Base64编码值(由浏览器生成)。
- Sec-WebSocket-Version:协议版本(通常是13)。
- Sec-WebSocket-Protocol:(可选)客户端希望使用的子协议列表。
- Origin:(浏览器通常发送)用于同源策略检查。
- 服务器响应一个 HTTP 101 Switching Protocols 响应。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
- 响应头包含:
- Connection:Upgrade
- Upgrade:websocket
- Sec-WebSocket-Accept:一个基于客户端发送的 Sec-WebSocket-Key 计算出的值(证明服务器理解 WebSocket 协议)。
- Sec-WebSocket-Protocol:(可选)服务器从客户端列表中选择的一个子协议。
2.数据传输
- 握手成功后,TCP 连接保持打开状态,但通信协议从 HTTP 升级为 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 ... |
+---------------------------------------------------------------+
- 帧是传输的基本单位,具有特定的二进制格式:
- FIN(1 bit):指示是否是消息的最后一帧。
- RSV1,RSV2,RSV3(各 1 bit):保留位,用于扩展。
- Opcode(4 bits):定义帧的类型(文本、二进制、连接关闭、ping、pong等)。
- Mask(1 bit):指示 Payload Data 是否被掩码(客户端到服务器的帧必须掩码,服务器到客户端的帧不掩码)。
- Payload Length(7,7+16,7+64 bits):有效载荷数据长度。
- Masking-Key(0 or 4 bytes):如果 Mask 为 1,则存在 4 字节的掩码秘钥。
- Payload Data:实际传输的数据(文本或二进制)。如果 Mask 为 1,该数据需与 Masking-Key 进行异或运算以解除掩码。
- 常见的帧类型(Opcode):
- 0x0:延续帧
- 0x1:文本帧-UTF-8 编码
- 0x2:二进制
- 0x8:连续关闭帧
- 0x9:Ping 帧
- 0xA:Pong 帧
3.连接关闭
- 任何一端都可以发送一个关闭帧来发起关闭。
- 关闭帧可以包含一个状态码(如 1000 表示正常关闭)和可选的关闭原因。
- 收到关闭帧的一端必须发送一个关闭帧作为响应。
- 发送关闭帧后,该端点不再发送任何数据。
- 收到对方的关闭帧后,端点认为连接已关闭,并关闭底层的 TCP 连接。
- Ping/Pong 机制:
- 服务器或客户端可以发送 Ping 帧。
- 接收方收到后必须尽快回复一个 Pong 帧(通常携带与 Ping 帧相同的 ApplicationData)。
- 用途:保持连接活跃、检测连接是否仍然有效(心跳)、测量延迟。
四、URL方案
- ws://:非加密 WebSocket 连接(对应 HTTP)。
- wss://:加密 WebSocket 连接(对应 HTTPS)。
五、优点
- 真正的实时性:服务端可以主动推送数据给客户端,无需等待客户端请求。
- 高效:极低的协议开销,节省带宽和服务器资源。
- 减少延迟:避免了 HTTP 请求的往返时间。
- 复用连接:一个连接处理所有通信。
六、与 HTTP 的关键区别
特性 | HTTP (Request/Response) | WebSocket |
---|---|---|
通信模式 | 半双工 (客户端发起请求) | 全双工 (双方可随时发送) |
连接状态 | 无状态 (通常每次请求后关闭) | 持久化连接 |
数据流 | 单向 (请求 -> 响应) | 双向 (消息流) |
延迟 | 较高 (每次交互需握手/头开销) | 极低 (连接后直接传输帧) |
开销 | 较高 (每次请求携带完整头) | 极低 (精简帧头) |
发起方 | 只能是客户端 | 双方均可主动发起数据 |
用途 | 文档、资源获取 | 实时交互应用 |