Node.js SSE res.write() 不在流中发送数据
Node.js SSE res.write() not sending data in streams
我正在尝试在 NodeJs 中添加 SSE(服务器发送的事件),但是当我使用 res.write()
发送响应时,数据没有被发送,但只有在写入 res.end()
之后所有数据都是同时发送。
我已经在 Github、Whosebug 上找到很多关于这个问题的帖子,并且在每个 res.write()
之后都提到使用 res.flush()
的地方,但这也不适用于我,我也没有明确使用任何压缩模块。
服务器端代码
任何人都可以告诉我有什么方法可以完成这项工作。
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!'))
客户端代码
<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>
解决方案 - 我使用 nginx 作为反向代理,这就是它发生的原因所以我尝试了这个解决方案并且它有效:)
EventSource / Server-Sent Events through Nginx
如果您的 Express 服务器位于防火墙或代理服务器之后,它们通常会等到服务器关闭连接后才发送整个响应。相反,您需要在浏览器和服务器之间建立允许 'Connection': 'keep-alive'
.
的连接
在您的示例中,为了防止完成整个迭代以发送事件,您需要在 res.write()
之后添加 res.end()
。
这样每个事件都会在 1 秒后发送。
function countdown(res, count) {
res.write("data: " + count + "\n\n")
res.end() // <-- add after each event
if (count)
setTimeout(() => countdown(res, count-1), 1000)
else
res.end()
}
我正在尝试在 NodeJs 中添加 SSE(服务器发送的事件),但是当我使用 res.write()
发送响应时,数据没有被发送,但只有在写入 res.end()
之后所有数据都是同时发送。
我已经在 Github、Whosebug 上找到很多关于这个问题的帖子,并且在每个 res.write()
之后都提到使用 res.flush()
的地方,但这也不适用于我,我也没有明确使用任何压缩模块。
服务器端代码
任何人都可以告诉我有什么方法可以完成这项工作。
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!'))
客户端代码
<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>
解决方案 - 我使用 nginx 作为反向代理,这就是它发生的原因所以我尝试了这个解决方案并且它有效:)
EventSource / Server-Sent Events through Nginx
如果您的 Express 服务器位于防火墙或代理服务器之后,它们通常会等到服务器关闭连接后才发送整个响应。相反,您需要在浏览器和服务器之间建立允许 'Connection': 'keep-alive'
.
在您的示例中,为了防止完成整个迭代以发送事件,您需要在 res.write()
之后添加 res.end()
。
这样每个事件都会在 1 秒后发送。
function countdown(res, count) {
res.write("data: " + count + "\n\n")
res.end() // <-- add after each event
if (count)
setTimeout(() => countdown(res, count-1), 1000)
else
res.end()
}