SSE
SSE 与 WebSocket 作用相似,都是建立浏览器与服务器之间的通信渠道,然后服务器向浏览器推送信息。 总体来说,WebSocket 更强大和灵活。因为它是全双工通道,可以双向通信;SSE 是单向通道,只能服务器向浏览器发送,因为流信息本质上就是下载。如果浏览器向服务器发送信息,就变成了另一次 HTTP 请求。
使用场景
SSE 特别适合以下场景:
- 实时数据更新(股票价格、天气信息)
- 社交媒体信息流
- 实时通知系统
- 日志实时显示
- 进度条更新
注意事项
- 服务器需要支持长连接
- 考虑设置适当的超时时间
- 注意内存管理,及时清理不需要的连接
- 考虑负载均衡的情况
- 建议实现错误重试机制
浏览器支持
SSE 在现代浏览器中得到广泛支持:
- Chrome 6+
- Firefox 6+
- Safari 5+
- Edge 79+
- Opera 11.5+
对于不支持的浏览器,可以使用 polyfill 库。
客户端API
EventSource 是 SSE 的客户端 API,用于创建服务器发送事件的连接。
const evtSource = new EventSource('http://your-server/events');
主要事件监听:
// 连接成功时触发
evtSource.onopen = function() {
console.log('连接已建立');
};
// 接收消息时触发
evtSource.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('收到数据:', data);
};
// 发生错误时触发
evtSource.onerror = function(err) {
console.error('发生错误:', err);
};
// 监听自定义事件
evtSource.addEventListener('customEvent', function(event) {
console.log('自定义事件数据:', event.data);
});
// 关闭连接
evtSource.close();
服务器端实现
Node.js Express 示例:
const express = require('express');
const app = express();
app.get('/events', (req, res) => {
// 设置 SSE 所需的响应头
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// 发送数据
const sendEvent = (data, eventType = null) => {
if (eventType) {
res.write(`event: ${eventType}\n`);
}
res.write(`data: ${JSON.stringify(data)}\n\n`);
};
// 定时发送数据示例
const intervalId = setInterval(() => {
sendEvent({ time: new Date().toISOString() });
}, 1000);
// 发送自定义事件
sendEvent({ type: 'welcome' }, 'customEvent');
// 客户端断开连接时清理
req.on('close', () => {
clearInterval(intervalId);
});
});
数据格式
服务器发送的消息格式:
例如:
- 基本消息:
// 只发送数据
res.write('data: {"message": "Hello World"}\n\n');
- 带事件类型的消息:
// 发送带自定义事件类型的消息
res.write('event: userLogin\n');
res.write('data: {"userId": "123", "username": "张三"}\n\n');
- 完整格式示例:
// 包含所有可选字段的完整消息
res.write('id: 1\n');
res.write('event: userUpdate\n');
res.write('retry: 10000\n'); // 重连间隔为10秒
res.write('data: {"userId": "123", "status": "online"}\n\n');
- 多行数据:
// 发送多行数据
res.write('event: article\n');
res.write('data: {\n');
res.write('data: "title": "SSE 教程",\n');
res.write('data: "content": "这是一个关于 SSE 的详细教程",\n');
res.write('data: "author": "张三"\n');
res.write('data: }\n\n');
- 使用工具函数发送消息:
function sendSSE(res, data, eventType = null, id = null, retry = null) {
let message = '';
// 添加ID
if (id) {
message += `id: ${id}\n`;
}
// 添加事件类型
if (eventType) {
message += `event: ${eventType}\n`;
}
// 添加重试时间
if (retry) {
message += `retry: ${retry}\n`;
}
// 添加数据
message += `data: ${JSON.stringify(data)}\n\n`;
res.write(message);
}
// 使用示例
sendSSE(res,
{ status: "success", data: "更新完成" },
"updateStatus",
"msg-001",
3000
);
每条消息以一个空行结束。可以包含以下字段:
event: 事件类型(可选)data: 消息数据(必需)id: 消息ID(可选)retry: 重连时间(可选)
例如
res.write('id: 1\n'+'event: customEvent\n' +
'data: {"info": "This is a custom event"}\n\n')