Skip to content

SSE vs WebSocket 对比

Server-Sent Events(SSE)和 WebSocket 都可以实现服务器到客户端的实时数据推送,但它们的设计目标和适用场景有很大差异。本节通过详细的对比表格和场景分析,帮助你在不同需求下选择最合适的技术方案。

前置知识

阅读本节前,建议先了解:EventSource API

基础概念

两种技术定位

技术定位核心能力
SSE服务器单向推送服务器持续向客户端发送数据
WebSocket全双工通信客户端和服务器双向实时通信

详细对比

核心特性对比

特性SSEWebSocket
通信方向服务器 → 客户端(单向)双向(全双工)
协议HTTPWS/WSS(独立协议)
数据格式文本(UTF-8)文本 + 二进制
浏览器 APIEventSourceWebSocket
自动重连内置支持需要手动实现
事件类型支持命名事件需要自行封装
断点续传内置 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.onmessagews.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();

性能对比

指标SSEWebSocket
连接建立快(标准 HTTP)稍慢(需要握手)
每帧开销较大(HTTP 头)极小(仅帧头 2-14 字节)
高频消息不适合(HTTP 帧开销大)适合(帧开销极小)
低频消息适合(自动重连)适合(双向通信)
内存占用稍高
服务器实现简单(任何语言)需要专用库

浏览器兼容性

浏览器SSEWebSocket
Chrome6.0+4.0+
Firefox6.0+4.0+
Safari5.0+5.0+
Edge79+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() {}
  };
}

最佳实践

  1. 单向推送选 SSE:更简单,自动重连,内置事件分发
  2. 双向通信选 WebSocket:功能更强大,支持二进制
  3. 混合使用:通知用 SSE,聊天用 WebSocket
  4. 渐进增强:不支持 SSE 时降级到轮询
  5. 根据频率选择:低频用 SSE,高频用 WebSocket

下一节

继续学习:history.pushState / replaceState

参考链接