什么是服务器发送事件?
TL;DR
服务器发送事件 (SSE) 是一种标准,允许网页通过 HTTP 连接从服务器接收自动更新。服务器发送事件与 EventSource
实例一起使用,该实例打开与服务器的连接,并允许客户端从服务器接收事件。服务器发送事件创建的连接是持久的(类似于 WebSocket
),但是有一些区别:
属性 | WebSocket | EventSource |
---|---|---|
方向 | 双向 – 客户端和服务器都可以交换消息 | 单向 – 只有服务器发送数据 |
数据类型 | 二进制和文本数据 | 仅文本数据 |
协议 | 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 的工作原理
-
客户端创建一个新的
EventSource
对象,传递将生成事件流的服务器端
脚本的 URL:const eventSource = new EventSource('/event-stream'); -
服务器端脚本设置适当的标头以指示它将发送事件流 (
Content-Type: text/event-stream
),然后开始向客户端发送事件。 -
服务器发送的每个事件都遵循特定的格式,包含
event
、data
和id
等字段。例如:event: messagedata: Hello, world!event: updateid: 123data: {"temperature": 25, "humidity": 60} -
在客户端,
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));}); -
如果连接断开,
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 objectconst eventSource = new EventSource('/sse');// Event listener for receiving messageseventSource.onmessage = function (event) {console.log('New message:', event.data);};// Event listener for errorseventSource.onerror = function (error) {console.error('Error occurred:', error);};// Optional: Event listener for open connectioneventSource.onopen = function () {console.log('Connection opened');};
在服务器上:
const http = require('http');http.createServer((req, res) => {if (req.url === '/sse') {// Set headers for SSEres.writeHead(200, {'Content-Type': 'text/event-stream','Cache-Control': 'no-cache',Connection: 'keep-alive',});// Function to send a messageconst sendMessage = (message) => {res.write(`data: ${message}\n\n`); // Messages are delimited with double line breaks.};// Send a message every 5 secondsconst intervalId = setInterval(() => {sendMessage(`Current time: ${new Date().toLocaleTimeString()}`);}, 5000);// Handle client disconnectreq.on('close', () => {clearInterval(intervalId);res.end();});} else {res.writeHead(404);res.end();}}).listen(8080, () => {console.log('SSE server running on port 8080');});
总结
服务器发送事件提供了一种高效且直接的方式,可以将更新从服务器推送到客户端。它们特别适用于需要连续数据流但不需要完全双向通信的应用程序。由于现代浏览器内置了支持,SSE 是许多实时 Web 应用程序的可靠选择。