背景
早期去玩了一下各个Ai厂商的免费额度(主要是国内的),虽然不是很给力,但是还是蛮好玩的。
建立长连接我们通常使用WebSocket,而对于流式数据发送,只需要服务器返回数据,而不需要客户端发送数据的情况下,SSE是一个不错的选择。
介绍
SSE(Server-Sent Events)。
数据格式大致如下,如果不写明event,那么默认为message事件。
\n
是必须的,可以看看阮一峰的文章,讲得比较详细。
https://www.ruanyifeng.com/blog/2017/05/server-sent_events.html
id: 12\n
event: myEvent\n
retry: 10000\n
data: {name: zhangsan, age: 18, sex: male}\n\n
demo
node服务端
const http = require("http");
const fs = require("fs");
http
.createServer((req, res) => {
const url = req.url;
if (url.includes("/sse")) {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
"Access-Control-Allow-Origin": "*", // 允许跨域
});
// 每隔 1 秒发送一条消息
let id = 0;
const intervalId = setInterval(() => {
// 这是我们想要返回的数据
const data = {
id,
time: new Date(),
body: "哈喽",
};
res.write(`id: ${id}\n\n`);
res.write("event: message\n\n");
res.write("retry: 10000\n\n");
res.write("data: " + JSON.stringify(data) + "\n\n");
console.log("当前id是: ", id);
// 0到4,发送5条消息打算关闭连接
if (id >= 4) {
clearInterval(intervalId);
res.write(`id: ${id}\n`);
res.write("event: close\n");
res.write("retry: 10000\n");
res.write("data: " + JSON.stringify(data) + "\n\n");
console.log("服务端发送完毕,请求关闭");
res.end();
}
id++;
}, 1000);
// 当客户端关闭连接时停止发送消息
req.on("close", () => {
clearInterval(intervalId);
id = 0;
res.end();
});
} else {
// 如果请求的路径无效,返回 404 状态码
res.writeHead(404);
res.end();
}
})
.listen(3001);
console.log("Server listening on port 3001");
客户端
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
let eventSource;
// 断开 SSE 连接
const closeSSE = () => {
eventSource.close();
console.log(`SSE 连接关闭,状态${eventSource.readyState}`);
};
// 建立 SSE 连接
const connectSSE = () => {
eventSource = new EventSource("http://127.0.0.1:3001/fetch-sse");
eventSource.onopen = () => {
console.log(`SSE 连接成功,状态${eventSource.readyState}`);
};
eventSource.onerror = () => {
console.log(`SSE 连接错误,状态${eventSource.readyState}`);
eventSource.close();
};
eventSource.onmessage = (event) => {
console.log('将字符串转化为json对象:',JSON.parse(event.data))
};
eventSource.addEventListener("close", (event) => {
console.log('close事件: ',event);
closeSSE();
});
};
connectSSE()
</script>
</body>
</html>
测试
服务端文件为server.js,在当前文件夹打开终端,输入如下命令可以开启服务器。
node ./server.js
使用live-server等方式,打开index.html,这个应该都熟悉。
打开浏览器,打开控制台
注意
可以看到eventSource对象没有onclose钩子,因此存在一些问题。
当服务端发送完消息后,断开连接,而客户端却认为消息没发送完,于是重连,这样会造成不断的重连,而且还会判定为error,触发onerror钩子。
解决重连问题
解决这个方法,我们可以自定义一个close事件,让服务端发送消息,提醒客户端应该断开连接。
以下为关键代码
// 服务端
res.write("event: close\n");
res.write("data: " + JSON.stringify(data) + "\n\n");
注意closeSSE是前面自定义的方法,并不是标准API
// 客户端
eventSource.addEventListener("close", (event) => {
console.log("close事件: ", event);
closeSSE();
});
效果
结语
当初踩了些坑,希望之后能少踩一点。
标签:write,res,流式,id,发送,eventSource,SSE,close,event From: https://www.cnblogs.com/oldsaltfish/p/18404647