用node写一个Server Send Events

本文介绍了如何在Node.js中利用Server-SentEvents(SSE)实现数据服务端的异步计算结果推送,对比了SSE与WebSocket的区别,并提供了客户端和服务器端的代码示例。
摘要由CSDN通过智能技术生成

    因为数据业务的特殊性,有些计算数据服务端不能同步计算得出,需要通过异步方式计算出结果,然后再通知用户,这里我们可以使用SSE或者WebSocket,鉴于无需客户端和服务器双向通信,故采用SSE更加的简洁,技术更加友好,接下来我们就写一个node.js的实现,一个经过一段随机的时间,服务端向客户端发送一个随机的数字串。

效果如下:

一、SSE和WebSocket的几个重要区别

与 WebSocket 相比,EventSource 是与服务器通信的一种不那么强大的方式。

我们为什么要使用它?

主要原因:简单。在很多应用中,WebSocket 有点大材小用。

我们需要从服务器接收一个数据流:可能是聊天消息或者市场价格等。这正是 EventSource 所擅长的。它还支持自动重新连接,而在 WebSocket 中这个功能需要我们手动实现。此外,它是一个普通的旧的 HTTP,不是一个新协议。

二、客户端获取消息

三、搭建工程

项目目录如下,包含client端和node端,实现

client端index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Server Sent Events example 1</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<script type="module" src="./js/main.js"></script>
<link rel="stylesheet" href="./css/main.css" />
</head>
<body>

  <h1>Server Sent Events example 1</h1>

  <output id="sselog"></output>

</body>
</html>

client端main.js,主要用来接收消息

// create connection
const source = new EventSource("/random");

// SSE connection open
source.addEventListener("open", (e) => {
  log("connected");
});

// SSE message
source.addEventListener("message", (e) => {
  log("RECEIVED data:", e.data);
});

// SSE error or termination
source.addEventListener("error", (e) => {
  if (e.eventPhase === EventSource.CLOSED) {
    log("disconnected");
  } else {
    log("error", e.message);
  }
});

const outlog = document.getElementById("sselog");
function log(...msg) {
  msg.forEach((m) => (outlog.textContent += m + " "));
  outlog.textContent += "\n";
  outlog.scrollTop = outlog.scrollHeight;
}

node端static.js 主要用来展示消息,将res写入到index.html并write到浏览器展示

// serve static file from root directory
import path from "node:path";
import { access, stat, readFile, constants } from "node:fs/promises";

const mime = {
  ".html": "text/html",
  ".css": "text/css",
  ".js": "application/javascript",
  err: "text/plain",
};

export async function serveStatic(res, uri, root) {
  // get filename
  let filename = path.join(root, uri);

  // is file readable?
  let isReadable;

  try {
    await access(filename, constants.R_OK);
    isReadable = true;
  } catch {}

  if (!isReadable) {
    serve(404, "404 Not Found\n");
    return;
  }

  // is a directory?
  const fileInfo = await stat(filename);
  if (fileInfo.isDirectory()) filename = path.join(filename, "./index.html");

  // read file contents
  try {
    const content = await readFile(filename);
    serve(200, content, path.extname(filename));
  } catch (err) {
    serve(500, err.message);
  }

  // return content
  function serve(code, content, type) {
    res.writeHead(code, {
      "Content-Type": mime[type] || mime["err"],
      "Cache-Control": "must-revalidate, max-age=0",
      "Content-Length": Buffer.byteLength(content),
    });
    res.write(content);
    res.end();
  }
}

node端index.js 借助http和url模块进行message的send

import http from "node:http";
import url from "node:url";
import { serveStatic } from "./static.js";

const port = 8000,
  root = "./client/";

// start server
http
  .createServer(async (req, res) => {
    // get URI path
    const uri = url.parse(req.url).pathname;

    // return response
    switch (uri) {
      case "/random":
        sseStart(res);
        sseRandom(res);
        break;

      default:
        await serveStatic(res, uri, root);
    }
  })
  .listen(port);

console.log(`server running: http://localhost:${port}\n\n`);

// SSE head
function sseStart(res) {
  res.writeHead(200, {
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache",
    Connection: "keep-alive",
  });
}

// SSE random number
function sseRandom(res) {
  res.write("data: " + (Math.floor(Math.random() * 1000) + 1) + "\n\n");
  setTimeout(() => sseRandom(res), Math.random() * 3000);
}

-- END --

    课外阅读   (看到带着孩子送外卖的妈妈有感)

观刈麦⑴

田家少闲月,五月人倍忙。

夜来南风起,小麦覆陇黄⑵。

妇姑荷箪食⑶,童稚携壶浆⑷,

相随饷田去⑸,丁壮在南冈⑹。

足蒸暑土气,背灼炎天光⑺,

力尽不知热,但惜夏日长⑻。

复有贫妇人,抱子在其旁⑼,

右手秉遗穗⑽,左臂悬敝筐⑾。

听其相顾言⑿,闻者为悲伤⒀。

家田输税尽⒁,拾此充饥肠。

今我何功德⒂,曾不事农桑⒃。

吏禄三百石⒄,岁晏有余粮⒅。

念此私自愧⒆,尽日不能忘⒇。 [1]

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值