0
点赞
收藏
分享

微信扫一扫

express 使用SSE轮询


SSE是客户端请求服务器后,服务器每隔一段时间向客户端发送数据(是单向的)。

详细可参照:http://www.ruanyifeng.com/blog/2017/05/server-sent_events.html

SSE 的本质

严格地说,HTTP 协议无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息(streaming)。

也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。本质上,这种通信就是以流信息的方式,完成一次用时很长的下载。

SSE 就是利用这种机制,使用流信息向浏览器推送信息。它基于 HTTP 协议,目前除了 IE/Edge,其他浏览器都支持。

SSE 的使用

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SSE</title>
</head>
<body>
<h3>获取服务端更新数据</h3>
<div id="result"></div>

<script>
	var source=new EventSource("/sse");
	source.onmessage=function(event)
	{
		document.getElementById("result").innerHTML+=event.data + "<br>";
	};
</script>

</body>
</html>

index.js

var app = require("express")();

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

app.get("/sse", (req, res) => {
  res.header({
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache",
    "Connection": "keep-alive",
  });
  res.write("data: " + (new Date()) + "\n\n");
  setInterval(() => {
    res.write("data: " + (new Date()) + "\n\n");
  }, 1000);
});

app.listen(3000, () => {
  console.log("listening on *:3000");
});

 执行 node index.js 启动服务,打开 http://localhost:3000/

express 使用SSE轮询_html

会看到不断地刷新数据

那么服务器端如何关闭 sse 连接

首先我们通过 EventSource 自定义个关闭连接的 close 事件

source.addEventListener("close", function (event) {
	source.close();
}, false);

然后通过 event 字段调用事件

setTimeout(() => {
    clearInterval(interval);
    res.write("event: close\n");
  }, 4000);

代码如下:

index.html

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title>SSE</title>
</head>
<body>
	<h3>获取服务端更新数据</h3>
	<div id="result"></div>
	<script>
		var source = new EventSource("/sse");
		source.onmessage = function (event) {
			document.getElementById("result").innerHTML += event.data + "<br>";
		};
		source.onerror = function (event) {
			source.close();
		};
		source.addEventListener("close", function (event) {
			source.close();
		}, false);
	</script>
</body>

</html>

index.js

var app = require("express")();

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

app.get("/sse", (req, res) => {
  res.header({
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive'
  });
  const interval = setInterval(() => {
    res.write("data: " + (new Date()) + "\n\n");
  }, 1000);

  setTimeout(() => {
    clearInterval(interval);
    res.write("event: close\n");
    res.write("data: test close->" + (new Date()) + "\n\n");
  }, 4000);
});

app.listen(3000, () => {
  console.log("listening on *:3000");
});

express 使用SSE轮询_服务端_02

只有三次,最后一次 test close 信息没有打出来,可见确实是关闭了

服务端返回数据需要特殊的格式,它分为四种消息类型:

event, data, id, retry

每个字段都有名称,紧接着有个”:“。当出现一个没有名称的字段而只有”:“时,这就会被服务端理解为”注释“,并不会被发送至浏览器端。

由于EventSource是基于HTTP连接之上的,因此在一段没有数据的时期会出现超时问题。服务器默认HTTP超时时间为2分钟,在node端可以通过response.connection.setTimeou(0)设置为默认的2min超时, 因此需要服务端做心跳保活,否则客户端在连接超时的情况下出现net::ERR_INCOMPLETE_CHUNKED_ENCODING错误。通过阅读相关规范,发现注释行可以用来防止连接超时,服务器可以定期发送一条消息注释行,以保持连接不断。

但是 EventSource  是不支持设置 request headers 的如果想设置有以下三种方法:

  • 将 headers 放到 url 中
  • 将 headers 放到 cookie 中,加上 { withCredentials: true } 参数,这样 EventSource 便会带上所有的 cookie
  • 使用 EventSource polyfill
举报

相关推荐

0 条评论