📡 WebSocket vs SSE:AI 聊天该用哪个
不是「谁强用谁」。一个单向一个双向,各自有坑,选型要看场景。
先回到 HTTP 的本质
☎️ HTTP 是「一问一答」
标准 HTTP:客户端发请求 → 服务端返回响应 → 连接关闭。
服务端任何时候都不能主动推数据,只能被动等客户端来问。
像打电话:你说「你好」对方回「你好」,然后挂断。
⌨️ AI 对话打破了这个模式
模型生成完整回答要几秒甚至十几秒,如果等全部生成完再返回,用户只能干瞪白屏。
我们要的是:生成一个词推一个词,像 ChatGPT 那样一个字一个字「打」出来。
这需要连接保持打开、服务端能主动持续往外推——HTTP 做不到。
SSE:用 HTTP 撑开一根「单向水管」
🚿 SSE 是 HTTP 的巧用
SSE(Server-Sent Events)不是新协议,是 HTTP/1.1 本来就有的特性。
客户端发普通 GET,但声明 Accept: text/event-stream。服务端收到后不关闭连接,持续往里写数据。
技术上仍是 HTTP 响应,只是响应体「无限长」。
水(数据)只能从服务端流向客户端。
📜 消息格式超简单
SSE 数据不是 JSON,是纯文本:每条以 data: 开头,结尾两个换行。
data: {"token": "你"}
浏览器有内置 EventSource API,注册 onmessage 回调即可,每收一条自动触发,不用自己解析。
为什么 SSE 成为 LLM 流式输出的行业标准?一个常被忽略的关键原因:文字传输天然适合 TCP 的可靠有序传输。
模型输出是连续 token,中间丢一个、乱个序,整段意思可能就变了。你希望每个 token 都准确到达、不乱序——这正是 TCP 的强项,你愿意等网络重传,因为等来的是正确内容。
(这和语音场景相反:语音用 WebRTC/UDP,因为 TCP 重传延迟不可接受。)
WebSocket:升级成真正的双向信道
📡 独立协议 + 握手仪式
WebSocket 是独立的协议,建立在 TCP 之上,但不是 HTTP 的特性。
建立有个握手:客户端发一个像 HTTP 的请求,头里带「我想升级」。服务端同意就回 101 Switching Protocols。
从此这条 TCP 连接「变性」为全双工信道。
🔁 本质区别:通信方向
SSE:只有服务端能推,客户端想发消息必须另起 HTTP 请求,两个方向两套机制。
WebSocket:真正的双向,双方随时都能主动发,一条连接搞定两个方向。
像从对讲机(轮流说话)升级成电话(谁都能随时开口)。
各自的局限(面试追问重点)
SSE:双通道尴尬
单向性导致架构上两条通道:用户发消息走 POST,模型回复走 SSE,靠 conversation ID 关联。状态管理比 WebSocket 复杂,排查链路长。
SSE:连接数上限
HTTP/1.1 下浏览器对同域名最多 6 条连接,SSE 占一条,第 7 个标签页会被排队 → 页面「卡死」。HTTP/2 多路复用解决,但老浏览器/代理仍是坑。
SSE:只能传文本
传语音/图片要 Base64 编码,数据量膨胀约 33%,还要解码。实时语音用 WebRTC 而不是 SSE。
WS:有状态难扩容
每条 WS 连接建立时被 LB 路由到某台后端,之后所有消息都要到同一台(状态存在那)。扩容时老连接迁不过去,得把状态外移到 Redis 做发布订阅,架构变复杂。
WS:代理/防火墙穿透
很多企业 HTTP 代理、老 CDN、安全网关不支持 Upgrade 握手,直接拒掉 → 连接失败。SSE 始终是普通 HTTP,任何代理都能透传。
WS:无请求-响应配对
HTTP 每个请求有自己的响应;WebSocket 消息就是消息,服务端发来一条你不知道对应哪个请求,要自己加请求 ID + 维护映射表,断线重连还要设计重试逻辑。
AI 场景下怎么选
大原则:「单向推就用 SSE,真正需要双向才上 WebSocket」。
文字对话天然是单向推:模型在说话、你在看,你想回复直接发个新 POST 就行,SSE 完全够用。
这也是为什么 OpenAI、Anthropic 的流式 API 全都用 SSE 而不是 WebSocket。
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| LLM 流式文字输出(ChatGPT 风格) | SSE | 单向推送够用,轻量,HTTP 原生支持 |
| 多轮对话(发消息 + 回复) | SSE + 普通 POST | 发消息走 POST,回复走 SSE,解耦简单 |
| 需要用户中途打断模型输出 | WebSocket | 需要在流式输出中途主动发消息 |
| 多人协同(实时同步编辑) | WebSocket | 频繁双向消息,SSE 双通道太繁琐 |
| 实时语音对话 | WebRTC | 音频需要 UDP + 低延迟,TCP 重传是瓶颈 |
| MCP 远程 Server | Streamable HTTP | 2025-03 规范升级,内部仍用 SSE 流,代理穿透友好 |
SSE 是 HTTP 原生特性、服务端单向推送、轻量运维简单,文字对话够用(所以 OpenAI/Anthropic 都用它);WebSocket 是独立协议、全双工双向,但「有状态难扩容、代理难穿透、无请求响应配对」。选型口诀:单向推用 SSE,真正需要双向才上 WebSocket。
最核心是通信方向:SSE 是服务端单向推,客户端想发消息得另起一个 HTTP 请求;WebSocket 是全双工,双方随时都能主动发。另外底层也不一样,SSE 是 HTTP 的原生特性,WebSocket 是独立协议,靠 101 握手从 HTTP 升级成全双工信道。
因为文字对话是单向推的场景:模型在生成、用户在看,中间不需要客户端主动插话。SSE 又轻量、又是 HTTP 原生支持、运维简单,还能被所有代理透传。而且文字传输天然适合 TCP 的可靠有序传输,丢一个 token 意思就变了,SSE 走 TCP 正好。所以用 SSE 完全够,没必要引入 WebSocket 的复杂度。
主要三个:第一是有状态,每条连接建立时被负载均衡路由到某台后端,之后所有消息都要去那台,横向扩容时老连接迁不过去,得把状态外移到 Redis 做发布订阅,架构变复杂;第二是代理和防火墙穿透,很多企业代理、老 CDN 不支持 Upgrade 握手会直接拒掉;第三是没有内置的请求-响应配对机制,要自己加请求 ID 维护映射,断线重连还要设计重试逻辑。