惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
WordPress大学
WordPress大学
Google DeepMind News
Google DeepMind News
T
The Exploit Database - CXSecurity.com
阮一峰的网络日志
阮一峰的网络日志
F
Fox-IT International blog
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
I
Intezer
P
Privacy & Cybersecurity Law Blog
B
Blog RSS Feed
Latest news
Latest news
小众软件
小众软件
A
Arctic Wolf
Attack and Defense Labs
Attack and Defense Labs
L
LINUX DO - 热门话题
博客园 - 聂微东
B
Blog
T
Troy Hunt's Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
Malwarebytes
Malwarebytes
爱范儿
爱范儿
Recorded Future
Recorded Future
Apple Machine Learning Research
Apple Machine Learning Research
人人都是产品经理
人人都是产品经理
D
Docker
T
Threat Research - Cisco Blogs
MyScale Blog
MyScale Blog
Martin Fowler
Martin Fowler
E
Exploit-DB.com RSS Feed
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
PCI Perspectives
PCI Perspectives
Scott Helme
Scott Helme
N
Netflix TechBlog - Medium
博客园 - 三生石上(FineUI控件)
T
True Tiger Recordings
C
Check Point Blog
Microsoft Azure Blog
Microsoft Azure Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
K
Kaspersky official blog
Security Latest
Security Latest
The Hacker News
The Hacker News
Microsoft Security Blog
Microsoft Security Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
Stack Overflow Blog
Stack Overflow Blog
S
Security @ Cisco Blogs
C
CXSECURITY Database RSS Feed - CXSecurity.com
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
M
Microsoft Research Blog - Microsoft Research

掘金

从“连接不上”到“交易成功”:我用 @solana/web3.js 在 React 中搞定 Solana 钱包交互的全过程 juejin.cn 海量人群包存储优化:基于 RoaringBitmap 交换格式与 Redis 分片 Bitmap 的实践 juejin.cn juejin.cn 鸿蒙项目首页启动链路与 ArkUI 架构学习总结 如何手写一个 AI Agent 工具调用循环(Tool Loop) Tauri 应用首次上架 App Store 被驳回了 3 次(iOS)和 12 轮(macOS)的经历 juejin.cn Flutter 桌面小组件开发 现代多模态大模型的核心基础:Unified Self-Attention juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn Transformer 原论文怎么训出来的:8 张 P100、12 小时、warmup 4000 步 Hermes 升级后,我的 Telegram 附件突然发不出来了 Transformer 中的前馈网络:那个看似平平无奇的两层 MLP,其实是「记忆」所在 AI Coding开始进入第四个时代,我还没上车呢! 【Agentic RL / 强化学习 / OPD】OpenClaw-RL 源码阅读笔记 --- (1)---基础 juejin.cn Vibe Coding 全栈实战:章鱼哥解题 01|搭好产品底座与登录链路 juejin.cn 我让 AI 加了一个开关,结果代码走了原本不该走的分支 Manim物理模拟:别自己写欧拉了! 我也该升级了,陪伴了我7年的博客 juejin.cn juejin.cn MCP 高德地图实战:当 AI 学会使用工具,一个协议如何重塑大模型的行动边界 用魔法打败魔法:我让AI替我去面试前端岗,AI面试官给我打了92分,还发了offer juejin.cn juejin.cn juejin.cn juejin.cn Android Input Spy Window Claude Code CLI 命令大全:60 个原生命令一次讲清 juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn 关于一个新手小白靠claude帮助下的全栈留言板项目开发 juejin.cn juejin.cn juejin.cn 从本地开发到生产部署:用 Docker Compose 跑通 NestJS、MySQL 与 Milvus AI应用开发七:可以替代 RAG 的技术 juejin.cn 小书匠:一款本地优先、去中心化的全能笔记软件 juejin.cn juejin.cn juejin.cn Shadow实战接入与生产落地:从零搭建到稳定运行 Shadow Transform:编译期的魔法——字节码替换实战 juejin.cn juejin.cn Hermes Agent:一个真正“会成长”的开源 AI Agent,正在改变 AI 自动化玩法 juejin.cn juejin.cn juejin.cn 残差连接:为什么深层网络必须留一条直路 juejin.cn FastAPI 从入门到实战:3 分钟构建高性能异步 API juejin.cn juejin.cn CryptoJS:数据安全的JavaScript加密利器 juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn ArkClaw AI 盯盘管家 —— 从手动口令到自动推送,4 套预置定时任务模版一键启用 juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn “杀!杀!杀!”、“我最讨厌事后道歉”——骂“杀哥”之前,谁还没当过情绪崩溃的人
WebSocket 比 SSE 复杂在哪里
卷帘依旧 · 2026-05-18 · via 掘金

WebSocket 比 SSE 复杂在哪里

这是一个很好的问题。WebSocket 和 SSE 都能实现服务器推送,但 WebSocket 的复杂度显著更高。下面从多个维度拆解这些复杂性。


一、复杂度总览对比

