技术文档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 对象本身有几个常用的事件:

  1. onopen:
    • 当与服务器的连接成功建立时触发。可以用来处理连接建立时的逻辑。
    • 例子:eventSource.onopen = function(event) { console.log("Connection established!"); };
  2. onmessage:
    • 这是最常见的事件。当从服务器收到消息时触发。消息内容通常在 event.data 中。
    • 例子:eventSource.onmessage = function(event) { console.log("Received message: ", event.data); };
  3. 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

  1. 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>
  1. 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();

});
  1. 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