
原文链接WebSocket 与 SSE初级开发者的实时通信选型指南很多人一听到“实时通信”就会立刻想到 WebSocket。但“实时”并不总是意味着客户端和服务端都要持续、频繁地互相发送消息。例如任务进度、系统通知、日志流、订单状态和监控指标通常是服务端持续通知页面而聊天、协作编辑、游戏操作和远程控制则往往需要两端都能随时主动发送消息。这正是 WebSocket 与 SSE 的核心分界线。先记住一句话SSEServer-Sent Events服务端 → 浏览器的单向事件流。WebSocket客户端 ↔ 服务端的全双工消息通道。如果页面只需要“接收更新”优先评估 SSE如果客户端也要在同一条长连接上频繁发送消息优先评估 WebSocket。1. 两者分别解决什么问题SSE让服务端持续推送事件SSE 使用一个保持打开的 HTTP 响应。浏览器通过EventSource发起请求后服务端以text/event-stream格式不断写入事件浏览器会持续接收这些事件。SSE 的方向是服务端到客户端客户端不能通过这条 SSE 连接反向发送事件。(html.spec.whatwg.org)但“不能反向发送”不等于客户端不能提交操作。用户点击按钮、提交表单或发送指令时仍然可以调用普通fetch()、XHR 或表单请求。适合的例子构建完成、导出完成等任务进度后台日志实时展示系统通知、未读消息数订单状态、股票或监控指标展示AI 输出的逐段文本流WebSocket建立双向持续消息通道WebSocket 建连时会先进行 HTTP 风格的握手握手成功后通信进入 WebSocket 协议的数据传输阶段。连接建立后客户端与服务端都可以独立、主动地发送消息。(rfc-editor.org)适合的例子聊天室与即时消息多人协作编辑在线游戏中的操作同步实时控制台、远程控制指令需要传输二进制数据的场景2. 通信模型的差异维度SSEWebSocket通信方向服务端 → 客户端客户端 ↔ 服务端连接基础HTTP 长响应流HTTP 握手后切换至 WebSocket 协议浏览器 APIEventSourceWebSocket客户端发送另走普通 HTTP 请求send()直接发送消息类型UTF-8 文本事件流文本与二进制消息自动重连浏览器原生支持基础重连语义通常由业务自行实现典型用途通知、日志、进度、数据流聊天、协作、控制、游戏一个实用判断是是否需要客户端通过同一条长连接频繁发消息 ├─ 是优先 WebSocket └─ 否是否只是服务端持续推送页面更新 ├─ 是优先 SSE └─ 否先考虑普通 HTTP 请求不必为了“实时”引入长连接3. 连接建立方式有什么不同WebSocketHTTP 握手后升级WebSocket 常以ws://或加密的wss://地址建立。浏览器会发送带有Upgrade: websocket的握手请求服务端成功响应后双方开始交换 WebSocket 帧。WebSocket 的握手与数据传输是两个阶段因此不能简单理解成“一个永远不结束的普通 HTTP 响应”。(rfc-editor.org)生产环境应优先使用wss://与 HTTPS 一样通过 TLS 保护传输内容。SSE一个持续输出的 HTTP 响应SSE 通常使用普通https://接口例如/api/events。原生EventSource会通过 GET 请求建立连接。服务端响应头应声明Content-Type: text/event-stream Cache-Control: no-cache随后服务端不断向响应体写入符合 SSE 格式的文本。SSE 事件流的 MIME 类型是text/event-stream并按 UTF-8 解码。(html.spec.whatwg.org)4. 前端最小用法对比WebSocket连接、发送、接收const socket new WebSocket(wss://example.com/chat); socket.addEventListener(open, () { socket.send(JSON.stringify({ type: join, roomId: room-1 })); }); socket.addEventListener(message, (event) { const message JSON.parse(event.data); console.log(收到服务端消息, message); }); socket.addEventListener(close, () { console.log(连接关闭); }); socket.addEventListener(error, (event) { console.error(连接异常, event); });WebSocket.send()可以发送字符串也可以发送Blob、ArrayBuffer、TypedArray等二进制数据。(developer.mozilla.org)SSE订阅、接收事件const source new EventSource(/api/events); source.addEventListener(message, (event) { const data JSON.parse(event.data); console.log(默认消息, data); }); source.addEventListener(task-progress, (event) { const progress JSON.parse(event.data); console.log(任务进度, progress.percent); }); source.onerror () { console.warn(SSE 连接异常浏览器通常会尝试重连); }; // 页面离开或不再订阅时关闭 // source.close();SSE 默认事件名是message。服务端也可以通过event:指定具名事件再由前端使用addEventListener()监听。(html.spec.whatwg.org)5. SSE 的消息格式不只能传 JSONSSE 是文本事件流常见格式如下id: 42 event: task-progress retry: 3000 data: {taskId:a-1,percent:60}每个事件以一个空行结束。其中data事件内容。JSON 只是常见编码方式也可以是普通文本。event事件名称缺省时为message。id事件标识可用于断线后的恢复位置。retry建议浏览器重连前等待的毫秒数。SSE 标准定义了这些字段及其解析方式事件流本身是 UTF-8 文本不适合作为原生二进制流通道。(html.spec.whatwg.org)6. 断线重连SSE 更省心但不等于可靠投递SSE 的一个明显优势是浏览器具有原生重连语义。服务端发送过id后浏览器重新建立连接时可以携带Last-Event-ID告知服务端客户端最后接收到的事件位置。(html.spec.whatwg.org)但这只解决了“客户端告诉服务端它上次收到哪里”。服务端是否保存历史事件、如何补发、如何避免重复、如何保证业务顺序仍需要自行设计。WebSocket 的浏览器原生 API 不会替你完成业务级重连、心跳、消息确认或会话恢复。常见实践包括连接关闭后使用指数退避策略重连在应用层定期发送心跳或由服务端推送保活消息为消息加入序号、版本号或事件 ID重连成功后重新拉取状态快照或按序号补齐增量。不要把“连接不断开”当作可靠性保证。网络切换、浏览器休眠、代理超时和服务发布都可能让长连接中断。7. 场景应该怎样选优先选择 SSE当核心需求是“服务端持续告诉页面新状态”时SSE 往往更简单通知中心任务进度CI/CD 或后端日志流监控数据推送订单、物流、审核状态更新单向行情或看板数据大模型文本逐段输出客户端若偶尔需要提交操作使用fetch()即可不必为了这一点改用 WebSocket。优先选择 WebSocket当客户端和服务端需要持续、低延迟地双向协作时WebSocket 更合适聊天和在线客服多人协作文档棋牌游戏或动作游戏实时控制指令双向交互频繁的实时应用二进制数据传输例如音频片段或自定义协议数据8. 常见误区误区一实时功能一定要用 WebSocket不对。单向推送需求使用 SSE 通常更直接浏览器 API 和事件格式也更贴近“服务端通知页面”的模型。误区二SSE 只能传 JSON不对。SSE 传递的是文本事件data可以放 JSON、纯文本或其他自行约定的文本编码JSON 只是前后端常用的数据格式。误区三WebSocket 天然更快不对。两者都能维持长连接。实际延迟和吞吐受消息大小、序列化方式、网络质量、服务端处理、代理配置和客户端渲染等因素影响。WebSocket 的关键能力优势是双向通信与二进制消息而不是在所有实时场景中自动更快。9. 上线前的注意事项长连接会占用资源每个 SSE 或 WebSocket 客户端都对应一个持续连接。服务端需要关注并发连接数、内存、文件描述符、负载均衡策略以及发布时如何让客户端平滑重连。代理和网关可能影响流式响应CDN、反向代理和负载均衡器可能存在空闲超时、响应缓冲或升级协议配置问题。SSE 要验证事件是否被缓冲WebSocket 要验证Upgrade请求是否正确转发以及连接超时配置是否合理。认证与跨域必须单独验证WebSocket 握手来自浏览器时会带有Origin信息服务端应校验允许的来源而不是只要能连上就接受。RFC 6455 将Origin作为限制未经授权跨域使用的重要信息。(rfc-editor.org)SSE 如果跨域且需要 Cookie 等凭据要正确配置 CORS并在创建EventSource时按需启用withCredentials。同时原生EventSource不像fetch()那样方便地附加自定义请求头如果项目依赖 Bearer Token 或自定义认证头需要在接口设计阶段提前处理。HTTP/1.1 下不要在一个页面滥开 SSE在未使用 HTTP/2 的情况下浏览器对同一浏览器和域名的并发 SSE 连接数限制较低。实践中应尽量复用一条事件流再在事件中按类型或业务 ID 分发而不是让每个组件各自建立连接。(developer.mozilla.org)结论按通信方向选而不是按“实时”二字选可以用下面这份简表作为最终判断只需服务端推送更新先选 SSE。需要客户端和服务端持续双向发消息选 WebSocket。需要二进制消息优先 WebSocket。希望浏览器提供基础自动重连语义SSE 更有优势。需要断线后不丢、不重、不乱序无论 SSE 还是 WebSocket都必须设计业务级事件 ID、补偿和状态同步。技术选型的重点不是“哪个更高级”而是业务到底需要单向推送还是双向持续交互。参考资料RFC 6455The WebSocket ProtocolWHATWG HTML StandardServer-sent eventsMDNUsing server-sent eventsMDNWriting WebSocket client applications