💡如何使用SSE通信(Event-stream)

我想通过这篇文章,以最简单的方式给大家讲解 SSE(event-stream)通信。

前后端均使用 js 来编写简单代码

后端代码:

需要安装 express:

shell 复制代码
pnpm i express

index.js:

javascript 复制代码
const express = require("express");
const app = express();
const PORT = 3000;

// 处理 SSE 连接
app.get("/events", (req, res) => {
	// 设置必要的 HTTP 头
	res.setHeader("Content-Type", "text/event-stream");
	res.setHeader("Cache-Control", "no-cache");
	res.setHeader("Connection", "keep-alive");

	// 允许跨域(如果需要)
	res.setHeader("Access-Control-Allow-Origin", "*");

	// 发送初始连接确认
	res.write("retry: 10000\n\n");

	// 定时发送消息(示例)
	const sendEvent = (data, id) => {
		res.write(`id: ${id}\n`);
		res.write(`data: ${JSON.stringify(data)}\n\n`);
	};

	let messageId = 0;
	const intervalId = setInterval(() => {
		const data = {
			message: `这是第 ${messageId} 条消息`,
			timestamp: new Date(),
		};
		sendEvent("hello", messageId++);
	}, 3000); // 每3秒发送一次

	// 监听客户端关闭连接
	req.on("close", () => {
		clearInterval(intervalId);
		res.end();
	});
});

app.listen(PORT, () => {
	console.log(`服务器正在运行在 http://localhost:${PORT}`);
});

为了方便测试,允许所有域名访问res.setHeader("Access-Control-Allow-Origin", "*")

代码很简单:

  1. 首先发送了 retry: 10000\n\n,表示意外断开了,重新连接的延迟时间。这里是 10s。如果不发送retry,chrome 会默认 5s
  2. 每隔三秒向浏览器发送消息,虽然调用了两次 write,但也是发送一条消息。
  3. 当客户端主动关闭连接后,会清除定时器

前端代码:

index.html:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <title>SSE 示例</title>
  </head>
  <body>
    <h1>服务器发送的消息:</h1>
    <ul id="messages"></ul>

    <script>
      const eventSource = new EventSource("http://localhost:3000/events");

      eventSource.onmessage = function (event) {
        console.log("event", event);
        const data = JSON.parse(event.data);
        const li = document.createElement("li");
        li.textContent = `[${new Date(
          data.timestamp
        ).toLocaleTimeString()}] ${data.message}`;
        document.getElementById("messages").appendChild(li);
      };

      eventSource.onerror = function (err) {
        console.error("EventSource 失败:", err);
        eventSource.close();
      };
    </script>
  </body>
</html>

启动前后端:

后端:

shell 复制代码
node index.js

前端:

shell 复制代码
http-server index.html

效果:

前端每收到一条消息,就会在页面上输出。同时在 network 的控制面板,也可以看到 eventStream 的消息流。

拓展:

测试完毕。上面就是SSE通信最基础的用法。

下面深入讲讲 SSE通信 的用法:

event-stream 的每条消息都是以\n\n作为分割符的

上面后端代码中,第二次调用 write 时,消息末尾是\n\n, 所以连同前一个 write的内容,视为一条消息。

消息格式:key: '...'\n 。这样的格式视为一个键值对

js 复制代码
res.write('key: content');

其中content,只允许是字符串。如果想向浏览器发送 json,务必先转成字符串。

下面着重讲讲 key

目前允许的 key 只有三种:ideventdata,这三个 key 对应着下面event-stream中的 IdTypeData

在一条消息中,其中只有 data 不能缺失。其他的两个 key 都可以缺失。

id 在网络重连的时候很有用,如果缺失了 id,不影响浏览器接收消息。

event 指定了消息的 Type,如果 event 缺失,那么 Type 默认是 message,如果指定了其他值,比如man,效果就是下面这样:

javascript 复制代码
res.write(`event: man\n`);

这个时候,前端接收消息就不能使用:

javascript 复制代码
eventSource.onmessage = function (event) {
  const data = JSON.parse(event.data);
  ...
};

而是要:

javascript 复制代码
eventSource.addEventListener("man", (event) => {
  const message = JSON.parse(event.data).message;
});

没错!Type 可以用来标识消息类型,就好像触发了不同的事件。

如果我想将 key 设置为 name,会发生什么?答案是什么也不会发生,SSE 协议会忽略除上面三种之外的所有自定义 key!!

字段类型 客户端是否处理 解决方案
<font style="color:rgba(0, 0, 0, 0.86);">data</font> ✅ 解析为 <font style="color:rgba(0, 0, 0, 0.86);">event.data</font> 直接使用 <font style="color:rgba(0, 0, 0, 0.86);">data</font>
<font style="color:rgba(0, 0, 0, 0.86);">event</font> ✅ 触发特定监听事件 定义 <font style="color:rgba(0, 0, 0, 0.86);">event</font> + 监听对应事件
<font style="color:rgba(0, 0, 0, 0.86);">id</font>/<font style="color:rgba(0, 0, 0, 0.86);">retry</font> ✅ 用于连接管理 按需使用
自定义字段 完全忽略

总结

这篇文章讲了 SSE 通信的简单用法,并且拓展讲了 SSEkey 使用时,要注意的点。大家可以在本地多尝试,加深印象

相关推荐
程序员黄同学18 分钟前
解释 TypeScript 中的枚举(enum),如何使用枚举定义一组常量?
javascript·ubuntu·typescript
Xlbb.24 分钟前
SpiderX:专为前端JS加密绕过设计的自动化工具
前端·javascript·自动化
uhakadotcom26 分钟前
uvloop让你的异步代码速度提升400%,实战讲解与代码示例
后端·面试·github
beibeibeiooo30 分钟前
【ES6】01-ECMAScript基本认识 + 变量常量 + 数据类型
前端·javascript·ecmascript·es6
前端南玖1 小时前
深入理解Base64编码原理
前端·javascript
玩转4G物联网2 小时前
玩转物联网-4G模块如何快速将数据上传到巴法云(TCP篇)
物联网·网络协议·tcp/ip·iot·核心板·fs800dtu·巴法云
今天吃了嘛o2 小时前
vue中根据html动态渲染内容
javascript·vue.js·html
千层冷面2 小时前
RabbitMQ 全面详解(附面试重点)
分布式·面试·rabbitmq
diang2 小时前
vue3实现监听从其他页签回到当前页签重新刷新setInterval
前端·javascript
Pandaconda3 小时前
【后端开发面试题】每日 3 题(十三)
redis·分布式·后端·面试·kafka·后端开发·缓存消息队列