HTML5 - Server-Sent Events
HTML5 - Server-Sent Events (SSE)
Section titled “HTML5 - Server-Sent Events (SSE)”Server-Sent Events (SSE) 是一种标准,使 Web 服务器能够通过单个、持久的 HTTP 连接,将数据更新推送到 Web 客户端(浏览器)。与 WebSockets(双向)不同,SSE 是单向的:数据只从服务器流向客户端。
这使得 SSE 成为 WebSockets 的更简单替代方案,特别是在你只需服务器向客户端发送更新的场景下,例如实时新闻源、股票行情、状态更新、通知或监控面板。
SSE 使用标准的 HTTP 连接和特定的基于文本的事件流格式,这使得它在某些服务器基础设施上可能比 WebSockets 更易于实现,并且在可能干扰 WebSocket 连接的代理中更具弹性。
客户端实现 (JavaScript)
Section titled “客户端实现 (JavaScript)”在客户端,你使用 EventSource 接口来连接到 SSE 端点。
// 创建一个 EventSource 实例,连接到服务器端点const source = new EventSource('/sse-endpoint'); // 替换为你的实际端点 URL如果连接丢失,浏览器将自动尝试重新连接。
你使用附加到 EventSource 对象的事件监听器来监听消息:
onopen: 当与服务器的连接成功建立时触发。onmessage: 当从服务器接收到没有特定事件名称的消息时触发。事件对象的data属性包含消息载荷。onerror: 如果发生错误(例如,连接失败)时触发。注意:初始时不会对像 404 或 500 这样的 HTTP 错误状态码触发,只有在连接断开时才会触发。- 自定义事件:如果服务器发送带有特定名称的事件(使用
event:字段),你可以使用addEventListener('eventName', handler)来监听它们。
<!DOCTYPE html><html><head> <title>Server-Sent Events Example</title></head><body> <h1>Server Time Updates (SSE)</h1> <div id="timeDisplay">Waiting for server time...</div> <div id="status">Connecting...</div>
<script> const timeDisplay = document.getElementById('timeDisplay'); const statusDiv = document.getElementById('status');
// 检查浏览器支持 if (typeof(EventSource) !== "undefined") { // 将 '/get-time-sse' 替换为你的实际服务器端点 const source = new EventSource('/get-time-sse');
statusDiv.textContent = 'Connecting to event source...';
// 连接打开时调用 source.onopen = function(event) { statusDiv.textContent = 'Connection opened!'; console.log("SSE Connection opened.", event); };
// 对于没有特定事件名称的消息调用(默认) source.onmessage = function(event) { // 假设服务器以纯文本形式在数据字段中发送时间 timeDisplay.textContent = `Server time: ${event.data}`; console.log("SSE Message received:", event.data); };
// 示例:监听自定义命名事件 'userUpdate' source.addEventListener('userUpdate', function(event) { console.log("Custom 'userUpdate' event received:", event.data); // 处理用户更新数据(例如,解析 JSON) // const userData = JSON.parse(event.data); // updateUI(userData); }, false);
// 发生错误时调用(例如,连接丢失) source.onerror = function(event) { statusDiv.textContent = 'Connection error. Reconnecting...'; console.error("SSE Error occurred:", event); // 浏览器将自动尝试重新连接。 // 如果服务器明确关闭连接(例如,HTTP 204), // 或者如果 readyState 变为 CLOSED,你可能想要停止重新连接: if (source.readyState === EventSource.CLOSED) { statusDiv.textContent = 'Connection closed permanently by server.'; source.close(); // 确保客户端也关闭它 } };
} else { statusDiv.textContent = 'Sorry, your browser does not support Server-Sent Events.'; } </script></body></html>你可以使用 close() 方法从客户端明确关闭连接:
// 关闭连接source.close();console.log("SSE connection closed by client.");服务器端实现
Section titled “服务器端实现”服务器端脚本需要:
- 设置
Content-Type头部为text/event-stream。 - 设置适当的缓存头部(例如,
Cache-Control: no-cache)以防止中间代理缓存响应。 - 保持连接打开。
- 根据 SSE 规范格式化数据并发送。
SSE 消息格式
Section titled “SSE 消息格式”消息是纯文本,由两个换行符 (\n\n) 分隔。每条消息可以包含一个或多个字段,每个字段以单个换行符 (\n) 结束。常见的字段有:
data: <消息内容>:事件的实际数据载荷。如果每行都以data:开头,则可以跨多行。event: <事件名称>:指定事件的名称。如果省略,消息将在客户端触发onmessage处理器。id: <唯一ID>:为事件设置一个唯一的 ID。如果连接中断,浏览器将发送一个带有最后接收到的 ID 的Last-Event-IDHTTP 头部,允许服务器恢复流。retry: <毫秒>:指定连接丢失时客户端应等待多长时间(以毫秒为单位)再尝试重新连接。
概念性的 Node.js 服务器示例
Section titled “概念性的 Node.js 服务器示例”这个简单的 Node.js 示例每秒发送一次当前服务器时间。
// server.js (Node.js 示例)const http = require('http');
const server = http.createServer((req, res) => { // SSE 端点的路由 if (req.url === '/get-time-sse' && req.method === 'GET') {
// 设置 SSE 的头部 res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', // 重要:保持连接打开 'Access-Control-Allow-Origin': '*' // 可选:如果需要,添加 CORS 头部 });
console.log('Client connected to SSE endpoint.');
// 立即发送一条初始消息(可选) res.write(`data: Server connected at ${new Date().toLocaleTimeString()}\n\n`);
// 每秒发送时间更新 const intervalId = setInterval(() => { const currentTime = new Date().toLocaleTimeString(); // 简单消息(触发 'onmessage') res.write(`data: ${currentTime}\n\n`);
// 发送命名事件和 ID 的示例 // res.write(`id: ${Date.now()}\n`); // res.write(`event: serverTimeUpdate\n`); // res.write(`data: ${JSON.stringify({ time: currentTime })}\n\n`);
}, 1000);
// 处理客户端断开连接 req.on('close', () => { console.log('Client disconnected from SSE endpoint.'); clearInterval(intervalId); // 停止发送更新 res.end(); // 结束响应 });
} else { // 处理其他请求(例如,提供 HTML 页面) res.writeHead(404); res.end('Not Found'); }});
const PORT = 3000;server.listen(PORT, () => { console.log(`Server running at http://localhost:${PORT}`);});要运行此代码,请将其保存为 server.js 并运行 node server.js。然后,访问 HTML 页面(你需要单独提供该页面或添加另一个路由),并将其 EventSource URL 更新为 http://localhost:3000/get-time-sse。
SSE vs. WebSockets
Section titled “SSE vs. WebSockets”- 方向性: SSE 仅从服务器到客户端;WebSockets 是双向的。
- 协议: SSE 使用标准的 HTTP(S);WebSockets 在初始 HTTP 握手后使用不同的协议(ws/wss)。
- 复杂度: SSE 通常被认为实现更简单,尤其是在客户端。
- 开销: SSE 通常比 WebSockets 的每条消息协议开销更少。
- 代理/防火墙兼容性: SSE 通常能更好地穿透可能阻止 WebSocket 连接的限制性代理。
- 内置特性: SSE 内置支持自动重连和事件 ID,这些在 WebSockets 中需要手动实现。
当你主要需要从服务器向客户端推送更新时,选择 SSE。需要完整的实时双向通信时,选择 WebSockets。