维度SSEWebSocket复杂度差
协议层级HTTP 之上独立协议(ws/wss)🔴 WebSocket 需处理协议升级
消息格式纯文本格式帧结构(二进制)🔴 WebSocket 需编解码
双向通信❌ 不支持✅ 原生支持🟡 复杂性由需求决定
自动重连✅ 内置❌ 需手动实现🔴 WebSocket 完全自主实现
连接状态EventSource 自动管理手动管理 + 心跳🔴 WebSocket 需大量状态管理
负载均衡简单(HTTP 兼容)复杂(需支持 ws 协议)🔴 WebSocket 运维复杂
调试难度简单(浏览器 Network)复杂(需专用工具)🟡 WebSocket 调试不便

二、协议层面的复杂性

2.1 SSE:简单的 HTTP 响应

SSE 就是一个普通的 HTTP 响应,只是 Content-Type 特殊一点:

// 服务端
res.writeHead(200, {
    'Content-Type': 'text/event-stream',  // 就多了这一行
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
});

任何 HTTP 服务器都能直接支持,不需要额外配置。

2.2 WebSocket:独立的协议

WebSocket 需要从 HTTP 升级到 ws 协议

客户端请求:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket        ← 要求升级协议
Connection: Upgrade
Sec-WebSocket-Key: x3JJ...
Sec-WebSocket-Version: 13

服务端响应:
HTTP/1.1 101 Switching Protocols  ← 返回 101,不是 200
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc...

复杂性体现:

  • 需要处理协议升级逻辑
  • 普通 HTTP 服务器无法直接支持(如 Express 需要 ws 库)
  • 需要独立的端口或路径配置
  • 代理服务器必须支持 WebSocket 协议

三、消息格式的复杂性

3.1 SSE:简单的文本格式

// 发送消息就这么简单
res.write('data: hello\n\n');

// 发送 JSON
res.write('data: {"message": "hello"}\n\n');

3.2 WebSocket:复杂的帧结构

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| opcode|M| Payload len |    Extended payload length    |
|I|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|       |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 位(标记消息结束)
  • opcode(文本/二进制/关闭/Ping/Pong)
  • Masking(客户端到服务端需要掩码)
  • Payload length(短/长/超长)

四、连接管理的复杂性

4.1 SSE:内置自动重连

// EventSource 自带重连机制
const es = new EventSource('/stream');

// 断线后自动重连,无需写任何代码
// 服务器可以通过 retry 字段控制重连间隔
res.write('retry: 5000\n\n');  // 告诉客户端 5 秒后重试

4.2 WebSocket:完全手动实现

class RobustWebSocket {
    constructor(url) {
        this.url = url;
        this.ws = null;
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 10;
        this.reconnectDelay = 1000;
        this.isClosed = false;
        
        this.connect();
    }
    
    connect() {
        this.ws = new WebSocket(this.url);
        
        this.ws.onopen = () => {
            console.log('连接成功');
            this.reconnectAttempts = 0;
            // 启动心跳检测
            this.startHeartbeat();
        };
        
        this.ws.onclose = () => {
            console.log('连接断开');
            this.handleDisconnect();
        };
        
        this.ws.onerror = (err) => {
            console.error('错误', err);
            // WebSocket 出错后会自动关闭,触发 onclose
        };
        
        this.ws.onmessage = (event) => {
            this.handleMessage(event);
        };
    }
    
    handleDisconnect() {
        if (this.isClosed) return;
        
        if (this.reconnectAttempts < this.maxReconnectAttempts) {
            this.reconnectAttempts++;
            const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts);
            
            setTimeout(() => {
                console.log(`第 ${this.reconnectAttempts} 次重连`);
                this.connect();
            }, delay);
        } else {
            console.error('超过最大重连次数');
        }
    }
    
    startHeartbeat() {
        // 需要自己实现心跳
        this.heartbeatInterval = setInterval(() => {
            if (this.ws.readyState === WebSocket.OPEN) {
                // 发送 ping 帧或自定义心跳消息
                this.ws.send(JSON.stringify({ type: 'ping' }));
            }
        }, 30000);
    }
    
    send(data) {
        if (this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(data);
        } else {
            // 需要实现消息队列,等待重连后发送
            this.pendingMessages.push(data);
        }
    }
    
    close() {
        this.isClosed = true;
        clearInterval(this.heartbeatInterval);
        this.ws.close();
    }
}

对比:SSE 只需要 3 行代码就能实现自动重连,WebSocket 需要 100+ 行。


五、运维与部署的复杂性

5.1 SSE:和普通 HTTP 完全一样

# Nginx 配置 SSE - 就这么简单
location /sse/ {
    proxy_pass http://backend;
    proxy_buffering off;           # 必须关闭缓冲
    proxy_cache off;               # 关闭缓存
    proxy_set_header Connection '';
    chunked_transfer_encoding off;
}

5.2 WebSocket:需要特殊配置

# Nginx 配置 WebSocket - 需要额外处理
location /ws/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;      # 支持协议升级
    proxy_set_header Connection "upgrade";       # 保持连接
    proxy_set_header Host $host;
    proxy_read_timeout 60s;                      # 需要更长的超时
}

