一. 本质
SSE(Server-Sent Events,服务器发送事件)是一种基于 HTTP 的 单向通信 技术,允许服务器主动向客户端(通常是浏览器)推送实时数据。它特别适合需要 实时更新 但不需要双向通信的场景,例如实时通知、股票价格更新、社交媒体动态等
二. 特点
- 单向通信:服务器主动发送数据,客户端被动接收
- 基于HTTP协议:无需额外协议,兼容性好,适合传统 Web 应用。
- 自动重连:断开后会自动尝试重新连接
- 轻量级:相比 WebSocket,实现简单,适合低复杂度场景。
三. 与WebSocket的区别
| 特性 | SSE | WebSocket |
|---|---|---|
| 通信方向 | 单向(服务器 → 客户端) | 双向(客户端 ↔ 服务器) |
| 协议 | HTTP/1.1 | 自定义协议(需握手) |
| 数据格式 | 文本(text/event-stream) | 二进制或文本(灵活) |
| 自动重连 | ✅ 支持 | ❌ 需手动实现 |
| 浏览器兼容性 | 良好(现代浏览器均支持) | 需要服务器和客户端配合 |
| 适用场景 | 实时通知、数据推送 | 实时聊天、游戏、远程控制等 |
四. SSE的工作原理
- 客户端建立连接:
使用EventSource对象发起 HTTP 请求,指向服务器的事件流接口(URL)。服务器向浏览器发送的 SSE 数据,必须是 UTF-8 编码的文本,具有如下的 HTTP 头信息: Accept: text/event-stream Cache-Control: no-cache Connection: keep-alive - 服务器保持连接:
服务器不立即关闭连接,而是持续发送数据(通过text/event-stream格式)。 - 数据传输:
服务器以text/event-stream格式发送数据,每条消息必须符合以下格式。客户端(浏览器)接收到数据后,自动解析并触发onmessage事件。data: <数据内容>\n\n - 自动重连
- 如果断开,客户端会自动重新连接服务器。
- 可通过
retry指令指定重连间隔时间
retry: 5000\n\n // 5秒后重连
五. 使用SSE请求
-
封装一个
useSSEhooksconst useSSE = (url) => { const [messages, setMessages] = useState([]); useEffect(() => { if (!url) return; const eventSource = new EventSource(url); eventSource.addEventListener('message', (event) => { try { const data = JSON.parse(event.data); setMessages((prev) => [...prev, data]); } catch (error) { console.error('解析 SSE 消息失败:', error); } }); eventSource.addEventListener('error', (err) => { console.error('SSE 错误:', err); setTimeout(() => { eventSource.close(); new EventSource(url); }, 5000); }); return () => { eventSource.close(); }; }, [url]); return messages; }; -
封装一个sse请求
const sseReauest = (sseUrl: string, setMessages: Function, callback?: Function) => { if (!sseUrl) return; const eventSource = new EventSource(sseUrl); // 监听 message 事件(默认事件) eventSource.addEventListener('message', (event) => { const data = event.data; // 处理数据(如更新 UI) if (data === "[DONE]") { eventSource.close(); // 关闭连接 callback && callback() } else { setMessages([data?.replaceAll('\\x0A','\n')]); } }); eventSource.addEventListener('error', (err) => { console.error('SSE 错误:', err); setMessages(["连接失败, 请重试..."]); callback && callback() // setTimeout(() => { // 自动重试 // eventSource.close(); // new EventSource(sseUrl); // }, 5000); }); return () => { eventSource.close(); }; };