一、SSE 简介
Server-Sent Events 是 HTML5 中定义的一种服务器推送技术,它基于 HTTP 协议,实现了服务器向客户端单向推送数据的功能。与传统的轮询方式相比,SSE 能够更高效地实时更新数据,减少了不必要的网络请求和延迟。
Server-Sent Events(SSE)是一种基于 HTTP 协议的单向通信技术,允许服务器向客户端推送实时更新。SSE 通过保持 HTTP 连接并持续发送数据来实现实时通信,适用于需要频繁更新但不需要客户端频繁响应的场景。
二、工作原理
- 客户端创建一个
EventSource对象,并指定要连接的服务器端 URL。 - 服务器端在响应中设置特定的头信息,包括
Content-Type为text/event-stream,以及其他相关的缓存控制头。 - 服务器可以随时向客户端发送数据,数据以事件的形式发送,每个事件包含数据和事件类型。
- 客户端通过
EventSource对象的onmessage回调函数接收服务器发送的事件,并进行相应的处理。
三、示例代码
客户端实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Disabled Input Placeholder</title>
<style>
</style>
</head>
<body>
<script>
if (typeof (EventSource) !== "undefined") {
var source = new EventSource("http://127.0.0.1:3000/server-endpoints");
source.onmessage = function (event) {
console.log("New message: " + event.data);
};
source.onerror = function (event) {
console.error("Error occurred: ", event);
};
} else {
console.log("SSE not supported in this browser.");
}
</script>
</body>
</html>
代码解读
1. 检查浏览器支持:首先检查浏览器是否支持 EventSource 对象。如果不支持,输出一条消息。
if (typeof(EventSource) !== "undefined") {
2. 创建 EventSource 实例:如果浏览器支持 EventSource,创建一个新的 EventSource 实例,传入服务器端的 SSE 端点 URL。
var source = new EventSource("http://127.0.0.1:3000/server-endpoints");
3. 处理消息事件:为 source 对象的 onmessage 事件添加一个处理函数,当服务器发送新消息时,这个函数会被调用。
source.onmessage = function (event) {
console.log("New message: " + event.data);
};
4. 处理错误事件:为 source 对象的 onerror 事件添加一个处理函数,当发生错误时,这个函数会被调用。
source.onerror = function (event) {
console.error("Error occurred: ", event);
};
服务端实现
const express = require('express');
const app = express();
app.get('/server-endpoints', (req, res) => {
// 设置响应头的 Content-Type 为 text/event-stream,表示服务器发送事件流
res.setHeader('Content-Type', 'text/event-stream');
// 设置响应头的 Cache-Control 为 no-cache,防止缓存
res.setHeader('Cache-Control', 'no-cache');
// 设置响应头的 Connection 为 keep-alive,保持连接活跃
res.setHeader('Connection', 'keep-alive');
// 定义发送事件的函数
const sendEvent = (data) => {
// 以特定格式将数据写入响应流
res.write(`data: ${JSON.stringify(data)}\n\n`);
};
// 模拟实时数据,每隔 1000 毫秒发送一个事件
const intervalId = setInterval(() => {
sendEvent({ message: 'Hello, SSE!' });
}, 1000);
// 监听请求的 close 事件,当客户端关闭连接时,清除定时器
req.on('close', () => {
clearInterval(intervalId);
});
});
// 启动服务器,监听 3000 端口
app.listen(3000, () => {
console.log('Server running on port 3000');
});
代码解读
1. 定义 SSE 端点:定义一个 GET 请求的处理函数,当客户端请求 /server-endpoint 时触发。。
app.get('/server-endpoint', (req, res) => {
2. 设置响应头:设置响应头以告知客户端这是一个 SSE 流。。
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
(1) res.setHeader('Content-Type', 'text/event-stream');
这行代码设置了响应头的 Content-Type 为 text/event-stream,这是 SSE(Server-Sent Events)流的 MIME 类型。它告诉客户端(通常是浏览器),服务器将发送的是一个事件流,而不是普通的 HTML、JSON 或其他类型的内容。
- 作用:告知客户端这是一个 SSE 流,客户端会根据这个 MIME 类型来处理接收到的数据。
- 重要性:这是实现 SSE 的关键步骤之一,客户端需要知道如何解析和处理从服务器接收到的数据。
(2) res.setHeader('Cache-Control', 'no-cache');
这行代码设置了 Cache-Control 头为 no-cache,目的是防止浏览器或其他中间代理缓存 SSE 流。
- 作用:确保每次请求都能从服务器获取最新的数据,而不是从缓存中读取。
- 重要性:实时数据传输要求数据是最新的,缓存可能会导致客户端接收到过时的数据,从而影响实时性。
(3) res.setHeader('Connection', 'keep-alive');
这行代码设置了 Connection 头为 keep-alive,目的是保持 HTTP 连接不断开。
- 作用:保持客户端和服务器之间的连接持续打开,以便服务器可以持续发送数据。
- 重要性:SSE 依赖于持久的 HTTP 连接来推送实时数据,如果连接关闭,SSE 流就会中断。
3. 定义发送事件的函数:定义一个 sendEvent 函数,用于向客户端发送数据。数据格式为 data: <data>\n\n。
const sendEvent = (data) => {
res.write(`data: ${JSON.stringify(data)}\n\n`);
};
4. 模拟实时数据:使用 setInterval 模拟每秒发送一次数据给客户端。
const intervalId = setInterval(() => {
sendEvent({ message: 'Hello, SSE!' });
}, 1000);
5. 处理连接关闭:当客户端关闭连接时,清除定时器以释放资源。
req.on('close', () => { clearInterval(intervalId); });
四、SSE 的优缺点
优点
- 简单易用:SSE 使用标准的 HTTP 协议,客户端和服务器端的实现都非常简单。
- 自动重连:
EventSource对象会在连接断开时自动尝试重连。 - 轻量级:相比 WebSocket,SSE 更加轻量级,适合频繁但数据量较小的更新。
缺点
- 单向通信:SSE 只能从服务器向客户端推送数据,无法实现双向通信。
- 浏览器兼容性:虽然大多数现代浏览器都支持 SSE,但仍有一些老旧浏览器不支持。
- 连接数限制:由于 SSE 基于 HTTP 协议,浏览器对同一域名的并发连接数有限制。
五、适用场景
SSE 非常适合以下场景:
- 实时通知:如新闻网站的实时更新、社交媒体的通知等。
- 数据流:如股票市场的实时数据、物联网设备的数据流等。
- 日志监控:如服务器日志的实时监控、应用程序的实时日志等。