SSE (server-sent events)

SSE 是基于 HTTP/HTTPS 的单向长连接协议 ,用于服务端主动向浏览器推送数据,仅支持服务端 → 客户端单向通信,相比 WebSocket 更轻量、原生支持浏览器、自动重连。

  • 传输层:标准 HTTP/1.1+ ,复用现有 HTTP 链路,不新建协议端口
  • 连接模式:长轮询长连接,客户端发起一次 HTTP 请求,服务端持续响应流,连接不断开。
  • 单向通信:仅服务端推数据,客户端如需回传,需额外走普通 HTTP 请求。
  • 浏览器原生支持:EventSource API,无需第三方库。

EventSource API 接口

js 复制代码
[Exposed=(Window,Worker)]
interface EventSource : EventTarget {
  constructor(USVString url, optional EventSourceInit eventSourceInitDict = {});

  readonly attribute USVString url;
  readonly attribute boolean withCredentials;

  // ready state
  const unsigned short CONNECTING = 0;
  const unsigned short OPEN = 1;
  const unsigned short CLOSED = 2;
  readonly attribute unsigned short readyState;

  // networking
  attribute EventHandler onopen;
  attribute EventHandler onmessage;
  attribute EventHandler onerror;
  undefined close();
};

dictionary EventSourceInit {
  boolean withCredentials = false;
};

服务端响应头必须设置

bash 复制代码
Content-Type: text/event-stream # 固定 MIME 类型,标识 SSE 流 
Cache-Control: no-cache # 禁止浏览器缓存流数据 
Connection: keep-alive # 保持长连接

标准消息格式

SSE 消息由多个行字段 组成,字段格式统一:[字段名]: [内容]冒号后必须跟空格(规范要求)。

