Server-Sent Events

138 阅读2分钟

一. 本质

SSE(Server-Sent Events,服务器发送事件)是一种基于 HTTP 的 单向通信 技术,允许服务器主动向客户端(通常是浏览器)推送实时数据。它特别适合需要 实时更新 但不需要双向通信的场景,例如实时通知、股票价格更新、社交媒体动态等

二. 特点

  1. 单向通信:服务器主动发送数据,客户端被动接收
  2. 基于HTTP协议:无需额外协议,兼容性好,适合传统 Web 应用。
  3. 自动重连:断开后会自动尝试重新连接
  4. 轻量级:相比 WebSocket,实现简单,适合低复杂度场景。

image.png

三. 与WebSocket的区别

特性SSEWebSocket
通信方向单向(服务器 → 客户端)双向(客户端 ↔ 服务器)
协议HTTP/1.1自定义协议(需握手)
数据格式文本(text/event-stream二进制或文本(灵活)
自动重连✅ 支持❌ 需手动实现
浏览器兼容性良好(现代浏览器均支持)需要服务器和客户端配合
适用场景实时通知、数据推送实时聊天、游戏、远程控制等

四. SSE的工作原理

  1. 客户端建立连接
    使用 EventSource 对象发起 HTTP 请求,指向服务器的事件流接口(URL)。
    服务器向浏览器发送的 SSE 数据,必须是 UTF-8 编码的文本,具有如下的 HTTP 头信息:
    Accept: text/event-stream 
    Cache-Control: no-cache 
    Connection: keep-alive
    
  2. 服务器保持连接
    服务器不立即关闭连接,而是持续发送数据(通过 text/event-stream 格式)。
  3. 数据传输
    服务器以 text/event-stream 格式发送数据,每条消息必须符合以下格式。客户端(浏览器)接收到数据后,自动解析并触发 onmessage 事件。
    data: <数据内容>\n\n
    
  4. 自动重连
    • 如果断开,客户端会自动重新连接服务器。
    • 可通过 retry 指令指定重连间隔时间
        retry: 5000\n\n  // 5秒后重连
    

五. 使用SSE请求

  1. 封装一个useSSEhooks

    const 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;
    };
    
  2. 封装一个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();
      };
    };