技术文档SSE(Server-Sent Events)
SSE(Server-Sent Events)
SSE前端
内容
什么是SSE(Server Send Events)
EventStream 一个字一个字把消息推送给前端,不必等待整个响应完成后再处理它
- 客户端不使用xhr/fetch,使用特定的API:EventSource
- 服务器Content-Type:text/event-stream
- 消息格式:"data:xxx\n\n"
SSE vs WebSocket
SSE和WebSocket作用相似,都是建立浏览器与服务器之间的通信渠道,服务器向浏览器推送消息。
| | sse | socket | | ---- | ----------------- | -------- | | 通信方式 | 单向通信(只能服务器向浏览器发送) | 全双工,双向通信 | | 协议 | http get | ws | | 跨域 | 不能跨域 | 可以跨域 |
- SSE 使用 HTTP 协议,现有的服务器软件都支持。WebSocket 是一个独立协议。
- SSE 属于轻量级,使用简单;WebSocket 协议相对复杂。
- SSE 默认支持断线重连,WebSocket 需要自己实现。
- SSE 一般只用来传送文本,二进制数据需要编码后传送,WebSocket 默认支持传送二进制数据。
- SSE 支持自定义发送的消息类型。
SSE客户端与服务器实现
客户端-EventSource
SSE 的客户端 API 部署在EventSource对象上。
const eventSource = new EventSource('your-server-url');
// 处理连接打开
eventSource.onopen = function(event) {
console.log('Connection established');
};
// 处理接收到的消息
eventSource.onmessage = function(event) {
console.log('Received message: ', event.data);
};
// 处理错误
eventSource.onerror = function(event) {
console.log('Error occurred:', event);
};
//关闭事件
eventSource.close();
EventSource常用事件
EventSource 对象本身有几个常用的事件:
onopen:- 当与服务器的连接成功建立时触发。可以用来处理连接建立时的逻辑。
- 例子:
eventSource.onopen = function(event) { console.log("Connection established!"); };
onmessage:- 这是最常见的事件。当从服务器收到消息时触发。消息内容通常在
event.data中。 - 例子:
eventSource.onmessage = function(event) { console.log("Received message: ", event.data); };
- 这是最常见的事件。当从服务器收到消息时触发。消息内容通常在
onerror:- 当发生错误时触发。通常用于处理连接错误或断开等异常情况。
- 例子:
eventSource.onerror = function(event) { console.log("Error occurred!"); };
自定义事件
浏览器对 SSE 的foo事件进行监听。如何实现服务器发送foo事件,请看下文。
source.addEventListener('foo', function (event) {
var data = event.data;
// handle message
}, false);
服务器
数据格式-请求头
服务器向浏览器发送的 SSE 数据,必须是 UTF-8 编码的文本,具有如下的 HTTP 头信息。
第一行的Content-Type必须指定 MIME 类型为event-steam。
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
"access-control-allow-origin": "*",
});
每一次发送的信息,由若干个message组成,每个message之间用\n\n分隔。每个message内部由若干行组成,每一行都是如下格式:
[field]:value\n
数据格式-field
上面的field可以取四个值:
- data
- 传输的实际数据内容。
- 如果数据很长,可以分成多行,最后一行用
\n\n结尾,前面行都用\n结尾。
- event
- 事件类型,用于区分不同的事件。默认是
message事件。 - 浏览器可以用
addEventListener()监听自定义事件。 - 下面的代码创造了三条信息。第一条的名字是
foo,触发浏览器的foo事件;第二条未取名,表示默认类型,触发浏览器的message事件;第三条是bar,触发浏览器的bar事件。
- 事件类型,用于区分不同的事件。默认是
event: foo\n
data: a foo event\n\n
data: an unnamed event\n\n
event: bar\n
data: a bar event\n\n
- id:事件标识符,用于重连时避免重复接收消息。
- retry:连接中断后客户端等待的重试时间。
retry:10000\n
注释
此外,还可以有冒号开头的行,表示注释。通常,服务器每隔一段时间就会向浏览器发送一个注释,保持连接不中断。
:this is a comment
数据传输例子
: this is a test stream\n\n
data: some text\n\n
data: another message\n
data: with two lines \n\n
代码实现
client.html,client.js,server.js实现客户端监听事件,服务端流式发送1-20个数字并自动结束connect
- client.html vscode 右键:open with live server
<!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>
<div id="messages"></div>
<script src="client.js"></script>
</body>
</html>
- client.js 就是html里面的js部分
const eventSource = new EventSource('http://localhost:3000/sse');
const messages=document.getElementById("messages");
eventSource.onmessage = function(event) {
console.log(event.data);
messages.innerHTML += `${event.data}`;
};
eventSource.addEventListener('close', function(event) {
console.log('Connection closed');
eventSource.close();
});
- server.js
import http from 'http';
const server = http.createServer((req, res) => {
if (req.url==="/sse"){
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
"access-control-allow-origin": "*",
});
let counter = 0;
const timer = setInterval(() => {
if (counter > 20) {
res.write('event: close\n');
res.write('data: Connection closed\n\n');
return
}
res.write(`data: ${counter}\n\n`);
console.log(`Sent: ${counter}`);
counter++;
},100);
req.on('close', () => {
clearInterval(timer);
console.log('Client disconnected');
});
}
});
server.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
Reference
- HTTP+SSE 将逐步被替换为 Streamable HTTP
- bilibili-从0实现一个SSE+MCP服务器
- https://github.com/KelvinQiu802/mcp-sse
- 阮一峰blog-SSE
- chatgpt stream 实时输出功能实现原理 SSE(Server-send events)
- openai api-reference
- openai chat stream