SSE 协议规范要求

什么是SSE

  • SSE是Server-Sent Events的简称,也会称为EventSource。
  • Server-Sent Event 技术的作用是可以完成从服务器端到客户端(浏览器)单向的消息传送。因此我们可以用这个来做推送。
  • SSE适用于更新频繁、低延迟并且数据都是从服务端到客户端。

客户端请求头要求

  1. Accept: text/event-stream

    • 表明客户端期望接收事件流格式的数据
    • 服务器必须返回此 MIME 类型
  2. Connection: keep-alive (隐含必需)

    • HTTP/1.1 默认开启 keep-alive,但显式声明更可靠
    • 保持 TCP 连接长期开放,用于持续发送事件
  3. Cache-Control: no-cache (强烈推荐)

    • 防止中间代理缓存事件流
    • 避免客户端获取过期事件
  4. Last-Event-ID (可选)

    • 客户端重连时发送上次收到的最后一个事件ID
    • 服务器据此决定发送哪些新事件

典型 SSE 请求头示例:

GET /updates HTTP/1.1
Host: api.example.com
Accept: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
Last-Event-ID: 12345

服务器响应要求

  1. 响应头

    HTTP/1.1 200 OK
    Content-Type: text/event-stream
    Cache-Control: no-cache
    Connection: keep-alive
    Transfer-Encoding: chunked
    
    • Content-Type: text/event-stream 必须设置
    • 建议禁用缓存(no-storeno-cache 更严格)
    • 保持连接开放(HTTP/2 自动处理持久连接)
  2. 响应体格式

    event: message\n
    id: 123\n
    retry: 3000\n
    data: {"user":"John","action":"login"}\n
    \n
    
    • 事件字段 (event, id, data, retry) 以 field: value 格式
    • 每个事件以两个换行符 \n\n 结束(必须)
    • 字段顺序无关紧要,但 data 可以多次出现(会被连接)

事件格式规范

字段 描述 示例
data 必需字段,事件内容(可多次出现) data: Hello\n data: World\n
id 事件ID,用于断线重连时定位 id: 12345\n
event 事件类型(默认 message event: statusUpdate\n
retry 重连时间(毫秒),建议 >= 1000ms retry: 3000\n
: 注释行(服务器发送,客户端忽略) : ping\n

完整事件流示例

: 连接建立\n
\n
event: userConnect\n
id: 1001\n
data: {"id": "user-123", "time": "2023-08-15T10:00:00Z"}\n
\n
data: 系统通知\n
data: 服务器将在5分钟后维护\n
id: 1002\n
\n
event: stockUpdate\n
id: 1003\n
data: {"symbol":"AAPL","price":182.91}\n
\n

关键协议要求

  1. 分隔符要求

    • 每个事件必须\n\n 结束
    • 字段行以 \n 结束
    • 空事件 (\n\n) 是有效的心跳包
  2. 编码要求

    • 必须使用 UTF-8 编码
    • 不支持二进制数据(需 base64 编码)
  3. 连接管理

    • 服务器不应主动关闭连接(除非错误)
    • 客户端断开后应自动重连
    • 建议设置 15-30 秒心跳机制

服务器端实现注意事项

  1. 框架支持

    // Node.js (Express)
    app.get('/events', (req, res) => {
      res.setHeader('Content-Type', 'text/event-stream');
      res.setHeader('Cache-Control', 'no-store');
      
      const timer = setInterval(() => {
        res.write(`data: ${JSON.stringify({time: Date.now()})}\n\n`);
      }, 1000);
      
      req.on('close', () => clearInterval(timer));
    });
    
  2. 防超时配置

    • Nginx:
      proxy_read_timeout 24h;
      proxy_buffering off;
      
    • Apache:
      TimeOut 86400
      
  3. 心跳机制

    # Flask
    @app.route('/stream')
    def stream():
        def generate():
            while True:
                yield ': heartbeat\n\n'  # 每15秒发送
                time.sleep(15)
        return Response(generate(), mimetype='text/event-stream')
    

调试工具

  1. 客户端验证

    curl -H "Accept: text/event-stream" https://api.example.com/events
    
  2. 协议检查点

    mermaid.png

    graph TD
      A[客户端发起请求] --> B{请求头包含<br>Accept: text/event-stream?}
      B -->|是| C[服务器响应200]
      B -->|否| D[返回406 Not Acceptable]
      C --> E{响应头包含<br>Content-Type: text/event-stream?}
      E -->|是| F[持续发送事件]
      E -->|否| G[客户端断开连接]
      F --> H{事件以\n\n结束?}
      H -->|是| I[客户端处理事件]
      H -->|否| J[协议错误]
    

常见问题处理

  1. 连接过早关闭

    • 服务器:检查防火墙/代理超时设置
    • 客户端:实现自动重连
    • 解决方案:添加心跳事件
  2. 编码问题

    • 确保服务器使用 UTF-8
    • 特殊字符需转义:
      func escapeSSEString(_ string: String) -> String {
          return string
              .replacingOccurrences(of: "\n", with: "\\n")
              .replacingOccurrences(of: "\r", with: "\\r")
      }
      
  3. 跨域问题

    Access-Control-Allow-Origin: *
    Access-Control-Expose-Headers: Last-Event-ID
    

SSE 协议的设计简洁而强大,核心在于保持 HTTP 连接开放并通过严格的 \n\n 分隔符实现事件流传输。正确实现后,可支持实时更新、通知推送等多种场景。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容