负载均衡的复杂性:

  • WebSocket 需要会话保持(同一客户端始终连到同一后端)
  • 传统轮询负载均衡会断开 WebSocket
  • 需要配置 ip_hash 或使用支持 WebSocket 的 LB

六、调试的复杂性

6.1 SSE:浏览器 Network 面板直接看

在 Chrome DevTools Network 标签中,SSE 请求显示为正常的 HTTP 请求,可以:

  • 实时看到每个 data: 消息
  • 检查响应头
  • 查看请求参数

所见即所得,和调试普通 API 一样简单。

6.2 WebSocket:需要专用工具

Chrome DevTools 虽然有 WebSocket 面板,但功能有限:

  • 消息不按时间线排列
  • 难以过滤特定消息
  • 无法查看二进制消息内容

常用的调试方法:

# 使用 wscat 命令行工具
wscat -c ws://localhost:3000

# 使用第三方工具如 Postman、WebSocket King

# 写日志中间件
ws.on('message', (data) => {
    console.log('收到:', data.toString());
});

七、实际代码量对比

场景:实现一个实时聊天室(仅服务器推送消息)

SSE 实现:

// 服务端 - 约 30 行
const clients = [];

app.get('/sse/chat', (req, res) => {
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache'
    });
    
    clients.push(res);
    
    req.on('close', () => {
        clients.splice(clients.indexOf(res), 1);
    });
});

function broadcast(message) {
    clients.forEach(client => {
        client.write(`data: ${JSON.stringify(message)}\n\n`);
    });
}

// 前端 - 约 10 行
const es = new EventSource('/sse/chat');
es.onmessage = (e) => {
    const msg = JSON.parse(e.data);
    appendMessage(msg);
};

WebSocket 实现:

// 服务端 - 约 60 行
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

const clients = new Set();

wss.on('connection', (ws) => {
    clients.add(ws);
    
    // 心跳检测
    ws.isAlive = true;
    ws.on('pong', () => { ws.isAlive = true; });
    
    ws.on('message', (data) => {
        // 解析消息、处理各种类型...
        broadcast(data);
    });
    
    ws.on('close', () => {
        clients.delete(ws);
    });
});

// 心跳检测定时器
setInterval(() => {
    clients.forEach((ws) => {
        if (ws.isAlive === false) {
            return ws.terminate();
        }
        ws.isAlive = false;
        ws.ping();
    });
}, 30000);

// 前端 - 约 40 行
const ws = new WebSocket('ws://localhost:8080');
let reconnectAttempts = 0;

ws.onopen = () => {
    console.log('connected');
    reconnectAttempts = 0;
};

ws.onclose = () => {
    // 手动重连逻辑
    setTimeout(() => {
        new WebSocket(...);
    }, 1000 * Math.pow(2, reconnectAttempts));
};

ws.onmessage = (e) => {
    const msg = JSON.parse(e.data);
    appendMessage(msg);
};

代码量:SSE 约 40 行 vs WebSocket 约 150 行(不含重连逻辑)。


八、什么时候必须接受 WebSocket 的复杂性?

场景SSE 是否够用说明
AI 流式输出✅ 完美单向推送,SSE 最佳选择
实时通知/提醒✅ 完美新消息、新邮件等
股票行情✅ 完美只需要服务器推送价格
日志流✅ 完美服务器推送日志行
协同编辑❌ 需要 WebSocket需要双向实时同步
在线游戏❌ 需要 WebSocket低延迟双向通信
视频/语音通话❌ 需要 WebSocket需要信令服务器
实时白板❌ 需要 WebSocket双方都需要发送

九、总结

graph TD
    subgraph "SSE 简单在哪"
        A1[HTTP 协议,无需升级]
        A2[文本格式,直接 write]
        A3[自动重连,EventSource 内置]
        A4[负载均衡无特殊要求]
        A5[Network 面板直接调试]
    end
    
    subgraph "WebSocket 复杂在哪"
        B1[独立协议,需 101 升级]
        B2[二进制帧,需编解码]
        B3[手动实现重连 + 心跳]
        B4[负载均衡需会话保持]
        B5[调试需要专用工具]
    end
    
    C[如果只需要服务器推送] --> A1
    D[如果需要双向实时通信] --> B1
维度SSEWebSocket
学习成本10 分钟2-3 天
代码量多 3-5 倍
调试难度极低中等
运维复杂度同普通 HTTP需特殊配置
自动重连内置手写 50+ 行
心跳检测可选(retry 字段)必须手动实现
浏览器兼容同 WebSocket同 SSE

一句话总结:

WebSocket 比 SSE 复杂的地方在于——它是一个完全独立的协议,需要处理协议升级、二进制帧编码、手动重连、心跳检测、会话保持等一系列 SSE 已经帮你封装好的事情。如果你的需求只是服务器单向推送(特别是 AI 流式输出),用 SSE;如果必须双向实时通信,再接受 WebSocket 的复杂性。