【问题标题】:Simple Way to Implement Server Sent Events in Node.js?在 Node.js 中实现服务器发送事件的简单方法?
【发布时间】:2022-02-01 08:08:26
【问题描述】:

我环顾四周,似乎所有在 Node.js 中实现 SSE 的方法都是通过更复杂的代码实现的,但似乎应该有一种更简单的方法来发送和接收 SSE。是否有任何 API 或模块可以让这变得更简单?

【问题讨论】:

  • 使用套接字怎么样?
  • @Daniel,SSE 在 HTTP 上运行,HTTP 在 TCP 套接字上运行 :) “websockets”会更合适。然而,今天有很多图书馆做 SSE。我个人创建了npmjs.com/package/@toverux/expresse,因为我对现有的包不满意。
  • 我在看一些简单的东西,找不到任何东西,所以我使用 nodejs 制作了一个简单的 SSE 服务器。它位于github.com/TheSalarKhan/node-sse-server 要在生产中使用它,您应该添加一个身份验证中间件。

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


【解决方案1】:

这是一个快速服务器,每秒发送一个服务器发送事件 (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!'))

将以上代码放入文件(index.js)并运行: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(),但是没有任何工作。我的问题在这里stackoverflow.com/questions/61412538/…
【解决方案2】:

我在这里添加了一个简单的 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}`);
});

【讨论】:

  • 这是一个很好的答案,因为它确实使用任何 3rd 方依赖项:) 但是我想指出 res.end(message) 应该替换为 setInterval(() =&gt; res.write(message), 1000) 因为连接不应该结束,同样应该一直使用连接,但是您当前的代码导致客户端每 1 秒重新连接并创建一个新连接
  • 感谢@KarlisBikis 的评论。你是绝对正确的。我已将您的代码提议添加到我的帖子中。谢谢!
  • 注意'Connection': 'keep-alive'不能用于HTTP/2。如果使用 HTTP/2,则需要在设置标头之前检查 res.httpVersionMajor。此外,您应该每 15 秒左右发送一个保持活动数据包(尽管您的示例每秒发送一个测试数据包)。一个空的注释行 :\n 就可以了。
  • 在实践中,我还建议将res 存储在Set 或Array 中。如果想将事件广播到一个组(就像socket.io 那样),你会迭代并发送。在 close 事件中,将其从集合中移除。但是这个示例做得很好,零依赖启动。干得好:)
  • 感谢@ShortFuse 的澄清。老实说,我还没有在生产环境中使用过 SSE,但如果我这样做了,你的消息将帮助我构建更强大的东西 :)
【解决方案3】:

如果您使用 express,这是最简单的方法 https://www.npmjs.com/package/express-sse

在 BE 上:

const SSE = require('express-sse');

const sse = new SSE();

...

app.get('/sse', sse.init);

...

sse.send('message', 'event-name');

关于 FE:

const EventSource = require('eventsource');

const es = new EventSource('http://localhost:3000/sse');

es.addEventListener('event-name', function (message) {
  console.log('message:', message)
});

【讨论】:

    【解决方案4】:

    我在 node.js 中找到了 SSE 实现。

    Github 链接:https://github.com/einaros/sse.js

    NPM 模块:https://www.npmjs.com/package/sse

    上面的链接对你有帮助吗?

    【讨论】:

      【解决方案5】:
      **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)
      

      【讨论】:

        【解决方案6】:

        你应该可以使用Socket.io 来做这样的事情。首先,您需要使用npm install socket.io 安装它。从那里开始,在您的代码中,您将希望拥有 var io = require(socket.io);

        你可以看到more in-depth examples given by 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>
        

        【讨论】:

        • 我绝对反对这种方法!我现在使用 Socket.IO 已经很长时间了,我可以肯定地说,随着时间的推移,你会开始挣扎很多痛苦。服务器端事件是单向,而套接字是双向。一种是请求-响应类型,另一种是长连接类型。区别是巨大的,你绝对不需要套接字——你将不得不处理重新连接、垃圾邮件保护、服务器宕机,也许是集群。使用简单的 SSE,它已经在浏览器中实现了!查看其他答案!
        • OP 要求的是 SSE,而不是 WebSockets。
        猜你喜欢
        • 1970-01-01
        • 2012-02-16
        • 1970-01-01
        • 1970-01-01
        • 2023-02-17
        • 2020-08-06
        • 2023-01-26
        • 2015-05-03
        • 2012-04-30
        相关资源
        最近更新 更多