在现代 Web 开发中,实时性已成为衡量用户体验的关键指标。从股票行情的跳动、AI 对话的流式输出,到在线协作编辑,都需要服务器能够"主动"与客户端通信。
为了实现这一目标,开发者们经历了从"笨拙"的轮询到"优雅"的长连接技术的演进。本文将深入剖析 短轮询、长轮询、WebSocket 和 SSE (Server-Sent Events) 的核心区别、适用场景及技术细节。
1. 基础概念解析
🔄 短轮询
这是最原始的实现方式。客户端(浏览器)不管服务器有没有新数据,都每隔固定的时间(如 1秒)向服务器发送一次 HTTP 请求。
- 原理:客户端定时发请求 -> 服务器立即响应(有数据返数据,无数据返空) -> 客户端处理 -> 等待下一次定时。
- 缺点:极其浪费带宽和服务器资源。大部分请求可能是无效的(没有新数据),且实时性受限于轮询间隔。
⏳ 长轮询
这是对短轮询的优化。客户端发送请求后,如果服务器没有新数据,服务器不会立即关闭连接,而是将请求挂起(Hold住) ,直到有数据更新或达到超时时间才返回响应。客户端收到响应后,立即发起下一次请求。
- 原理:客户端发请求 -> 服务器挂起连接(等待数据) -> 有数据了 -> 服务器响应 -> 客户端立即发新请求。
- 优点:减少了无效请求,实时性比短轮询好。
- 缺点:服务器需要维持大量挂起的连接,高并发下对服务器资源(线程/内存)消耗大。
⚡ WebSocket
HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
- 原理 :通过 HTTP 协议发起握手请求,协商升级协议(
Upgrade: websocket)。握手成功后,底层 TCP 连接保持打开,客户端和服务器可以随时互相发送数据。 - 特点:真正的双向通信,低延迟,支持二进制数据。
📢 SSE
Server-Sent Events 是一种允许服务器向浏览器单向 推送实时更新的技术。它基于 HTTP 协议,使用 text/event-stream 格式传输数据。
- 原理 :客户端建立 HTTP 长连接 -> 服务器保持连接打开 -> 服务器有数据时直接推送 -> 客户端通过
EventSourceAPI 接收。 - 特点:基于 HTTP,单向推送(服务端 -> 客户端),原生支持断线重连。
2. 核心区别对比表
为了让你一目了然,我将这四种技术的核心差异整理如下表:
表格
| 特性 | 短轮询 | 长轮询 | WebSocket | SSE |
|---|---|---|---|---|
| 通信方向 | 单向 (客户端请求) | 单向 (客户端请求) | 双向 (全双工) | 单向 (服务端推送) |
| 连接状态 | 频繁建立/断开 | 频繁建立/断开 (长连接挂起) | 持久连接 | 持久连接 |
| 底层协议 | HTTP | HTTP | WebSocket (独立协议) | HTTP |
| 数据类型 | 文本/JSON | 文本/JSON | 文本 & 二进制 | 仅文本 (UTF-8) |
| 断线重连 | 无 (由定时器控制) | 无 (需代码控制) | 需手动实现 | 浏览器原生自动重连 |
| 实现复杂度 | 简单 | 中等 | 复杂 (需处理心跳、状态) | 简单 |
3. 深度解析:WebSocket vs SSE
这是目前面试和选型中最常遇到的"二选一"问题。根据参考资料,两者的核心博弈点如下:
🛠️ 协议与连接
- WebSocket:虽然握手阶段使用 HTTP,但一旦连接建立,它就升级为一个独立的 TCP 协议。它完全脱离了 HTTP 的请求-响应模式。
- SSE :始终运行在 HTTP 协议之上。它利用了 HTTP 的长连接特性(
Connection: keep-alive),服务器通过Content-Type: text/event-stream告诉浏览器"我要开始发流了,别关连接"。
🔄 通信模式
- WebSocket :双向车道。服务器可以发,客户端也可以发。适合聊天室、在线游戏等需要频繁交互的场景。
- SSE :单行道。只能服务器发给客户端。如果客户端想发数据(比如发送聊天内容),必须通过普通的 AJAX/Fetch 请求另起炉灶。
🛡️ 健壮性与重连
- SSE 的杀手锏 :浏览器原生的
EventSourceAPI 内置了自动重连机制 。当网络断开时,浏览器会自动尝试重新连接(通常有几秒的延迟),开发者无需编写复杂的try-catch-reconnect逻辑。 - WebSocket :虽然强大,但原生 API 没有自动重连机制 。一旦连接断开,开发者必须手动监听
onclose事件并编写重连逻辑(包括指数退避算法等),否则连接就彻底断了。
📦 数据格式
- WebSocket :支持发送字符串和
ArrayBuffer(二进制),适合传输图片、音频流或 Protobuf 数据。 - SSE:只能发送 UTF-8 编码的文本数据。如果需要传对象,通常序列化为 JSON 字符串。
4. 选型指南:我该用哪个?
根据参考资料中的建议,结合实际开发经验,建议如下:
✅ 选择 SSE 的场景
如果你的业务场景符合以下特征,SSE 是比 WebSocket 更轻量、更简单的选择:
- 服务器单向推送:如实时通知、股票行情、体育比分、AI 大模型的流式回答(ChatGPT 类应用)。
- 数据量小且为文本:不需要传输二进制文件。
- 追求开发效率:不想处理复杂的连接管理和心跳检测,利用 HTTP 协议穿透防火墙更容易。
- 移动端兼容:在某些移动网络环境下,SSE 的保活比 WebSocket 更稳定。
✅ 选择 WebSocket 的场景
如果你的业务场景符合以下特征,WebSocket 是唯一选择:
- 双向高频交互:如在线聊天、视频会议信令、协同编辑文档。
- 低延迟要求极高:如实时竞技游戏。
- 传输二进制数据:如实时音视频流传输。
✅ 轮询的使用场景
- 短轮询:仅在老旧浏览器不支持 SSE/WebSocket,且实时性要求极低(如几分钟更新一次)时使用。
- 长轮询:作为 WebSocket/SSE 的降级方案(Polyfill)。当网络环境极其恶劣,不支持长连接时,使用长轮询兜底。
5. 代码示例 (基于参考资料)
SSE 实现 (前端)
SSE 的使用非常简单,几行代码即可实现自动重连的实时流:
js
// 前端代码
const eventSource = new EventSource('/api/stream');
// 监听消息
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('收到推送:', data);
};
// 监听自定义事件
eventSource.addEventListener('systemAlert', function(event) {
console.log('系统警报:', event.data);
});
// 错误处理 (浏览器会自动尝试重连)
eventSource.onerror = function(err) {
console.error("SSE 连接出错", err);
};
WebSocket 实现 (前端)
WebSocket 需要手动管理状态和重连:
js
// 前端代码
const ws = new WebSocket('ws://localhost:8080');
ws.onopen = () => {
console.log('连接已建立');
ws.send('Hello Server');
};
ws.onmessage = (event) => {
console.log('收到消息:', event.data);
};
ws.onclose = () => {
console.log('连接已关闭,需要手动实现重连逻辑...');
// reconnect();
};
📌 总结
- 短/长轮询:是 HTTP/1.1 时代的妥协方案,现在多用于兼容降级。
- SSE :是"轻量级的 WebSocket",专为服务端推送设计,原生支持重连,基于 HTTP,开发成本低,是 AI 流式输出和通知系统的首选。
- WebSocket :是"全能型选手",专为双向实时交互设计,功能最强,但实现和维护成本相对较高。
参考资料及文章:
聊聊四种实时通信技术:长轮询、短轮询、WebSocket 和 SSE
前端如何理解SSE(Server-Sent Events)和WebSocket
放弃 Websocket 使用 SSE 才发现这些功能两三行代码就搞定了