说说 WebSocket 和 SSE 通信的区别及局限性?
为什么 AI 流式输出常用 SSE,而强交互场景更适合 WebSocket 很多人一听到“实时通信”,第一反应就是 WebSocket。理由也很简单:WebSocket 是双向的,听起来更强。 但做 AI 流式对话时,真正常见的方案反而是
为什么 AI 流式输出常用 SSE,而强交互场景更适合 WebSocket
很多人一听到“实时通信”,第一反应就是 WebSocket。理由也很简单:WebSocket 是双向的,听起来更强。
但做 AI 流式对话时,真正常见的方案反而是 SSE。因为大多数文字对话的链路是:用户先发问题,服务端再持续把模型生成的 token 推回来。这个过程并不需要客户端在同一条连接里频繁说话。
1. SSE 不是 WebSocket 的低配版
SSE 和 WebSocket 都能做“看起来实时”的效果,但它们的出发点不一样。SSE 更像“服务端广播”,浏览器连接上以后,服务端一直往外推事件;WebSocket 更像“电话”,连接建立后双方都可以随时发消息。
如果只是 LLM 文字流式输出、通知、监控日志、行情刷新,SSE 通常更简单、更好维护。如果是协同编辑、在线游戏、远程控制、用户随时打断模型输出,WebSocket 才更合适。
2. 普通 HTTP 为什么不够用?
普通 HTTP 是典型的一问一答:客户端发请求,服务端处理完再返回。这个模式适合网页加载、接口查询、提交表单,但不适合“服务端不断产生新内容”的场景。
比如大模型要生成一篇长回答,完整生成可能需要十几秒。如果等全部生成完再返回,用户会觉得页面卡住了。流式通信的价值就在这里:模型生成一点,服务端就推一点,前端马上显示。
3. SSE:服务端单向推送,适合“边生成边显示”
SSE 全称 Server-Sent Events。它基于 HTTP,客户端建立连接后,服务端持续写入 text/event-stream 格式的数据。浏览器侧可以用 EventSource 接收消息,断线后也有基础重连能力。
SSE 的重点不是“双向实时”,而是“服务端持续推”。这和 AI 文字输出特别贴合:用户发起请求后,服务端只要不断推 token 即可。
客户端接收 SSE 的写法非常直观:
const source = new EventSource("/api/chat-stream?question=介绍一下 SSE");
source.onmessage = (event) => {
const data = JSON.parse(event.data);
appendToPage(data.delta);
};
source.addEventListener("done", () => {
source.close();
});
source.onerror = () => {
console.log("SSE 连接异常,浏览器会尝试重连");
};服务端返回时,关键是设置 text/event-stream,并不断写入 data 行:
app.get("/api/chat-stream", async (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
for await (const token of streamTokens(req.query.question)) {
res.write(`data: ${JSON.stringify({ delta: token })}\n\n`);
}
res.write("event: done\n");
res.write("data: {}\n\n");
res.end();
});4. WebSocket:双向长连接,适合强交互
WebSocket 会先通过 HTTP 发起握手,然后升级成 WebSocket 连接。连接建立后,客户端和服务端都可以随时主动发消息。
这类能力适合“双方都要频繁说话”的场景,比如多人协同编辑、在线游戏、实时客服坐席、远程控制,或者用户在模型输出过程中随时发出“停止、改写、切换工具”等指令。
WebSocket 客户端代码通常长这样:
const socket = new WebSocket("wss://example.com/chat");
socket.onopen = () => {
socket.send(JSON.stringify({
type: "ask",
text: "用一句话解释 WebSocket"
}));
};
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
appendToPage(data.delta);
};
function interrupt() {
socket.send(JSON.stringify({ type: "stop" }));
}5. SSE 和 WebSocket 到底差在哪?
很多文章只说“一个单向,一个双向”,但工程落地时还要看协议形态、重连、代理、扩容、数据类型和运维复杂度。下面这张表可以直接记。
6. 为什么很多 LLM 流式接口会用 SSE?
LLM 的文字流式输出,大多数时候是“用户发起一次请求,服务端持续返回 token”。这个方向天然是服务端到客户端。
这也是为什么很多大模型 HTTP 流式接口选择 SSE:它不用维护复杂的双向连接,能直接借助 HTTP 网关、鉴权、日志、限流和代理体系,落地成本更低。
OpenAI 的流式接口文档也明确说明,stream=true 的 HTTP 流式响应基于 Server-Sent Events。对普通文字对话来说,这条路已经足够好用。
7. 两者各自的局限性
SSE 的局限很明确:它不是双向通道;事件流主要是文本;HTTP/1.1 下同域连接数可能成为问题;部分代理如果没有正确配置,可能会把流式响应缓冲起来,导致前端不是逐字显示,而是最后一起出现。
WebSocket 的问题也很现实:连接是有状态的,服务端扩容和负载均衡更复杂;断线重连、心跳、鉴权续期、消息顺序都要自己设计;一些企业代理、防火墙或网关对 WebSocket 不够友好。
8. 放到 MCP 传输里怎么理解?
MCP 的新版远程传输是 Streamable HTTP。它使用 POST / GET 承载 JSON-RPC 消息,服务端可以用 SSE 流式返回多条消息。也就是说,在 MCP 语境里,SSE 不是“落后方案”,而是 HTTP 流式能力的一部分。
如果只是工具调用结果、进度通知、长任务状态回传,HTTP + SSE 更贴近 MCP 的标准传输思路。除非业务本身强依赖双向实时连接,否则不要为了“看起来高级”强行换成 WebSocket。
POST /mcp HTTP/1.1
Accept: application/json, text/event-stream
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_docs",
"arguments": {
"keyword": "SSE vs WebSocket"
}
}
}9. 真实项目怎么选?
不要先问“哪个功能更强”,先问下面几个问题:服务端是不是主要单向推?客户端是否需要在同一条连接里频繁发指令?数据是不是文本为主?是否需要二进制?服务端是否容易维护大量有状态连接?
如果你做的是普通 AI 聊天框、文档问答、代码解释、日志流、任务进度条,SSE 通常够用。如果你做的是语音实时对话、游戏、协同编辑、远程控制,WebSocket 更合适。如果是音视频实时传输,就要继续看 WebRTC。
10. 面试可以这样回答
SSE 和 WebSocket 的核心区别是通信模型不同。SSE 是基于 HTTP 的服务端单向推送,客户端建立连接后主要接收服务端事件;WebSocket 是握手升级后的双向长连接,客户端和服务端都可以随时发消息。
做 LLM 流式输出时,用户通常只是发起一次请求,后面主要是服务端持续推 token,所以 SSE 更简单,能复用 HTTP 生态,运维成本低。只有在需要强双向交互,比如用户随时打断、实时协作、游戏或控制指令频繁来回时,WebSocket 才更合适。
两者都有局限:SSE 只适合服务端到客户端的文本流,HTTP/1.1 下连接数可能受限;WebSocket 虽然双向,但连接有状态,重连、心跳、扩容、代理兼容都更麻烦。选型不是看谁更强,而是看业务通信方向和复杂度。
要点速读
为什么 AI 流式输出常用 SSE,而强交互场景更适合 WebSocket 很多人一听到“实时通信”,第一反应就是 We
- 为什么 AI 流式输出常用 SSE,而强交互场景更适合 WebSocket 很多人一听到“实时通信”,第一反应就是 We
- 更多细节仍在持续更新中
- 更多细节仍在持续更新中