字段名 作用 格式说明
data 主体数据(最常用) 消息内容,可多行 (多行写多个 data:
event 自定义事件名 用于区分不同业务事件,客户端按事件名监听
id 消息 ID(断点续传) 每条消息唯一标识,用于重连定位
retry 重连间隔(毫秒) 客户端断连后,等待多久重试连接
: 注释行 仅服务端注释,客户端直接忽略

示例 基本使用 (onopen、onerror、onmessage)

js 复制代码
const eventSource = new EventSource("https://localhost:8443/api/sse");
console.log("eventSource", eventSource);
// 连接建立、流就绪时触发一次
eventSource.onopen = () => {
  console.log("连接成功", eventSource);
};
eventSource.onerror = (event) => {
  console.log("连接错误", event);
};

eventSource.onmessage = (event) => {
  console.log("收到消息", event);
};

const btnClose = document.getElementById("btn-close");
btnClose.addEventListener("click", () => {
  // 手动关闭,不会自动重连
  eventSource.close();
});

readyState 状态

  • 0: CONNECTING 正在连接
  • 1: OPEN 连接成功,正常通信
  • 2: CLOSED 连接已关闭,不再重连

网络中断、服务端断开、HTTP 异常、跨域失败、超时都会触发 onerror

出现跨域失败问题,会触发 onerror 事件

js 复制代码
eventSource.onerror = function () { 
    switch (eventSource.readyState) { 
    case EventSource.CONNECTING: 
        console.log('临时断连,即将自动重连'); 
        break; 
    case EventSource.CLOSED: 
        console.log('连接彻底关闭,不再重连');
        break; 
    } 
};

示例 自定义事件

服务端通过 event: 事件名 定义自定义事件,前端不能用 onXXX ,必须用 addEventListener 监听。

js 复制代码
const eventSource = new EventSource("https://localhost:8443/api/sse-event");
console.log("eventSource", eventSource);

// 监听默认 message 事件(无 event 字段时触发)
eventSource.onmessage = (e) => {
  console.log("默认消息:", e.data);
};
eventSource.addEventListener("alarm", (event) => {
  console.log("收到报警事件", event);
});

eventSource.addEventListener("notice", (event) => {
  console.log("收到通知事件", event);
});

// 错误处理
eventSource.onerror = () => {
  console.error("连接异常");
};

const btnClose = document.getElementById("btn-close");
btnClose.addEventListener("click", () => {
  eventSource.close();
});
js 复制代码
app.get("/api/sse-event", (req, res) => {
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");

  // 模拟定时推送不同自定义事件
  let count = 1;
  const timer = setInterval(() => {
    // 场景1:推送 notice 自定义事件
    if (count % 2 === 1) {
      res.write("event: notice\n");
      res.write(`id: msg_${count}\n`); // 消息ID
      res.write(`data: 第${count}条系统公告\n\n`);
    }
    // 场景2:推送 alarm 自定义事件
    else {
      res.write("event: alarm\n");
      res.write(`id: msg_${count}\n`);
      res.write("data: 设备负载过高,请关注\n\n");
    }
    count++;
  }, 2000);

  // 客户端断开连接,清除定时器,防止内存泄漏
  req.on("close", () => {
    clearInterval(timer);
    res.end();
    console.log("客户端已开连接,定时器已清除");
  });
});

重连机制 (SSE 内置自动重连

一、默认重连规则

  1. 默认重连间隔3000 毫秒(3 秒)
  2. 服务端可通过 retry: 毫秒数 全局修改客户端重连等待时间。
  3. 只要不是手动调用 close() ,断开后客户端一定会自动重试。
状态值 常量名 含义 重连行为
0 CONNECTING 正在建立连接 / 等待重连 断连后进入此状态,倒计时结束发起新请求
1 OPEN 连接正常、可收发数据 无重连
2 CLOSED 连接永久关闭 彻底停止重连

二、完整重连流程

  1. 正常连接中断(网络 / 服务端关闭流)→ 触发 error 事件。
  2. 客户端等待 retry 指定时间(默认 3s)。
  3. 自动发起全新 HTTP GET 请求重建长连接。
  4. 连接成功 → 触发 open,恢复推送

三、断点续传(基于 id + Last-Event-ID

这是 SSE 可靠推送的核心:

  1. 服务端每条消息携带 id: xxx
  2. 客户端接收后,缓存当前最新 lastEventId
  3. 断连重连时,客户端请求头自动带上:Last-Event-ID: xxx
  4. 服务端读取该请求头,从该 ID 的下一条消息开始补发,实现断连不丢数据。

四、重连终止条件

满足任一,客户端停止自动重连

  • 前端代码执行 EventSource.close() → 主动关闭。
  • 服务端返回 HTTP 204 状态码(规范约定:204 = 永久关闭,禁止重连)。
  • 404 → URL 无效,重连无意义
  • CORS → 被拦截,重连也会被拦截
  • 浏览器页面关闭 / 标签页销毁。

五、边界说明

  • 重连是纯客户端行为,服务端无需额外逻辑。
  • 跨域 SSE:服务端必须配置 Access-Control-* 跨域头,否则连接失败。
  • 最大重连次数:无上限,会一直重试。

示例 断点重连

前端 EventSource 天生缓存 lastEventId、自动携带请求头,只需正常监听即可。

  • 只要帧里有 ide.lastEventId / es.lastEventId 自动更新。
  • 重连请求自动附加 Last-Event-ID 请求头,无需手动拼接。
  • 手动调用 es.close() 后不再重连,也不会再发该请求头。

请求头会自动携带 Last-Event-ID

服务端

  • 维护消息队列 / 存储(记录每条消息 + ID)。
  • 接收 Last-Event-ID 请求头,增量补发历史消息,再推送实时消息。

示例 重连时间间隔

js 复制代码
  const eventSource = new EventSource("https://localhost:8443/api/sse-retry");
  eventSource.onmessage = (event) => {
    console.log("收到消息", event);
  };

  let lastErrorTime = 0;
  eventSource.onerror = (event) => {
    const now = Date.now();
    const interval = lastErrorTime ? now - lastErrorTime : 0;
    console.log("连接错误", event, `距上次错误: ${interval}ms`);
    lastErrorTime = now;
  };

  eventSource.onopen = () => {
    console.log("连接成功");
    
  };
  const btnClose = document.getElementById("btn-close");
  btnClose.addEventListener("click", () => {
    eventSource.close();
  });

默认 间隔

服务器服务端通过 retry: 数值 动态设置客户端断连后的重连等待时长 ,单位 毫秒 (ms) ,仅支持正整数;写 0 / 负数 / 非数字,浏览器会忽略,沿用原有配置。

js 复制代码
app.get("/api/sse-retry", (req, res) => {
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");
  // 连接建立,立即设置重连间隔为 5 秒
  res.write("retry: 5000\n\n");
  let code_id = 1;
  const timer = setInterval(() => {
    res.write(`id: ${code_id++}\n`);
    res.write("data: 123\n\n");
  }, 3000);

  req.on("close", () => {
    clearInterval(timer);
    code_id = null;
    res.end();
  });
});

同源默认协议cookie。

跨域,服务端需要配置响应头Access-Control-Allow-Origin: 前端具体域名Access-Control-Allow-Credentials:true

js 复制代码
new EventSource(SSE_URL,{
    withCredentials: true,
  });

EventSource 不支持自定义请求头,也不能用 Authorization 头。

最后

  1. SSE
相关推荐
桜吹雪1 小时前
所有智能体架构(3):Planning(计划任务)
javascript·人工智能·langchain
gCode Teacher 格码致知1 小时前
Javascript技术:CSS 中rem、vh 和 px各有其最佳适用场景-由Deepseek产生
开发语言·javascript·css
fastjson_2 小时前
Edge浏览器开启IE兼容模式
javascript·edge·html
饼饼饼2 小时前
React19 新手指南:JSX 没那么难,用好这几条规则就够了
前端·javascript·react.js
丷丩3 小时前
MapLibre GL JS第50课:用表达式创建虚线渐变线
javascript·gis·mapbox·maplibre gl js
石山代码4 小时前
变量与解构
开发语言·前端·javascript
hyunbar4 小时前
配置 Cloudflare Tunnel:把 Mac 上的 Web 服务变成安全域名
网络协议·https·bash
888CC++4 小时前
箭头函数(ES6)
前端·javascript·es6
qq_419854055 小时前
css filter
前端·javascript·css