簡體   English   中英

NodeJS + WS 訪問當前運行的 WS 服務器實例

[英]NodeJS + WS access currently running WS server instance

我已經使用 NodeJS、ExpressJS 和路由控制器實現了一個簡單的 REST API。 我還實現了一個與 REST API 一起運行並使用 WS 的基本 WebSocket 服務器。

const app = express();

app.use(bodyParser.json({limit: "50mb"}));
app.use(bodyParser.urlencoded({limit: "50mb", extended: true}));

useExpressServer(app, {
    controllers: [
        UserController
    ]
});

const server = app.listen(21443, (err: Error) => {
    console.log("listening on port 21443");
});

const wss = new WebSocket.Server({server});

wss.on("connection", (ws: WebSocket) => {
    ws.on("message", (message: string) => {
        console.log("received: %s", message);
        ws.send(`Hello, you sent -> ${message}`);
    });

    ws.send("Hi there, I am a WebSocket server");
});

我的問題是如何訪問當前正在運行的 WS 實例,以便我能夠從我的控制器方法sendbroadcast 我有許多運行長進程的POST方法,因此向客戶端返回 HTTP 200,然后我想向所有連接的 WS 客戶端sendbroadcast

從我的控制器類中訪問WebSocket.Server實例的正確方法是什么?

連接的客戶端列表存儲在wss對象中。 您可以像這樣接收並遍歷它們:

wss.clients.forEach((client) => {
    if (client.userId === current_user_id && client.readyState === WebSocket.OPEN) {
        // this is the socket of your current user
    }
})

現在,您需要以某種方式識別您的客戶。 您可以通過在連接時為此客戶端分配一些ID來做到這一點:

wss.on('connection', async (ws, req) => {
    // req.url is the url that user connected with
    // use a query parameter on connection, or an authorization token by which you can identify the user
    // so your connection url will look like
    // http://example.com/socket?token=your_token
    ws.userId = your_user_identifier
    ....
})

廣播使用:

wss.clients.forEach((client) => {
    if (client.readyState === WebSocket.OPEN) {
        client.send(data);
    }
});

如果您的控制器和套接字將位於不同的文件中(並且我相信它們會存在),則必須將wss對象導出到套接字文件中,並將其導入控制器中。

您可以更早地創建 websocket 並傳遞實例:

const notifier = new NotifierService(); 
notifier.connect(http.createServer(app));

app.get("/somethingHappened", () => {
  notifier.broadcast("new notification!!");
});

app.use(routes(notifier))

完整代碼:

app.js

將 websocket 傳遞給其他路由:

const express = require("express");
const http = require("http");
const NotifierService = require("../server/NotifierService.js");
const routes = require("./routes");

const app = express();
const server = http.createServer(app);
const notifier = new NotifierService();
notifier.connect(server);

app.get("/somethingHappened", () => {
  notifier.broadcast("new notification!!");
});

//   to demonstrate how the notifier instance can be
//   passed around to different routes
app.use(routes(notifier));

server
  .listen(4000)
  .on("listening", () =>
    console.log("info", `HTTP server listening on port 4000`)
  );

處理 websocket 的NotifierService.js

const url = require("url");
const { Server } = require("ws");

class NotifierService {
  constructor() {
    this.connections = new Map();
  }

  connect(server) {
    this.server = new Server({ noServer: true });
    this.interval = setInterval(this.checkAll.bind(this), 10000);
    this.server.on("close", this.close.bind(this));
    this.server.on("connection", this.add.bind(this));
    server.on("upgrade", (request, socket, head) => {
      console.log("ws upgrade");
      const id = url.parse(request.url, true).query.storeId;

      if (id) {
        this.server.handleUpgrade(request, socket, head, (ws) =>
          this.server.emit("connection", id, ws)
        );
      } else {
        socket.destroy();
      }
    });
  }

  add(id, socket) {
    console.log("ws add");
    socket.isAlive = true;
    socket.on("pong", () => (socket.isAlive = true));
    socket.on("close", this.remove.bind(this, id));
    this.connections.set(id, socket);
  }

  send(id, message) {
    console.log("ws sending message");

    const connection = this.connections.get(id);

    connection.send(JSON.stringify(message));
  }

  broadcast(message) {
    console.log("ws broadcast");
    this.connections.forEach((connection) =>
      connection.send(JSON.stringify(message))
    );
  }

  isAlive(id) {
    return !!this.connections.get(id);
  }

  checkAll() {
    this.connections.forEach((connection) => {
      if (!connection.isAlive) {
        return connection.terminate();
      }

      connection.isAlive = false;
      connection.ping("");
    });
  }

  remove(id) {
    this.connections.delete(id);
  }

  close() {
    clearInterval(this.interval);
  }
}

module.exports = NotifierService;

routes.js

const express = require("express");

const router = express.Router();
module.exports = (webSocketNotifier) => {
  router.post("/newPurchase/:id", (req, res, next) => {
    webSocketNotifier.send(req.params.id, "purchase made");
    res.status(200).send();
  });
  return router;
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM