【问题标题】:Use server side events to dynamically send message to browser使用服务器端事件向浏览器动态发送消息
【发布时间】:2019-11-25 13:06:03
【问题描述】:

我希望使用 SSE 向浏览器动态发送消息。理想情况下,我想要一个最小的应用程序,其中浏览器在调用函数或方法(将消息作为参数)之前什么都不做,并且浏览器收到此消息并仅记录一次。我试图用以下来说明这一点:

const http = require("http");
const server = http.createServer(<not sure what code goes here>);
server.listen(8000);

// this can be called at any time after creating the server so the browser 
// can receive the message and log it to the console.
sendMessageToBrowser(`data: this is a dynamic message\n\n`) 

但是,下面的基本 SSE 应用程序只是每 3 秒(默认)将“hello world”记录到浏览器控制台。我不明白这与通过常规路线提供数据并使用类似的方式有什么不同:

setInterval(fetch("/events").then(res => res.text()).then(data => console.log(data)));

我的请求是否可以通过 SSE 提出,还是我误解了它的工作原理?我知道我的请求可以通过 websockets/socket.io 实现,但我希望使用 SSE,因为我不想使用更易于理解和实现的库和 SSE。

每 3 秒记录一次 hello world 的最小示例:

const http = require("http");

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",
      Connection: "keep-alive",
    });
    res.end(`data: hello world\n\n`);
    return;
  }

  // Client side logs message data to console
  res.writeHead(200, { "Content-Type": "text/html" });
  res.end(`
      <script>
        var eventSource = new EventSource('/events');
        eventSource.onmessage = function(event) {
          console.log(event.data);
        };
      </script>
  `);
});

server.listen(8000);

【问题讨论】:

  • 发生的事情是它只是每 3 秒重新连接一次,检查您的网络选项卡,SSE 应该循环服务器端的原因,因此连接挂起直到数据输出,您的 res.end 将结束连接,忽略任何保持活动状态.. 我没有在 nodejs 中实现 SSE,socketio 太容易了,但是如果您要移植,也许一个 php 示例会有所帮助。 stackoverflow.com/a/49081040/661872
  • 你可以使用Socket.io作为后端来监听前端的事件你可以使用Socket.io-client
  • @LawrenceCherone 感谢您提供的信息。我已将 res.end() 更改为 res.write() 所以消息只发送到浏览器一次。但是,我仍然看不到如何从服务器发送其他消息。我认为 SSE 的全部意义在于,在发生某些服务器端事件时,我可以向浏览器发送新消息?
  • 你可以例如:setInterval(),也许是 eventemitter,或者通过数据库行和一个循环代理,在里面轮询事件 id 的变化,没有像 socketio 这样的发射概念
  • “没有像socketio一样发射的概念”这是关键。回想起来,我的问题应该只是:“SSE 的 socket.io 的 socket.emit() 是什么?”答案似乎是没有。不过我觉得这很奇怪,因为我读过的所有内容都将 SSE 描述为 websocket 的单向等价物,并且顾名思义,可以使用服务器端事件来发出消息。

标签: javascript node.js websocket server-sent-events


【解决方案1】:

我不知道我是否真的明白你需要什么。我更改了代码,现在如果您转到/sendEvent url,您会在根页面中看到一个新日志。 您可以在任何函数中使用resEvents var 在根页面中记录新消息。

const http = require("http");
var resEvents;
var count = 0;

var myInterval = setInterval(() => {
  if (count < 50) sendMessageToBrowser(count);
  else clearInterval(myInterval);
}, 1000);

function sendMessageToBrowser(msg) {
  if (resEvents) {
    resEvents.write(`data: ${msg}\n\n`);
    count++;
  }
}

const server = http.createServer((req, res) => {
  // Server-sent events endpoint
  if (req.url === "/events") {
    resEvents = res;
    res.writeHead(200, {
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-cache",
      Connection: "keep-alive"
    });
    res.write(
      `data: Go to http://localhost:8000/sendEvent to log a new Messages \n\n`
    );
    return;
  }

  if (req.url === "/sendEvent") {
    if (!resEvents) {
      res.writeHead(200, { "Content-Type": "text/html" });
      res.end(`Logged a new Message<br>Refresh the page to log a new Message`);
      resEvents.write(
        `data: From Url: ${Math.floor(Math.random() * 5000) + 1}\n\n`
      );
    } else {
      res.writeHead(200, { "Content-Type": "text/html" });
      res.end(`First go to http://localhost:8000/`);
    }
    return;
  }

  // Client side logs message data to console
  res.writeHead(200, { "Content-Type": "text/html" });
  res.end(`
      <script>
        var eventSource = new EventSource('/events');
        eventSource.onmessage = function(event) {
          console.log(event.data);
        };
      </script>
  `);
});

server.listen(8000);

更新

我添加了sendMessageToBrowser 函数。它位于setInterval 内,因此您可以看到服务器每秒向浏览器发送最多 50 条消息。

【讨论】:

  • 谢谢,这很有趣,但似乎没有达到我的要求。刷新浏览器意味着生成事件的是客户端,而不是服务器。而且我无法指定服务器发送给浏览器的消息。
  • @Max888 刷新浏览器只是为了告诉你如何调用服务器上的函数。您可以将消息从您只需要使用的服务器发送到浏览器resEvents
  • 谢谢 - 这正是我希望做的。我已经添加了您答案的简化版本。
【解决方案2】:

这是@Mattia 答案的最小版本。

const http = require("http");
var resEvents;

const server = http.createServer((req, res) => {
  if (req.url === "/events") {
    resEvents = res;
    res.writeHead(200, {
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-cache",
      Connection: "keep-alive",
    });
    return;
  }

  res.writeHead(200, { "Content-Type": "text/html" });
  res.end(`
      <script>
        var eventSource = new EventSource('/events');
        eventSource.onmessage = function(event) {
          console.log(event.data);
        };
      </script>
  `);
});

server.listen(8000);

// this can only be run after the browser has connected - either run code
// sequentially in a REPL or put this in a setTimeout() to give you time 
// to go to localhost:8000
resEvents.write(`data: hello\n\n`); 

【讨论】:

  • 在您的版本上启动服务器时,resEvents 未定义
  • 好点,我已经运行了 REPL 的每一行,这让我有机会在运行 resEvents.write() 之前转到 localhost:8000,在这种情况下它可以工作。
猜你喜欢
  • 2013-04-02
  • 1970-01-01
  • 2015-01-05
  • 2015-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-12
相关资源
最近更新 更多