ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

WebSocket 和 SSE 有什么区别?从实时通信到 AI 流式输出

WebSocket 和 SSE 有什么区别?从实时通信到 AI 流式输出 HTTP的基本模型是一问一答客户端发一个请求服务器回一个响应然后这次交互就结束了。用户不点、不刷新服务器就没有机会主动开口。但有一类需求天生就是反过来的——数据在服务器那边不断产生要主动推给客户端聊天消息、股票行情、系统通知、以及现在的 AI 一个字一个字往外吐。HTTP 这套你不问我不说的模型顶不住于是就有了WebSocket和SSE两条路。这两样东西经常被摆在一起比较因为它们解决的是同一类问题但走的是完全不同的方向。这篇文章先把 HTTP 为什么不够说清楚再分别讲这两条路最后落到 AI 流式输出为什么选 SSE。HTTP 为什么不够用请求-响应是本位HTTP 每次交互都是客户端发起、服务器响应。哪怕开了keep-alive复用 TCP 连接语义上还是一问一答服务器不能凭空插一句。要做服务器主动推我们能想到的最直觉的土办法是轮询( polling )客户端每隔两秒问一次有没有新消息。客户端 → 服务器有新消息吗 没有 客户端 → 服务器有新消息吗 没有 客户端 → 服务器有新消息吗 还是没有 客户端 → 服务器有新消息吗 有一条问题很明显消息什么时候来是随机的但你的提问间隔是固定的。来得早你就等在下一个轮询周期来得晚前几次问都是白问。间隔调小浪费请求调大延迟又高怎么都别扭。我们再看改进版长轮询( long polling )服务器收到请求后先不回应一直挂着等真有消息了才把响应返回客户端收到后立刻再发下一个请求。这能压低延迟但每次消息都要重新建一次请求而且服务器要一直挂着大量连接。这两条路都是在请求-响应这个框子里想办法而问题恰恰是这个框子本身。真正的解法是把连接的性质改掉——SSE 和 WebSocket 就是两种改法。SSE它其实就是一个不结束的 HTTP 响应SSE( Server-Sent Events ) 的做法非常朴素客户端发起一个普通 HTTP 请求服务器不回完就这么一直挂着隔一会儿往响应体里写一段数据。响应头里声明类型GET /stream HTTP/1.1 Accept: text/event-stream HTTP/1.1 200 OK Content-Type: text/event-stream Cache-Control: no-cache然后响应体不是一次性的一个 HTML而是一段一段往下流的文本每段格式固定我们看一段例子event: message id: 42 data: {text: 你} data: {text: 好} data: {text: 呀}规则是每个事件由若干行组成行是字段: 值一个空行代表这个事件结束。常用的几个字段字段作用data要发送的内容可以出现多行客户端会拼接event事件名前端可以按名字分别监听id事件编号重连时用来续传retry建议客户端重连的间隔毫秒浏览器端用内置的EventSource就能消费不需要额外库constesnewEventSource(/stream);es.onmessage(e){appendToPage(JSON.parse(e.data));};自带的两个好处SSE 最省心的地方是断线重连是浏览器自动做的。连接断了EventSource会自己按retry的间隔重连而且重连时会带上Last-Event-ID请求头把上次收到的最后一个id报给服务器服务器可以据此从断点继续发。另一个好处是它就是普通 HTTP。不用改协议、不用配特殊端口普通的负载均衡、CDN、反向代理、鉴权中间件都能直接套上去防火墙也不会拦。限制只能单向。数据只会从服务器流向客户端客户端没法用这条连接往回说话。要发东西得另开一个普通 HTTP 请求。只能是文本。传输内容必须是 UTF-8 文本二进制数据要先 base64 编码塞进去会变大一截。HTTP/1.1 下有连接数上限。浏览器对同一个域名的并发连接默认限制在 6 个左右开满之后第七条就排队了。所以一个页面开太多 SSE 会互相卡住。换到 HTTP/2 就没这个问题——多路复用下连接上限高得多。WebSocket先借 HTTP 握个手再把协议换掉WebSocket 也从一个 HTTP 请求开始但这个请求是来谈判的GET /chat HTTP/1.1 Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbKxOo服务器如果同意会回一个101 Switching Protocols。这一行返回之后这条 TCP 连接上跑的就不再是 HTTP 了换成了 WebSocket 自己的帧协议。之后的通信都是一个个帧双向的。地址用ws://和wss://后者就是加了 TLS 的版本和 HTTPS 的关系一模一样可以回顾一下 HTTPS 和 TLS 那篇。全双工握手完成后这条连接是全双工的客户端和服务器可以随时往对方发数据不用等谁先问。constwsnewWebSocket(wss://example.com/chat);ws.onmessage(e){appendToPage(JSON.parse(e.data));};ws.send(JSON.stringify({type:msg,text:hello}));// 随时往回发帧既支持文本也支持二进制不用像 SSE 那样为二进制做编码。协议里还定义了ping/pong帧用来探测连接是否还活着。代价是重连要自己写。WebSocket 没有内置自动重连断线之后得自己实现重连还要处理重连期间的消息补偿、心跳保活这些事。SSE 那边浏览器帮你做掉的这里都要自己来。两者的区别SSEWebSocket方向单向服务器 → 客户端全双工双向底层协议就是 HTTP响应一直不结束HTTP 握手后升级为独立帧协议地址普通 HTTP 地址ws:///wss://数据格式只能是 UTF-8 文本文本 二进制自动重连浏览器内置要自己实现消息编号/续传内置id/Last-Event-ID要自己实现代理 / 防火墙兼容最好就是普通 HTTP需要支持Upgrade个别老代理会拦实现成本低高典型场景通知、进度条、AI 流式输出聊天、协同编辑、游戏、行情简单记SSE 只允许服务器往客户端发WebSocket 允许双方随时对发。方向上只需要单向的时候SSE 通常更划算一旦需要双向或者要传二进制就得上 WebSocket。AI 流式输出为什么用 SSE现在说标题的后半段。大模型生成回答是一个 token 一个 token 往外蹦的产品上想做成打字机效果——生成一个就显示一个而不是等一整段生成完再一次性返回。这就是 AI 流式输出的场景。这个场景几乎清一色用 SSE原因是两者刚好对上方向本来就是单向的。模型生成的时候数据只需要从服务器流向浏览器用户在这一刻并不需要同时往回发东西。SSE 唯一的短板不能双向在这个场景里根本不算短板。HTTP 基础设施全兼容。不用额外配 WebSocket 网关公司里现成的反向代理、负载均衡、鉴权、日志都能直接用上。自动重连省事。网络抖一下断了EventSource会自己接回来。实现简单。后端只要设好Content-Type: text/event-stream然后一行行往外写data: ...就行不需要维护连接状态机。以 OpenAI 的接口为例请求里带stream: true返回的就是 SSEdata: {choices:[{delta:{content:你}}]} data: {choices:[{delta:{content:好}}]} data: [DONE]一段一段data: {...}流下来最后用一条data: [DONE]表示结束。前端把每个 chunk 里的delta.content拼起来就是屏幕上看到的一个个字往外冒的效果。注意这里的 SSE 是借来当传输用的消息内容是 JSON格式完全是应用层自己定的跟 SSE 标准本身关系不大。选它主要是因为它就是普通 HTTP还能流。什么时候会想换回 WebSocket前面讲的都是你问一句、AI 答一段的简单对话单向确实够用。但现在的 AI 产品往 agent 方向走之后出现了双向的需求打断生成用户看到一半想让模型停下或者中途改个方向——这是客户端往服务器发的指令。工具调用确认agent 动手之前弹窗问用户同意吗用户点同意或拒绝又是一次反向消息。多设备同步手机上批准了一个操作笔记本上那个标签页也要立刻更新。这些都需要在同一条连接上双向通信。用 SSE 的话每个反向动作都得另开一个 HTTP 请求前端要维护SSE 流和一堆 HTTP 端点之间的状态对应写起来很快就开始打补丁。所以 2026 年前后能明显看到一些团队在 agent 场景里从 SSE 往 WebSocket 迁。所以选型很直白纯问答式流式输出用 SSE 就够了一旦进入需要用户实时介入的 agent 交互就该考虑 WebSocket 了。还有个地方要注意SSE 和 WebSocket 都建立在 HTTP 之上属于应用层和互联网四层模型对照着看它们的位置在Transport Layer的 TCP 之上、Application Layer这一层。SummaryHTTP 不够用的地方它是一问一答服务器无法主动推数据轮询和长轮询都只是在这个框子里打补丁。SSE一个不结束的 HTTP 响应服务器持续往下写data:文本。单向、纯文本、浏览器自带重连兼容性最好。WebSocket先用 HTTP 握手再升级成全双工的帧协议ws:///wss://。双向、支持二进制但重连要自己写。区别的核心方向单向 / 双向其它差异数据格式、重连、代理兼容基本都是从这个区别衍生出来的。AI 流式输出选 SSE生成是单向的SSE 的短板用不上而 HTTP 兼容 自动重连正好是对的。等交互变成需要用户实时介入的 agent 时才会考虑换 WebSocket。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进