一、什么是SSE
Server-Sent Events (SSE) 是一种用于实现服务器主动向客户端推送数据的技术,也被称为“事件流”(Event Stream)。
这种技术的核心思想是服务器通过HTTP连接持续推送数据到客户端,而无需客户端频繁发起请求。
相比传统的轮询方式,SSE 更加高效和节能,因为它避免了多次请求和响应的开销,从而提供更流畅的实时数据更新体验。
二、SSE原理
Server-Sent Events (SSE) 是一种允许服务器主动向客户端发送实时更新数据的 Web 技术。它利用 HTTP 协议的长连接特性来实现数据推送。
1、客户端发起连接
- 客户端(通常是浏览器)通过 EventSource 接口发起一个 GET 请求,目标是支持 SSE 的服务器端点。
- 请求头中包含 Accept: text/event-stream,表明客户端期望接收事件流。
- 客户端可以在请求 URL 中包含查询参数,用于向服务器传递数据。
2、服务器响应
- 服务器接受到请求后,发送一个带有 Content-Type: text/event-stream 的响应头,告知客户端即将传输的内容类型。
- 服务器使用分块传输编码(chunked transfer encoding)持续发送数据,以保持连接的打开状态。
3、数据推送
- 3.1服务器在有新的数据时,以特定的事件流格式向客户端推送数据。每个事件由以下部分组成:
①事件数据: 使用 data: 关键字,事件数据可以有多行,每行都以 data: 开头。
②事件类型(可选): 使用 event: 关键字指定,用于客户端区分不同类型的事件。
③事件 ID(可选): 使用 id: 关键字,用于断线重连时让客户端知道上次接收到的最后一个事件的 ID。
④retry(可选): 指定客户端重连时间间隔
⑤注释行(可选): 以 : 开头的行,用于保持连接的活动状态,无实际数据。
- 3.2各部分之间用单个换行符分隔,不同事件之间用两个换行符分隔。
4、客户端处理
- 客户端通过 EventSource 对象的事件监听器来接收和处理数据,如 onmessage 用于处理收到的事件数据,onerror 处理连接错误,onopen 处理连接成功。
- 客户端自动处理重连机制,若连接断开,将自动尝试重新连接,通常使用 id: 字段提供的事件 ID 从上次断开的地方继续接收数据。
5、连接管理
- SSE 的设计确保连接保持活跃,通过发送注释行或心跳数据来防止连接超时。
- 客户端可以通过调用 EventSource.close() 方法主动关闭连接。
三、SSE优点和局限性
1、优点
- 简单性:SSE协议使用纯文本格式,基于HTTP协议之上,简单易用。
- 自动重连:如果连接断开,浏览器会自动尝试重连,并继续接收后续事件。
- 浏览器支持广泛:大多数现代浏览器都支持SSE,无需额外的插件或库。
- 节省资源:相比传统的轮询方式,SSE只在需要时发送数据,减少了服务器和客户端的负载。
2、局限性
- 单向数据流:只能从服务器推送到客户端,不能由客户端推送到服务器。
- 同源策略限制:SSE受同源策略影响,需要处理跨域问题。
- 连接限制:对于大型系统,SSE的连接数量可能受到服务器的限制。
四、SSE使用场景
- 实时更新:如股票行情、新闻推送、实时天气更新等,需要服务器主动向客户端推送最新数据。
- 消息通知:如社交媒体通知、即时消息、系统报警等。
- 数据流式处理:如监控数据流、日志数据分析等。
五、SSE注意事项
- 浏览器兼容性:SSE 并不支持所有浏览器。现代浏览器大多支持 SSE,但仍需注意检查兼容性,特别是在移动设备和较旧的浏览器版本上。
- 连接数限制:部分浏览器对每个域的最大连接数有限制。SSE 占用一个持久的 HTTP 连接,因此在大量用户访问时可能导致连接耗尽的问题。
- 连接稳定性:尽管SSE具有自动重连功能,但在不稳定的网络环境中,可能会导致数据丢失或延迟。
- 传输数据量:SSE 适用于轻量级的数据传输。如果需要传输大量数据或文件,SSE 可能不是最佳选择。
- 数据安全性:SSE 默认使用 HTTP 传输,数据传输不加密。若传输敏感数据,建议使用 HTTPS 以确保数据安全。
- 服务器端性能:持久的连接会对服务器资源产生压力,特别是在高并发情况下。需要对服务器进行性能优化,并可能考虑使用负载均衡技术。
- 数据格式限制:SSE 传输的数据必须是 UTF-8 编码文本,因此不适合传输二进制数据或非 UTF-8 编码的数据。
- 单向通信限制:SSE 是服务器向客户端的单向通信。如果需要双向通信,可能需要结合其他技术如 WebSocket 或 AJAX 请求。
六、SSE和WebSocket的区别
1、协议层级
- SSE:使用 HTTP 长连接,基于标准的 HTTP 协议,客户端通过发送一个常规的 GET 请求来建立连接,服务器在连接建立后持续发送数据。
- WebSocket:使用专用协议,WebSocket 在初始阶段通过 HTTP/HTTPS 协议进行握手,然后升级到 WebSocket 协议进行通信。
2、通信方向
- SSE:单向通信,只允许服务器向客户端推送数据。客户端通过 EventSource 接收数据。客户端无法通过同一连接发送数据回服务器。
- WebSocket:全双工通信,WebSocket 允许客户端和服务器之间进行全双工(双向)的实时通信,双方都可以在任意时刻发送数据。
3、数据格式
- SSE:数据以文本格式发送,事件格式简单明了(如 data: message),容易处理和调试 。
- WebSocket:数据可以是文本或二进制格式,适合传输结构化或非结构化的数据,甚至是流媒体。
4、状态管理
- SSE:①持久化连接:SSE 通过一个持久化的 HTTP 连接将服务器的数据推送给客户端。连接在数据传输完成后继续保持打开状态,等待新的数据。 ②自动重连机制:SSE 内置了自动重连功能,当连接断开时,客户端会尝试重新连接。重连后,客户端可以通过最后接收到的事件 ID 继续接收后续事件,避免数据丢失。SSE 的自动重连机制使得它在网络波动或连接中断的情况下更加稳定。 ③连接中断检测:服务器可以通过发送注释行保持连接活跃,客户端可以通过监听 onerror 事件来检测连接中断。
- WebSocket:①持续连接:WebSocket 建立连接后,该连接通常会持续保持,直到客户端或服务器主动关闭。WebSocket 的连接状态通常是“连接中”、“已连接”、“已断开”等。 ②手动重连:WebSocket 协议本身没有定义自动重连机制。连接断开后,通常需要在应用层手动管理连接的重新建立,以便在网络恢复或其他条件满足时重新连接。③心跳机制:为检测连接的存活状态,常在应用层实现心跳机制(发送定期的 ping/pong 消息),确保连接的稳定性和活跃性。
5、简易性与控制
- SSE:简单易用,尤其适合需要服务器单向推送实时数据的场景,如新闻更新、社交媒体通知等。不需要复杂的服务器设置,容易集成和使用。
- WebSocket:提供更大的灵活性和控制能力,适合需要双向实时通信的复杂场景,如在线游戏、实时聊天应用、金融交易系统等。
6、浏览器支持
- SSE:得到了大多数现代浏览器的支持,IE9及更高版本支持 SSE,但需要注意 IE 及旧版浏览器可能不支持。
- WebSocket:也得到了广泛的浏览器支持,但有时需要考虑后端服务器和网络基础设施对 WebSocket 的支持情况。
7、资源消耗
- SSE:因为是基于 HTTP 的长连接,资源消耗较少,特别适合频繁数据更新但数据量不大的场景。
- WebSocket:在高并发场景下,WebSocket 的连接数管理和资源消耗相对较大,但其高效的双向通信特性在需要频繁且复杂交互的场景中更具优势。
8、适用场景
- SSE:适合数据更新频率高,但每次更新数据量较小,且只需要服务器向客户端单向推送的场景。 如实时新闻、社交媒体动态、股票行情等。具有实现简单、兼容性广的特点。
- WebSocket:适合需要低延迟、双向通信和高实时性互动的复杂应用。 如在线游戏、实时聊天系统、协同编辑等,支持复杂数据传输,但部署和实现较为复杂。
七、WEB实时通讯技术
1、WebRTC
介绍:WebRTC是一种支持浏览器直接进行音频、视频、数据点对点传输的技术。
优点:支持高效的音视频流传输。 点对点连接,延迟低。
缺点:需要处理NAT穿透和网络状况的复杂性。
场景:视频聊天、P2P文件传输、在线会议。
2、Long Polling
介绍:Long Polling是HTTP轮询的一种改进方式。客户端发送HTTP请求到服务器后,服务器不会立即返回响应,而是等待数据准备好后再返回。客户端收到数据后,会再次发送请求,循环往复。
优点:减少不必要的请求。
缺点:可能导致服务器端资源被长时间占用 。
场景:数据更新不频繁但实时性要求较高的场景。
3、Comet
介绍:Comet是一种基于HTTP长连接的技术,通过HTTP请求来保持连接。当服务器有新消息时,就发送给客户端,实现实时推送。
优点:类似于Long Polling 。
缺点:依赖于HTTP连接。
场景:需要实时推送的Web应用。
4、SignalR
介绍:SignalR是一个开源的实时通信框架,它支持多种通信方式,包括WebSocket、SSE等。SignalR可以简化实时通信的实现,提供了一套统一的API。
优点:跨平台、灵活性、简单易用。
缺点:需要选择合适的通信方式。
场景:需要实现实时通信的Web应用。
5、MQTT
介绍:MQTT是一个轻量级的消息传输协议,它可以在低带宽、不稳定的网络环境下实现可靠的消息传输。MQTT协议基于发布/订阅模式,客户端可以订阅一个或多个主题,当这些主题有消息发布时,客户端会收到相应的消息。
优点:轻量级、可靠性、灵活性。
缺点:消息格式和传输方式限制。
场景:物联网、移动应用等低带宽、高可靠性场景。
08、SSE的实现示例
index.html
<body>
<h1>SSE 示例</h1>
<button onclick="restartBtn()">生成(模拟输入完问题回车操作)</button>
<button onclick="button_close()">暂停</button>
<button onclick="startEventSource()">继续</button>
<div id="messages"></div>
<script>
let startIndex = 0;
let eventSource = null;
function startEventSource() {
// 创建 EventSource 对象,连接到服务器的 /events 路径
eventSource = new EventSource(`/events?startIndex=${startIndex}`);
// 监听消息事件,接收服务器发送的数据
eventSource.onmessage = function(event) {
const newElement = document.createElement("span");
newElement.textContent = event.data;
document.getElementById("messages").appendChild(newElement);
startIndex = Number(event.lastEventId) + 1;
};
// 处理连接打开事件
eventSource.onopen = function() {
console.log("EventSource 连接已打开");
};
// 处理连接错误事件
eventSource.onerror = function(event) {
console.error("EventSource 发生错误:", event);
eventSource.close();
};
}
function restartBtn() {
button_close()
startIndex = 0; // 重置 startIndex
document.getElementById('messages').innerHTML = '';
startEventSource();
}
function button_close() {
if (eventSource) {
eventSource.close();
eventSource = null;
}
}
</script>
</body>
server.js
const http = require('http');
const fs = require('fs');
const path = require('path');
let connections = {}; // 跟踪每个客户端连接的进度 键是客户端的IP地址,值是该客户端当前接收到的数据位置。
var timerId = null
// 创建 HTTP 服务器
const server = http.createServer((req, res) => {
if (req.url === '/') {
// 处理主页请求,返回 HTML 文件
fs.readFile(path.join(__dirname, 'index.html'), (err, data) => {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(data);
});
} else if (req.url.startsWith('/events')) {
// 处理 SSE 连接请求
res.writeHead(200, {
'Content-Type': 'text/event-stream', // SSE 特定的 MIME 类型
'Cache-Control': 'no-cache', // 禁用缓存
'Connection': 'keep-alive' // 保持连接
});
// ========== 服务器端分块传输 客户端逐字显示
const mockdata = '【店铺介绍】****以成立10年时间,现有100多名员工,专业家电清洗,开荒保洁、家庭保洁、家政保姆,烟罩排烟管道清洗、地板打蜡养护等。专业团队,热情服务!'
let startIndex = 0;
if (req.url.includes('?')) {
const urlParams = new URLSearchParams(req.url.split('?')[1]);
startIndex = parseInt(urlParams.get('startIndex')) || 0;
}
if (startIndex === 0) { connections = {} }
connections[req.socket.remoteAddress] = startIndex;
function sendChunk() {
if (connections[req.socket.remoteAddress] !== undefined && connections[req.socket.remoteAddress] < mockdata.length) {
res.write(`id: ${connections[req.socket.remoteAddress]}\n`);
res.write(`data: ${mockdata.charAt(connections[req.socket.remoteAddress])}\n\n`);
connections[req.socket.remoteAddress]++;
timerId = setTimeout(() => {
sendChunk();
}, 50);
} else {
res.end();
}
}
sendChunk()
req.on('close', () => {
if (timerId !== null) {
clearTimeout(timerId);
timerId = null;
}
console.log('连接关闭');
});
} else {
// 处理其他请求
res.writeHead(404);
res.end('Not Found');
}
});
// 服务器监听端口
server.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
启动服务
node server.js
*** 本文仅是学习中的记录,有错误请指正。 ***
暂无评论内容