pin_drop当前位置:知识文库 ❯ 图文
HTML5 API:HTML5 Server-Sent Events - 完整教程与代码示例
一、教程简介
Server-Sent Events(SSE)是 HTML5 提供的一种基于 HTTP 协议的服务器推送技术,允许服务器通过持久化的 HTTP 连接向客户端单向推送数据。与 WebSocket 的双向通信不同,SSE 专注于服务器到客户端的单向实时数据流,天然支持自动重连、事件类型区分和消息 ID 追踪。SSE 特别适用于实时通知推送、股票行情、新闻动态、日志流等场景,是构建实时 Web 应用的轻量级方案。
二、核心概念
SSE 与 WebSocket 对比
SSE 数据格式
服务器发送的数据必须遵循特定的文本格式:
代码示例
field: value\n常用字段:
消息格式规则:
-
每条消息以空行(
\n\n)分隔 -
data字段可以多行,多行data之间用\n连接 -
以
:开头的行是注释,会被忽略
代码示例
data: 第一行数据\n
data: 第二行数据\n
\n
event: update\n
data: {"price": 100}\n
id: 42\n
\n
: 这是注释\n
retry: 5000\n
\nEventSource 对象
代码示例
const eventSource = new EventSource(url, options);三、语法与用法
创建连接
代码示例
// 基本连接
const eventSource = new EventSource('/api/sse');
// 带凭据的连接
const eventSource = new EventSource('/api/sse', {
withCredentials: true
});接收消息
代码示例
// 接收默认 message 事件
eventSource.onmessage = function(e) {
console.log('数据:', e.data);
console.log('事件:', e.type);
console.log('ID:', e.lastEventId);
};
// 接收自定义事件
eventSource.addEventListener('update', function(e) {
const data = JSON.parse(e.data);
console.log('更新:', data);
});
eventSource.addEventListener('notification', function(e) {
console.log('通知:', e.data);
});连接状态与关闭
代码示例
// 监听连接打开
eventSource.onopen = function() {
console.log('连接已建立');
};
// 监听错误
eventSource.onerror = function(e) {
console.log('连接错误,状态:', eventSource.readyState);
// readyState 为 0 表示正在自动重连
// readyState 为 2 表示连接已关闭
};
// 关闭连接
eventSource.close();服务器端实现(Node.js 示例)
代码示例
// Node.js 服务端示例
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*'
});
let id = 0;
const interval = setInterval(() => {
id++;
res.write('id: ' + id + '\n');
res.write('data: ' + JSON.stringify({ time: new Date().toISOString(), value: Math.random() }) + '\n\n');
}, 1000);
req.on('close', () => {
clearInterval(interval);
});
}).listen(3000);四、代码示例
示例一:SSE 模拟实时数据推送
模拟 SSE 服务器推送,展示实时消息接收和自动重连机制。支持多种事件类型(update、alert、info),展示消息流、连接状态和统计信息。
示例二:SSE 实时日志查看器
模拟实时日志流,支持按日志级别(INFO、WARN、ERROR、DEBUG)筛选,自动滚动,展示日志查看器的典型实现方式。
五、浏览器兼容性
提示:对于不支持 SSE 的浏览器,可以使用
eventsourcepolyfill 库。
六、注意事项与最佳实践
1. 连接管理
代码示例
// 合理管理 SSE 连接生命周期
let eventSource = null;
function connectSSE() {
if (eventSource) {
eventSource.close();
}
eventSource = new EventSource('/api/sse');
eventSource.onopen = function() {
console.log('SSE 连接已建立');
};
eventSource.onerror = function() {
if (eventSource.readyState === EventSource.CLOSED) {
console.log('SSE 连接已关闭');
}
// readyState 为 CONNECTING 时会自动重连
};
eventSource.onmessage = function(e) {
console.log('收到消息:', e.data);
};
}
function disconnectSSE() {
if (eventSource) {
eventSource.close();
eventSource = null;
}
}
// 页面隐藏时断开,可见时重连
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
disconnectSSE();
} else {
connectSSE();
}
});2. 消息 ID 与断点续传
代码示例
// 利用 Last-Event-ID 实现断点续传
const eventSource = new EventSource('/api/sse');
eventSource.onmessage = function(e) {
// e.lastEventId 包含服务器发送的消息 ID
// 断线重连时,浏览器会自动在请求头中发送 Last-Event-ID
console.log('消息 ID:', e.lastEventId);
console.log('数据:', e.data);
};3. 自定义事件类型
代码示例
// 监听不同类型的事件
const eventSource = new EventSource('/api/sse');
eventSource.addEventListener('price', function(e) {
const data = JSON.parse(e.data);
updatePriceDisplay(data);
});
eventSource.addEventListener('notification', function(e) {
showNotification(e.data);
});
eventSource.addEventListener('heartbeat', function(e) {
console.log('心跳:', e.data);
});4. 安全考虑
-
SSE 连接遵循同源策略
-
跨域时需要服务器设置 CORS 头
-
敏感数据应通过 HTTPS 传输
-
注意限制连接数,避免资源浪费
七、代码规范示例
代码示例
// 推荐:封装 SSE 管理类
class SSEClient {
constructor(url, options = {}) {
this.url = url;
this.options = options;
this.eventSource = null;
this.listeners = new Map();
this.reconnectAttempts = 0;
this.maxReconnectAttempts = options.maxReconnectAttempts || 5;
}
connect() {
if (this.eventSource) {
this.disconnect();
}
this.eventSource = new EventSource(this.url, {
withCredentials: this.options.withCredentials || false
});
this.eventSource.onopen = () => {
this.reconnectAttempts = 0;
this._emit('open');
};
this.eventSource.onerror = () => {
if (this.eventSource.readyState === EventSource.CLOSED) {
this._emit('close');
} else if (this.eventSource.readyState === EventSource.CONNECTING) {
this.reconnectAttempts++;
if (this.reconnectAttempts > this.maxReconnectAttempts) {
this.disconnect();
this._emit('maxReconnect');
} else {
this._emit('reconnecting', { attempt: this.reconnectAttempts });
}
}
};
this.eventSource.onmessage = (e) => {
this._emit('message', {
data: e.data,
lastEventId: e.lastEventId,
type: e.type
});
};
// 注册自定义事件
if (this.options.events) {
Object.entries(this.options.events).forEach(([event, handler]) => {
this.on(event, handler);
});
}
}
on(event, handler) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
if (this.eventSource && event !== 'open' && event !== 'close' &&
event !== 'message' && event !== 'reconnecting' && event !== 'maxReconnect') {
this.eventSource.addEventListener(event, (e) => {
this._emit(event, { data: e.data, lastEventId: e.lastEventId });
});
}
}
this.listeners.get(event).push(handler);
}
off(event, handler) {
if (this.listeners.has(event)) {
const handlers = this.listeners.get(event);
const index = handlers.indexOf(handler);
if (index > -1) handlers.splice(index, 1);
}
}
_emit(event, data) {
if (this.listeners.has(event)) {
this.listeners.get(event).forEach(handler => handler(data));
}
}
disconnect() {
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
}
this._emit('close');
}
}
// 使用示例
const sse = new SSEClient('/api/sse', {
withCredentials: true,
maxReconnectAttempts: 10,
events: {
update: (data) => console.log('更新:', data),
alert: (data) => console.log('告警:', data)
}
});
sse.on('open', () => console.log('已连接'));
sse.on('message', (data) => console.log('消息:', data));
sse.on('reconnecting', (data) => console.log('重连中,第', data.attempt, '次'));
sse.connect();八、常见问题与解决方案
Q1:SSE 连接频繁断开重连?
原因:可能是代理服务器或负载均衡器超时断开了长连接。
解决方案:
-
服务器定期发送心跳消息保持连接活跃
-
配置代理服务器的超时时间
-
设置合理的
retry值
Q2:如何实现 SSE 的认证?
解决方案:
-
使用
withCredentials: true发送 Cookie -
或在 URL 中附带 token 参数(不太安全)
-
更好的方案是先通过 API 获取一次性 token,再用 token 连接 SSE
Q3:IE 浏览器不支持 SSE?
解决方案:使用 polyfill 库,或降级为轮询方案:
代码示例
function createEventSource(url) {
if (typeof EventSource !== 'undefined') {
return new EventSource(url);
}
// 降级为轮询
return createPollingFallback(url);
}
function createPollingFallback(url) {
let lastId = 0;
const interval = setInterval(async () => {
const response = await fetch(url + '?lastId=' + lastId);
const data = await response.json();
if (data.id > lastId) {
lastId = data.id;
// 触发回调
}
}, 3000);
return {
close: () => clearInterval(interval)
};
}Q4:SSE 能发送二进制数据吗?
解答:SSE 只支持文本数据。如需发送二进制数据,可以 Base64 编码后发送,或使用 WebSocket 替代。
九、总结
Server-Sent Events 是一种轻量级的服务器推送技术,基于标准 HTTP 协议,天然支持自动重连和消息 ID 追踪,非常适合服务器到客户端的单向实时数据推送场景。与 WebSocket 相比,SSE 更简单、更易部署,在通知推送、实时更新、日志流等场景下是更优的选择。使用时需注意连接管理(页面隐藏时断开)、心跳保活、认证安全和 IE 兼容性降级。
常见问题
SSE 连接为什么会频繁断开重连?
可能是代理服务器或负载均衡器超时断开了长连接。解决方案包括:服务器定期发送心跳消息保持连接活跃、配置代理服务器的超时时间、设置合理的 retry 值。
如何实现 SSE 的认证?
可以使用 withCredentials: true 发送 Cookie,或在 URL 中附带 token 参数。更好的方案是先通过 API 获取一次性 token,再用 token 连接 SSE。
IE 浏览器不支持 SSE 怎么办?
可以使用 eventsource polyfill 库,或降级为轮询方案。通过检测 EventSource 是否存在来决定使用 SSE 还是轮询。
SSE 能发送二进制数据吗?
SSE 只支持文本数据。如需发送二进制数据,可以 Base64 编码后发送,或使用 WebSocket 替代。
SSE 和 WebSocket 应该如何选择?
如果只是服务器向客户端推送数据(如通知、行情、日志),选择 SSE 更简单高效。如果需要双向通信(如聊天、协作编辑),则选择 WebSocket。SSE 基于 HTTP,部署更简单,且内置自动重连。
本文涉及AI创作
内容由AI创作,请仔细甄别