简介
SSE规范描述了一个内置的EventSource
类,它能让客户端打开一个与服务端的长连接,然后让客户端可以持续接收来自服务端的消息。
The Server-Sent Events specification describes a built-in class
EventSource
, that keeps connection with the server and allows to receive events from it.
SSE和WebSocket不一样,它不是一个新的协议,因此上手起来超简单。
我们知道一般情况下HTTP请求是这样的:客户端发送HTTP请求
->服务端处理并返回请求结果
->客户端接收到服务端返回数据
在这个过程中,客户端占主导地位,它可以随时主动向服务端发送请求,但服务端一直处于被动状态,不能主动向客户端发送信息,因为每个HTTP请求响应完毕后便马上断开了。
SSE的原理就是让客户端和服务端打开一个HTTP长连接,这样服务端就可以通过这个长连接主动向客户端发送数据了,过程如下:
客户端发送HTTP请求
->服务端响应200,并告知客户端这是一个event-stream接口
->客户端与服务端成功建立一个HTTP长连接,服务端可通过该连接持续向客户端发送数据
Demo
NodeJS示例
SSE服务端
通过NodeJS实现服务端,结合使用node-static
实现静态网页部署
server.js
let http = require('http');
let url = require('url');
let querystring = require('querystring');
let node_static = require('node-static');
let fileServer = new node_static.Server('.');
function onDigits(req, res) {
res.writeHead(200, {
'Content-Type': 'text/event-stream; charset=utf-8',
'Cache-Control': 'no-cache'
});
let i = 0;
let timer = setInterval(write, 1000);
write();
function write() {
i++;
if (i === 4000) {
res.write('event: bye\ndata: bye-bye\n\n');
clearInterval(timer);
res.end();
return;
}
res.write('data: ' + i + '\n\n');
}
}
function accept(req, res) {
if (req.url === '/digits') {
onDigits(req, res);
return;
}
fileServer.serve(req, res);
}
if (!module.parent) {
http.createServer(accept).listen(8080);
} else {
exports.accept = accept;
}
index.html
<!DOCTYPE html>
<script>
let eventSource;
function start() { // when "Start" button pressed
if (!window.EventSource) {
// IE or an old browser
alert("The browser doesn't support EventSource.");
return;
}
eventSource = new EventSource('digits');
eventSource.onopen = function(e) {
log("Event: open");
};
eventSource.onerror = function(e) {
log("Event: error");
if (this.readyState === EventSource.CONNECTING) {
log(`Reconnecting (readyState=${this.readyState})...`);
} else {
log("Error has occured.");
}
};
eventSource.addEventListener('bye', function(e) {
log("Event: bye, data: " + e.data);
});
eventSource.onmessage = function(e) {
log("Event: message, data: " + e.data);
};
}
function stop() { // when "Stop" button pressed
eventSource.close();
log("eventSource.close()");
}
function log(msg) {
logElem.innerHTML += msg + "<br>";
document.documentElement.scrollTop = 99999999;
}
</script>
<button onclick="start()">Start</button> Press the "Start" to begin.
<div id="logElem" style="margin: 6px 0"></div>
<button onclick="stop()">Stop</button> "Stop" to finish.
使用Node启动运行后访问本机1080端口,即可看到效果。
运行效果如下:
NodeJS SSE客户端
SSE是浏览器原生支持的,但它不属于Node的原生库,如果想在NodeJS中创建一个SSE客户端,那么就需要安装相关依赖:
下方任选一个
# NPM
npm i eventsource
# yarn
yarn add eventsource
# pnpm
pnpm add eventsource
然后写一个客户端:
client.js
const EventSource = require('eventsource');
let eventSource;
function start() { // when "Start" button pressed
eventSource = new EventSource('http://localhost:8080/digits');
eventSource.onopen = function(e) {
console.log("Event: open");
};
eventSource.onerror = function(e) {
console.log("Event: error");
if (this.readyState === EventSource.CONNECTING) {
console.log(`Reconnecting (readyState=${this.readyState})...`);
} else {
console.log("Error has occured.");
}
};
eventSource.addEventListener('bye', function(e) {
console.log("Event: bye, data: " + e.data);
});
eventSource.onmessage = function(e) {
console.log("Event: message, data: " + e.data);
};
}
function stop() { // when "Stop" button pressed
eventSource.close();
console.log("eventSource.close()");
}
start()
效果如下:
参考文章: