SSE与WebSocket对比:特性、适用场景与性能

笔记哥 / 04-17 / 37点赞 / 0评论 / 736阅读
Server-Sent Events (SSE) 和 WebSocket 都是用于实现服务器与客户端实时通信的技术,但它们在设计目标、协议特性和适用场景上有显著区别。以下是两者的详细对比: * * * ### **一、核心区别总结** | **对比维度** | **SSE (Server-Sent Events)** | **WebSocket** | | --- | --- | --- | | **通信方向** | 单向(服务器 → 客户端) | 全双工(服务器 ↔ 客户端) | | **协议基础** | 基于HTTP | 独立协议(`ws://` 或 `wss://`) | | **数据格式** | 纯文本(事件流格式) | 二进制或文本 | | **自动重连** | 内置支持 | 需手动实现 | | **浏览器兼容性** | 除IE外主流浏览器支持 | 所有现代浏览器支持 | | **适用场景** | 服务器向客户端推送实时数据(如股票行情、新闻) | 双向交互场景(如聊天、游戏、协同编辑) | * * * ### **二、技术细节对比** #### 1. **连接建立** - **SSE**: ```javascript // 客户端代码 const eventSource = new EventSource("/sse-endpoint"); eventSource.onmessage = (e) => console.log(e.data); ``` - 使用标准HTTP请求,头部包含: ```csharp Accept: text/event-stream Cache-Control: no-cache Connection: keep-alive ``` - **WebSocket**: ```javascript // 客户端代码 const socket = new WebSocket("ws://example.com/ws"); socket.onmessage = (e) => console.log(e.data); ``` - 通过HTTP Upgrade切换协议: ```csharp GET /ws HTTP/1.1 Upgrade: websocket Connection: Upgrade ``` #### 2. **数据传输** - **SSE**: - 服务器响应格式: ```csharp event: priceUpdate data: {"symbol":"AAPL","price":182.73} \n\n ``` - 支持事件类型(`event`字段)和重试时间(`retry`字段) - **WebSocket**: - 二进制或文本帧自由传输: ```javascript // 发送文本 socket.send("Hello Server!"); // 发送二进制数据(如文件) socket.send(arrayBuffer); ``` #### 3. **连接维护** | **特性** | **SSE** | **WebSocket** | | --- | --- | --- | | 心跳检测 | 依赖HTTP长连接 | 需手动实现Ping/Pong帧 | | 断线重连 | 自动(客户端默认3秒重试) | 需手动重连 | | 连接状态管理 | 简单(HTTP状态码控制) | 复杂(需处理多种帧类型) | * * * ### **三、选择建议** #### **使用SSE的场景** ✅ 1. **服务器单向推送** - 实时监控(服务器指标、日志流) - 新闻/股票行情推送 - 长轮询替代方案 2. **需要简单实现** - 无需额外协议,复用HTTP基础设施 - 自动重连和事件ID支持 3. **文本数据为主** - 结构化数据(JSON)传输 #### **使用WebSocket的场景** ✅ 1. **双向交互需求** - 在线聊天室 - 多人在线游戏 - 实时协作编辑 2. **低延迟通信** - 高频双向数据交换(如视频会议信令) 3. **二进制数据传输** - 文件传输、音视频流 * * * ### **四、代码示例对比** #### **SSE实现(Spring Boot)** ```java @GetMapping("/sse") public SseEmitter streamData() { SseEmitter emitter = new SseEmitter(); executor.execute(() -> { try { for (int i = 0; i < 10; i++) { emitter.send( SseEmitter.event() .name("update") .data("Event #" + i) ); Thread.sleep(1000); } emitter.complete(); } catch (Exception e) { emitter.completeWithError(e); } }); return emitter; } ``` #### **WebSocket实现(Spring Boot)** ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myHandler(), "/ws") .setAllowedOrigins("*"); } @Bean public WebSocketHandler myHandler() { return new TextWebSocketHandler() { @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) { // 处理客户端消息 session.sendMessage(new TextMessage("ECHO: " + message.getPayload())); } }; } } ``` * * * ### **五、性能与资源消耗** | **指标** | **SSE** | **WebSocket** | | --- | --- | --- | | 连接开销 | 较高(HTTP头重复传输) | 低(连接后无额外开销) | | 服务器内存占用 | 每个连接独立线程/连接 | 更高效的连接管理 | | 适合连接数 | 适合中低并发(数千连接) | 适合高并发(数万连接) | * * * ### **六、兼容性解决方案** 当需要兼容老旧浏览器时: - **SSE降级方案**:使用长轮询(Long Polling) - **WebSocket降级方案**:使用SockJS库 ```javascript const sock = new SockJS('/ws-endpoint'); sock.onmessage = (e) => console.log(e.data); ``` * * * ### **总结** - **SSE**是**简单、单向**实时通信的理想选择,尤其适合已有HTTP架构的项目。 - **WebSocket**在需要**双向、低延迟**交互时不可或缺,但实现复杂度更高。 根据你的应用场景选择: - **只需接收服务器更新?** → 用SSE - **需要双向对话?** → 用WebSocket