我查看了一些资料,似乎在Node.js中实现SSE的方式都比较复杂,但是应该有更简单的方法来发送和接收SSE。是否有任何API或模块可以使这个过程更简单?
我查看了一些资料,似乎在Node.js中实现SSE的方式都比较复杂,但是应该有更简单的方法来发送和接收SSE。是否有任何API或模块可以使这个过程更简单?
这里有一个Express服务器,每秒钟发送一个Server-Sent Event(SSE),从10倒数到0:
const express = require('express')
const app = express()
app.use(express.static('public'))
app.get('/countdown', function(req, res) {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
})
countdown(res, 10)
})
function countdown(res, count) {
res.write("data: " + count + "\n\n")
if (count)
setTimeout(() => countdown(res, count-1), 1000)
else
res.end()
}
app.listen(3000, () => console.log('SSE app listening on port 3000!'))
node index
接下来,将以下HTML代码放入一个文件中(public/index.html):<html>
<head>
<script>
if (!!window.EventSource) {
var source = new EventSource('/countdown')
source.addEventListener('message', function(e) {
document.getElementById('data').innerHTML = e.data
}, false)
source.addEventListener('open', function(e) {
document.getElementById('state').innerHTML = "Connected"
}, false)
source.addEventListener('error', function(e) {
const id_state = document.getElementById('state')
if (e.eventPhase == EventSource.CLOSED)
source.close()
if (e.target.readyState == EventSource.CLOSED) {
id_state.innerHTML = "Disconnected"
}
else if (e.target.readyState == EventSource.CONNECTING) {
id_state.innerHTML = "Connecting..."
}
}, false)
} else {
console.log("Your browser doesn't support SSE")
}
</script>
</head>
<body>
<h1>SSE: <span id="state"></span></h1>
<h3>Data: <span id="data"></span></h3>
</body>
</html>
在您的浏览器中打开localhost:3000
,并观看SSE倒计时。
res.end()
时一次性发送,即使我在每个 res.write()
后尝试了 res.flush()
,但什么也没有效果。我的问题在这里:https://dev59.com/tbroa4cB1Zd3GeqPm5LU。 - Sudhanshu Gaur我在这里添加了一个简单的SSE实现。它只是一个Node.js文件。
你可以在这里查看结果:https://glossy-ox.glitch.me/
const http = require('http');
const port = process.env.PORT || 3000;
const server = http.createServer((req, res) => {
// Server-sent events endpoint
if (req.url === '/events') {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
...(req.httpVersionMajor === 1 && { 'Connection': 'keep-alive' })
});
const refreshRate = 1000; // in milliseconds
return setInterval(() => {
const id = Date.now();
const data = `Hello World ${id}`;
const message =
`retry: ${refreshRate}\nid:${id}\ndata: ${data}\n\n`;
res.write(message);
}, refreshRate);
}
// Client side
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(`
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>SSE</title>
</head>
<body>
<pre id="log"></pre>
</body>
<script>
var eventSource = new EventSource('/events');
eventSource.onmessage = function(event) {
document.getElementById('log').innerHTML += event.data + '<br>';
};
</script>
</html>
`);
});
server.listen(port);
server.on('error', (err) => {
console.log(err);
process.exit(1);
});
server.on('listening', () => {
console.log(`Listening on port ${port}`);
});
res.end(message)
应被替换为 setInterval(() => res.write(message), 1000)
,因为连接不应该被终止,应该一直使用同一连接。目前你的代码会导致客户端每秒重新连接并创建新连接。 - Karlis Bikis'Connection': 'keep-alive'
不能用于 HTTP/2。如果使用 HTTP/2,则需要在设置标头之前检查 res.httpVersionMajor
。此外,您应该每隔大约 15 秒发送一个保持活动数据包(尽管您的示例每秒发送一个测试数据包)。一个空注释行 :\n
就可以了。 - ShortFuseres
存储在Set或Array中。如果想要向组广播事件(例如socket.io
),则需要迭代并发送。在close
事件中,从集合中删除它。但是这个示例没有任何依赖项,做得很好 :) - ShortFuseconst SSE = require('express-sse');
const sse = new SSE();
...
app.get('/sse', sse.init);
...
sse.send('message', 'event-name');
关于前端:
const EventSource = require('eventsource');
const es = new EventSource('http://localhost:3000/sse');
es.addEventListener('event-name', function (message) {
console.log('message:', message)
});
**client.js**
var eventSource = new EventSource("/route/events");
eventSource.addEventListner("ping", function(e){log(e.data)});
//if no events specified
eventSource.addEventListner("message", function(e){log(e.data)});
**server.js**
http.createServer((req, res)=>{
if(req.url.indexOf("/route/events")>=){
res.setHeader('Connection', 'keep-alive');
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Content-Type", "text/event-stream");
let event = "event: ping";
let id = `id: ${Date.now()}`;
let data = {
message:`hello @${new Date().toString()}`
}
data = "data: "+JSON.stringify(data);
res.end(`${event}\n${id}\n${data}\n\n`);
}
}).listen(PORT)
在查看了其他答案后,我终于让它工作了,但最终我不得不做一些不同的事情。
express-sse
:确切的express-sse
版本非常重要。最新版本尝试使用res.flush()
,但会失败并导致HTTP服务器崩溃。
"express-sse": "0.5.1",
express-sse
:npm install
app.use(app.baseUri, require('./lib/server-sent-events').router);
调用pause()
相当于flush()
,这个函数已经从express中移除了。它确保你将会在消息被发送时持续接收到它们。
var express = require('express');
const SSE = require('express-sse');
const sse = new SSE();
var router = express.Router();
router.get('/sse', sse.init)
module.exports = {
send,
router
};
async function send(message) {
sse.send(message.toProperCase(), 'message');
await pause();
}
function pause() {
return new Promise((resolve, reject) => {
setImmediate(resolve)
})
}
send
:var express = require('express');
var serverSentEvents = require('../lib/server-sent-events');
var router = express.Router();
router.get('/somepath', yourhandler);
module.exports = router;
async function yourhandler (req, res, next) {
await serverSentEvents.send('hello sse!'); // <<<<<
}
我建议您保留 event.data.replace(/"/g,'')
,因为 express-sse 会添加引号并且我们不需要它们。
const eventSource = new EventSource('http://yourserver/sse');
eventSource.onmessage = function(event) {
document.getElementById("result").innerHTML = event.data.replace(/"/g,'') + '...';
};
你可以使用Socket.io来实现这样的功能。首先,你需要使用npm install socket.io
安装它。然后,在你的代码中,你需要有var io = require(socket.io);
你可以查看Socket.IO提供的更深入的示例
在服务器上,你可以使用类似以下的代码:
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('../..')(server);
var port = process.env.PORT || 3000;
server.listen(port, function () {
console.log('Server listening at port ' + port);
});
app.use(express.static(__dirname + '/public'));
io.on('connection', function (socket) {
socket.emit('EVENT_NAME', {data});
});
在客户端上类似于这样:
<script src="socket_src_file_path_here"></script>
<script>
var socket = io('http://localhost');
socket.on('EVENT_NAME', function (data) {
console.log(data);
//Do whatever you want with the data on the client
});
</script>