测验

什么是服务器发送事件?

主题
JavaScript网络
在GitHub上编辑

TL;DR

服务器发送事件 (SSE) 是一种标准,允许网页通过 HTTP 连接从服务器接收自动更新。服务器发送事件与 EventSource 实例一起使用,该实例打开与服务器的连接,并允许客户端从服务器接收事件。服务器发送事件创建的连接是持久的(类似于 WebSocket),但是有一些区别:

属性WebSocketEventSource
方向双向 – 客户端和服务器都可以交换消息单向 – 只有服务器发送数据
数据类型二进制和文本数据仅文本数据
协议WebSocket 协议 (ws://)常规 HTTP (http://)

创建事件源

const eventSource = new EventSource('/sse-stream');

监听事件

// 建立连接时触发。
eventSource.addEventListener('open', () => {
console.log('连接已打开');
});
// 从服务器收到消息时触发。
eventSource.addEventListener('message', (event) => {
console.log('收到消息:', event.data);
});
// 发生错误时触发。
eventSource.addEventListener('error', (error) => {
console.error('发生错误:', error);
});

从服务器发送事件

const express = require('express');
const app = express();
app.get('/sse-stream', (req, res) => {
// `Content-Type` 需要设置为 `text/event-stream`。
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// 每条消息都应以 data 开头。
const sendEvent = (data) => res.write(`data: ${data}\n\n`);
sendEvent('来自服务器的问候');
const intervalId = setInterval(() => sendEvent(new Date().toString()), 1000);
res.on('close', () => {
console.log('客户端关闭连接');
clearInterval(intervalId);
});
});
app.listen(3000, () => console.log('服务器已在端口 3000 启动'));

在此示例中,服务器最初发送“来自服务器的问候”消息,然后每秒发送当前日期。连接保持活动状态,直到客户端将其关闭


服务器发送事件 (SSE)

服务器发送事件 (SSE) 是一种标准,允许服务器通过单个、长连接的 HTTP 连接将更新推送到 Web 客户端。它支持实时更新,而无需客户端不断轮询服务器以获取新数据。

SSE 的工作原理

  1. 客户端创建一个新的 EventSource 对象,传递将生成事件流的 服务器端 脚本的 URL:

    const eventSource = new EventSource('/event-stream');
  2. 服务器端脚本设置适当的标头以指示它将发送事件流 (Content-Type: text/event-stream),然后开始向客户端发送事件。

  3. 服务器发送的每个事件都遵循特定的格式,包含 eventdataid 等字段。例如:

    event: message
    data: Hello, world!
    event: update
    id: 123
    data: {"temperature": 25, "humidity": 60}
  4. 在客户端,EventSource 对象接收这些事件并将其作为浏览器事件分派,可以使用事件侦听器或 onmessage 事件处理程序来处理这些事件:

    eventSource.onmessage = function (event) {
    console.log('Received message:', event.data);
    };
    eventSource.addEventListener('update', function (event) {
    console.log('Received update:', JSON.parse(event.data));
    });
  5. 如果连接断开,EventSource 对象会自动处理重新连接,并且可以使用 Last-Event-ID HTTP 标头 从上次接收到的事件 ID 恢复事件流。

SSE 功能

  • 单向:只有服务器才能向客户端发送数据。对于双向通信,Web 套接字将更合适。
  • 重试机制:如果连接失败,客户端将重试连接,重试间隔由服务器的 retry: 字段指定。
  • 仅文本数据:SSE 只能传输文本数据,这意味着在传输之前需要对二进制数据进行编码(例如,Base64)。对于需要传输大型二进制有效负载的应用程序,这可能会导致开销增加和效率低下。
  • 内置浏览器支持:受大多数现代浏览器支持,无需其他库。
  • 事件类型:SSE 使用 event: 字段支持自定义事件类型,允许对消息进行分类。
  • Last-Event-Id:客户端在重新连接时发送 Last-Event-Id 标头,允许服务器从上次接收到的事件恢复流。但是,没有内置的机制来重放断开连接期间错过的事件。您可能需要实现一种机制来处理错过的事件,例如使用 Last-Event-Id 标头。
  • 连接限制:浏览器对并发 SSE 连接的最大数量有限制,通常每个域大约 6 个。如果需要从同一客户端建立多个 SSE 连接,这可能会成为瓶颈。使用 HTTP/2 将缓解此问题。

在 JavaScript 中实现 SSE

以下代码演示了客户端和服务器上 SSE 的最小实现:

  • 服务器设置适当的标头以建立 SSE 连接。
  • 消息每 5 秒发送到客户端。
  • 当客户端断开连接时,服务器清理间隔并结束响应。

在客户端上:

// Create a new EventSource object
const eventSource = new EventSource('/sse');
// Event listener for receiving messages
eventSource.onmessage = function (event) {
console.log('New message:', event.data);
};
// Event listener for errors
eventSource.onerror = function (error) {
console.error('Error occurred:', error);
};
// Optional: Event listener for open connection
eventSource.onopen = function () {
console.log('Connection opened');
};

在服务器上:

const http = require('http');
http
.createServer((req, res) => {
if (req.url === '/sse') {
// Set headers for SSE
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
});
// Function to send a message
const sendMessage = (message) => {
res.write(`data: ${message}\n\n`); // Messages are delimited with double line breaks.
};
// Send a message every 5 seconds
const intervalId = setInterval(() => {
sendMessage(`Current time: ${new Date().toLocaleTimeString()}`);
}, 5000);
// Handle client disconnect
req.on('close', () => {
clearInterval(intervalId);
res.end();
});
} else {
res.writeHead(404);
res.end();
}
})
.listen(8080, () => {
console.log('SSE server running on port 8080');
});

总结

服务器发送事件提供了一种高效且直接的方式,可以将更新从服务器推送到客户端。它们特别适用于需要连续数据流但不需要完全双向通信的应用程序。由于现代浏览器内置了支持,SSE 是许多实时 Web 应用程序的可靠选择。

延伸阅读

在GitHub上编辑