«

Server-Sent Events (SSE)实现长链接,服务端单向推送

emer 发布于 2021-9-15 17:44   1392 次阅读     


一、SSE 的本质

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

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

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


SSE 也有自己的优点。

  • SSE 使用 HTTP 协议,现有的服务器软件都支持。WebSocket 是一个独立协议。
  • SSE 属于轻量级,使用简单;WebSocket 协议相对复杂。
  • SSE 默认支持断线重连,WebSocket 需要自己实现。
  • SSE 一般只用来传送文本,二进制数据需要编码后传送,WebSocket 默认支持传送二进制数据。
  • SSE 支持自定义发送的消息类型。

客户端:


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    我是接收的
    <div id="app"></div>
  </body>
</html>
<script>
  var app = document.getElementById("app");
  var url = "http://127.0.0.1:3000/stream";
  var source = new EventSource(url, { withCredentials: true });
  source.addEventListener(
    "open",
    function (event) {
      // ...
      console.log("open", event);
    },
    false
  );
  source.addEventListener(
    "message",
    function (event) {
      var data = event.data;
      console.log("[ data ]", data);
      // handle message
      var divContainer = document.createElement("div");
      var pText = document.createTextNode(data);
      divContainer.appendChild(pText);
      app.appendChild(divContainer);
    },
    false
  );
</script>
服务端:

var express = require("express");
var app = express();
app.use(express.static("./"));
app.get("/stream", function (req, res) {
  var val = 1;
  console.log("[ req ]", req);
  res.writeHead(200, {
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache",
    Connection: "keep-alive",
    "Access-Control-Allow-Origin": "*",
  });
  res.write("retry: 10000\n"); //设置断线重连时间
  res.write("event: connecttime\n");
  res.write("data: " + new Date() + "\n\n");
  // res.write("data: " + new Date() + "\n\n");

  interval = setInterval(function () {
    val++;
    res.write("data: " + val + "-----" + new Date().getTime() + "\n\n");
  }, 1000);

  req.connection.addListener(
    "close",
    function () {
      clearInterval(interval);
    },
    false
  );
});
var server = app.listen(3000, function () {
  console.log("Example app listening on port 3000!");
});