SSE vs WebSocket 对比
Server-Sent Events(SSE)和 WebSocket 都可以实现服务器到客户端的实时数据推送,但它们的设计目标和适用场景有很大差异。本节通过详细的对比表格和场景分析,帮助你在不同需求下选择最合适的技术方案。
前置知识
阅读本节前,建议先了解:EventSource API
基础概念
两种技术定位
| 技术 | 定位 | 核心能力 |
|---|---|---|
| SSE | 服务器单向推送 | 服务器持续向客户端发送数据 |
| WebSocket | 全双工通信 | 客户端和服务器双向实时通信 |
详细对比
核心特性对比
| 特性 | SSE | WebSocket |
|---|---|---|
| 通信方向 | 服务器 → 客户端(单向) | 双向(全双工) |
| 协议 | HTTP | WS/WSS(独立协议) |
| 数据格式 | 文本(UTF-8) | 文本 + 二进制 |
| 浏览器 API | EventSource | WebSocket |
| 自动重连 | 内置支持 | 需要手动实现 |
| 事件类型 | 支持命名事件 | 需要自行封装 |
| 断点续传 | 内置 Last-Event-ID | 需要自行实现 |
| HTTP/2 兼容 | 完全兼容 | 不兼容(需升级) |
| 连接开销 | 极低(基于 HTTP) | 需要握手 |
| 跨域 | 标准 CORS | 需要协议升级 |
连接方式对比
SSE 连接流程:
客户端 ──── GET /sse ────→ 服务器
客户端 ←──── HTTP 200 ──── 服务器 (Content-Type: text/event-stream)
客户端 ←──── data: ...\n\n ── 服务器 (持续推送)
客户端 ←──── data: ...\n\n ── 服务器
...
WebSocket 连接流程:
客户端 ──── GET /ws (Upgrade: websocket) ────→ 服务器
客户端 ←──── 101 Switching Protocols ──────── 服务器
客户端 ──── 发送数据 ────→ 服务器
客户端 ←──── 发送数据 ──── 服务器
...API 复杂度对比
| 方面 | SSE (EventSource) | WebSocket |
|---|---|---|
| 创建连接 | new EventSource(url) | new WebSocket(url) |
| 接收消息 | es.onmessage | ws.onmessage |
| 发送消息 | 不支持 | ws.send(data) |
| 自定义事件 | 内置支持 | 需自行封装 |
| 自动重连 | 自动 | 手动实现 |
| 断点续传 | 自动(Last-Event-ID) | 手动实现 |
| 二进制传输 | 不支持 | 支持 |
| 心跳检测 | 不需要 | 需要手动实现 |
| 关闭连接 | es.close() | ws.close() |
代码量对比
javascript
// ===== SSE:接收实时数据(约 10 行) =====
const es = new EventSource('/api/notifications');
es.addEventListener('notification', (e) => {
showNotification(JSON.parse(e.data));
});
es.onerror = () => console.log('重连中...');
// ===== WebSocket:接收实时数据(约 30 行) =====
const ws = new WebSocket('wss://example.com/ws');
let reconnectAttempts = 0;
function connect() {
ws = new WebSocket('wss://example.com/ws');
ws.onopen = () => { reconnectAttempts = 0; startHeartbeat(); };
ws.onmessage = (e) => {
const data = JSON.parse(e.data);
if (data.type === 'notification') showNotification(data);
};
ws.onclose = () => {
stopHeartbeat();
setTimeout(connect, Math.min(1000 * Math.pow(2, reconnectAttempts++), 30000));
};
}
connect();性能对比
| 指标 | SSE | WebSocket |
|---|---|---|
| 连接建立 | 快(标准 HTTP) | 稍慢(需要握手) |
| 每帧开销 | 较大(HTTP 头) | 极小(仅帧头 2-14 字节) |
| 高频消息 | 不适合(HTTP 帧开销大) | 适合(帧开销极小) |
| 低频消息 | 适合(自动重连) | 适合(双向通信) |
| 内存占用 | 低 | 稍高 |
| 服务器实现 | 简单(任何语言) | 需要专用库 |
浏览器兼容性
| 浏览器 | SSE | WebSocket |
|---|---|---|
| Chrome | 6.0+ | 4.0+ |
| Firefox | 6.0+ | 4.0+ |
| Safari | 5.0+ | 5.0+ |
| Edge | 79+ | 12+ |
| IE | 不支持 | 10+ |
| 移动端 | 大部分支持 | 全面支持 |
场景选择指南
适合 SSE 的场景
| 场景 | 原因 |
|---|---|
| 新闻推送 | 只需服务器推送 |
| 股票行情 | 单向数据流 |
| 日志监控 | 持续文本数据 |
| 通知系统 | 不需要客户端发送 |
| 实时计数器 | 低频更新 |
| AI 流式输出 | 流式文本数据 |
| 天气更新 | 定时推送 |
适合 WebSocket 的场景
| 场景 | 原因 |
|---|---|
| 在线聊天 | 需要双向通信 |
| 多人游戏 | 低延迟双向数据 |
| 协同编辑 | 实时双向同步 |
| 屏幕共享 | 二进制数据传输 |
| 视频通话 | 二进制流传输 |
| IoT 控制 | 双向低延迟 |
| 白板/绘图 | 实时双向 |
决策流程图
需要实时通信
│
├── 客户端需要向服务器发送数据?
│ ├── 是 → WebSocket
│ │
│ └── 否
│ │
│ ├── 需要传输二进制数据?
│ │ ├── 是 → WebSocket
│ │ └── 否
│ │
│ ├── 消息频率很高(每秒几十条以上)?
│ │ ├── 是 → WebSocket
│ │ └── 否 → SSE
│ │
│ └── 需要自动重连和断点续传?
│ └── 是 → SSE
│
└── 都可以 → 优先 SSE(更简单)实战示例
技术选型对比表
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>SSE vs WebSocket 对比</title>
<style>
body { font-family: -apple-system, sans-serif; padding: 20px; }
h2 { text-align: center; color: #1a1a2e; }
table {
width: 100%; border-collapse: collapse;
margin: 20px 0; font-size: 14px;
}
th, td { padding: 10px 14px; border: 1px solid #e0e0e0; text-align: center; }
th { background: #1a73e8; color: white; font-weight: 600; }
td:first-child { text-align: left; font-weight: 600; background: #f8f9fa; }
tr:nth-child(even) td:first-child { background: #f0f2f5; }
.winner-sse { background: #e8f5e9 !important; }
.winner-ws { background: #e3f2fd !important; }
.tie { background: #fff8e1 !important; }
.verdict {
margin-top: 20px; padding: 16px;
background: white; border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.verdict h3 { margin-bottom: 12px; }
.verdict ul { padding-left: 20px; }
.verdict li { margin: 6px 0; color: #555; }
</style>
</head>
<body>
<h2>SSE vs WebSocket 完整对比</h2>
<table>
<thead>
<tr>
<th>特性</th>
<th>Server-Sent Events</th>
<th>WebSocket</th>
</tr>
</thead>
<tbody>
<tr>
<td>通信方向</td>
<td class="winner-ws">单向(服务器→客户端)</td>
<td class="winner-sse">双向(全双工)</td>
</tr>
<tr>
<td>实现简单度</td>
<td class="winner-sse">非常简单</td>
<td>较复杂</td>
</tr>
<tr>
<td>自动重连</td>
<td class="winner-sse">内置支持</td>
<td>需要手动实现</td>
</tr>
<tr>
<td>断点续传</td>
<td class="winner-sse">内置支持(Last-Event-ID)</td>
<td>需要手动实现</td>
</tr>
<tr>
<td>事件分发</td>
<td class="winner-sse">原生支持命名事件</td>
<td>需要自行封装</td>
</tr>
<tr>
<td>二进制数据</td>
<td class="winner-ws">不支持</td>
<td class="winner-sse">支持</td>
</tr>
<tr>
<td>每帧开销</td>
<td class="winner-ws">较大(HTTP 头)</td>
<td class="winner-sse">极小(2-14 字节)</td>
</tr>
<tr>
<td>高频消息</td>
<td class="winner-ws">不适合</td>
<td class="winner-sse">适合</td>
</tr>
<tr>
<td>协议兼容性</td>
<td class="winner-sse">HTTP/HTTP2 兼容</td>
<td>独立协议</td>
</tr>
<tr>
<td>浏览器支持</td>
<td class="winner-ws">较新浏览器(IE不支持)</td>
<td class="winner-sse">全面支持(IE10+)</td>
</tr>
<tr>
<td>连接数限制</td>
<td class="winner-ws">~6 个/域名</td>
<td class="winner-sse">无明确限制</td>
</tr>
<tr>
<td>代理友好</td>
<td class="winner-sse">HTTP 兼容,无问题</td>
<td>可能被代理拦截</td>
</tr>
</tbody>
</table>
<div class="verdict">
<h3>选择建议</h3>
<ul>
<li><strong>选 SSE:</strong>只需要服务器推送、新闻通知、实时日志、股票行情、AI 流式输出</li>
<li><strong>选 WebSocket:</strong>聊天、游戏、协同编辑、视频通话、双向控制、高频消息</li>
<li><strong>不确定时:</strong>优先尝试 SSE,不够用时再切换到 WebSocket</li>
</ul>
</div>
</body>
</html>注意事项
可以混合使用
SSE 和 WebSocket 并不互斥,可以在同一个应用中同时使用:
javascript
// SSE 用于接收通知
const notifications = new EventSource('/api/notifications');
// WebSocket 用于实时聊天
const chat = new WebSocket('wss://example.com/chat');
// 各司其职,互不干扰降级策略
javascript
// 优先使用 SSE,不支持时降级到轮询
function createNotificationStream(url) {
if (window.EventSource) {
return new EventSource(url);
}
// 降级:短轮询
return {
addEventListener(event, handler) {
setInterval(() => fetch(url).then(r => r.text()).then(handler), 5000);
},
close() {}
};
}最佳实践
- 单向推送选 SSE:更简单,自动重连,内置事件分发
- 双向通信选 WebSocket:功能更强大,支持二进制
- 混合使用:通知用 SSE,聊天用 WebSocket
- 渐进增强:不支持 SSE 时降级到轮询
- 根据频率选择:低频用 SSE,高频用 WebSocket
下一节