【问题标题】:How to setup route for websocket server in express?如何在 express 中为 websocket 服务器设置路由?
【发布时间】:2014-04-21 04:55:02
【问题描述】:

我有一个类似的设置:

var WebSocketServer = require("ws").Server,
    express = require("express"),
    http = require("http"),
    app = express(),
    server = http.createServer(app);

app.post("/login", login);
app.get("/...", callSomething);
// ...

server.listen(8000);


var wss = new WebSocketServer({server: server});

wss.on("connection", function(ws){
   // ...
});

我想把 WebSocketServer 放在一个特定的路径下,例如"...com/whatever"。问题是如何设置路径?有可能吗?

【问题讨论】:

    标签: javascript node.js express websocket


    【解决方案1】:

    使用 express-ws: https://www.npmjs.com/package/express-ws

    安装:

    npm i express-ws -S
    

    HTTP 服务器示例:

    const express = require('express')
    const enableWs = require('express-ws')
    
    const app = express()
    enableWs(app)
    
    app.ws('/echo', (ws, req) => {
        ws.on('message', msg => {
            ws.send(msg)
        })
    
        ws.on('close', () => {
            console.log('WebSocket was closed')
        })
    })
    
    app.listen(80)
    

    HTTPS 服务器示例:

    注意 我强烈建议使用 NodeJS 和 Internet 之间的中间服务器(例如 Nginx)来制作 HTTPS、压缩和缓存等功能,它的工作效率更高,并且其配置更容易更改未来

    const https     = require('https')
    const fs        = require('fs')
    const express   = require('express')
    const expressWs = require('express-ws')
    
    const serverOptions = {
      key: fs.readFileSync('key.pem'),
      cert: fs.readFileSync('cert.pem')
    }
    
    const app       = express()
    const server    = https.createServer(serverOptions, app)
    
    expressWs(app, server)
    
    app.ws('/echo', (ws, req) => {
        ws.on('message', msg => {
            ws.send(msg)
        })
    
        ws.on('close', () => {
            console.log('WebSocket was closed')
        })
    })
    
    server.listen(443)
    

    浏览器客户端示例:

    // wss: protocol is equivalent of https: 
    // ws:  protocol is equivalent of http:
    // You ALWAYS need to provide absolute address
    // I mean, you can't just use relative path like /echo
    const socketProtocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:')
    const echoSocketUrl = socketProtocol + '//' + window.location.hostname + '/echo/'
    const socket = new WebSocket(echoSocketUrl);
    
    socket.onopen = () => {
      socket.send('Here\'s some text that the server is urgently awaiting!'); 
    }
    
    socket.onmessage = e => {
      console.log('Message from server:', event.data)
    }
    

    【讨论】:

    • 感谢您的建议!我开始到处使用它。 const enableWs = require('express-ws'); enableWs(app); 比“正常”的方式可读性强很多
    • 当我尝试从网络浏览器连接时收到此错误:WebSocket connection to 'wss://xxx.xxx.xxx:8080/echo' failed: Connection closed before receiving a handshake response
    • @Ujeenator 我找到了this page 并由此推断我在使用https 时必须以不同的方式构造expressWs,即沿着var server = https.createServer(credentials, app); var expressWs = require("express-ws")(app,server) 的行。
    • @PeteAlvin 我添加了客户端示例
    • 自 2020 年起避免使用 express-ws - 包在 2 年内未更新
    【解决方案2】:

    您需要使用path 选项:

    var wss = new WebSocketServer({server: server, path: "/hereIsWS"});
    

    查看完整文档here

    【讨论】:

    • 谢谢,但是是否可以将其完全从 app.js 中移出并将其放到 app.get("/hereIsWS", runServer) 或者我基本上必须向服务器请求文件?
    • 不确定我是否理解。您正在尝试将程序拆分为多个文件?这应该没问题 - 但您必须有权访问 server 对象,而不是 app 对象。 express 只处理普通的 http 请求。
    • 这个答案不再是最新的。
    • 太棒了...对我有用new WebSocketServer({ port: x, path: y })。您如何从 README 文档中发现这一点对我来说是个谜 8-[
    • 客户端连接时如何获取客户端ID?
    【解决方案3】:

    UPDATE 路径在 ws 服务器选项中有效。

    interface ServerOptions {
            host?: string;
            port?: number;
            backlog?: number;
            server?: http.Server | https.Server;
            verifyClient?: VerifyClientCallbackAsync | VerifyClientCallbackSync;
            handleProtocols?: any;
            path?: string;
            noServer?: boolean;
            clientTracking?: boolean;
            perMessageDeflate?: boolean | PerMessageDeflateOptions;
            maxPayload?: number;
        }
    

    接受的答案不再有效,将引发Frame Header Invalid 错误。 Pull Request #885

    正如Lpinca 所说,WS 路径已被删除:

    这里的问题是每个 WebSocketServer 都为 HTTP 服务器上的升级事件以及该事件何时发出, 在所有服务器上调用 handleUpgrade。

    这里是解决方法:

    const wss1 = new WebSocket.Server({ noServer: true });
    const wss2 = new WebSocket.Server({ noServer: true });
    const server = http.createServer();
    
    server.on('upgrade', (request, socket, head) => {
      const pathname = url.parse(request.url).pathname;
    
      if (pathname === '/foo') {
        wss1.handleUpgrade(request, socket, head, (ws) => {
          wss1.emit('connection', ws);
        });
      } else if (pathname === '/bar') {
        wss2.handleUpgrade(request, socket, head, (ws) => {
          wss2.emit('connection', ws);
        });
      } else {
        socket.destroy();
      }
    });
    

    【讨论】:

    • 您的链接问题是关于使用多个套接字服务器安装多个套接字路径。与问题完全无关。
    • 路径没有被删除并且仍然可行
    【解决方案4】:

    要以 Ivan Kolyhalov 的方法为基础,可以通过将 WebSocketServer(或其任何属性)分配给 app.locals 来从任何端点访问 WebSocketServer。因此,您只需管理处理与server.js 中的 WebSocketServer 的连接。

    在下面的代码中,我们将 WebSocketServer 的 clients 属性分配给 app.locals,这允许我们通过向路由端点发出 HTTP 请求来向所有连接的客户端广播/推送自定义消息。

    server.js

    const { createServer } = require("http");
    const express = require("express");
    const WebSocket = require("ws");
    
    const app = express();
    app.use(express.json({ extended: false }));
    app.use("/api/pets", require("./routes/api/pets"));
    
    const port = process.env.PORT || 5000;
    const server = createServer(app);
    server.listen(port, () => console.info(`Server running on port: ${port}`));
    
    const webSocketServer = new WebSocket.Server({ server });
    webSocketServer.on("connection", (webSocket) => {
    
        console.info("Total connected clients:", webSocketServer.clients.size);
    
        app.locals.clients = webSocketServer.clients;
    });
    

    ./routes/api/pets.js

    const router = require("express").Router();
    const WebSocket = require("ws");
    
    const broadcast = (clients, message) => {
    
        clients.forEach((client) => {
    
            if (client.readyState === WebSocket.OPEN) {
    
                client.send(message);
            }
        });
    };
    
    router.get("/dog", (req, res) => {
    
        broadcast(req.app.locals.clients, "Bark!");
    
        return res.sendStatus(200);
    });
    
    router.get("/cat", (req, res) => {
    
        broadcast(req.app.locals.clients, "Meow!");
    
        return res.sendStatus(200);
    });
    
    module.exports = router;
    

    【讨论】:

      【解决方案5】:

      您可以通过将传入的套接字请求作为中间件来使用这个简单的想法,我发现这非常有用

      在您的 app.js 中

      const server = http.createServer(app)
      const WebSocket = require('ws');
      const ws = new WebSocket.Server({server});
      

      现在把中间件放在那里

      app.use(function (req, res, next) {
          req.ws = ws;
          return next();
      });
      

      或者,这显然更简单,而是:

      app.ws=ws;
      

      现在您的 ws 构造在您的路由器中可用,例如:

      // main user dashboard GET
      router.get('/', async function(req, res) {
      
              let ws = req.ws
      
              ws.once('connection', function connection(wss) {
                  wss.on('message', function incoming(message) {
                      console.log('received: %s', message);
                  });
      
                  wss.send(JSON.stringify('it works! Yeeee! :))' ));
              });
      });
      

      或者如果您通过 app.ws 将其附加到您的应用:

      // main user dashboard GET
      router.get('/', async function(req, res) {
          req.app.ws.once('connection', (wss) => {
                  console.log('connected:', req.app.ws.clients.size)
              });
      });
      

      请密切注意“ws.once”的使用,而不是“ws.on”,否则您将在每个请求的 websocket.server 的新实例上获得多个连接。

      干杯! :)

      【讨论】:

      • 我在 /bin/www 中声明了这些变量,并将中间件内容粘贴到 app.js 中并保留在 index.js 中。无法通过中间件传递值 ws
      • 不知道你做了什么以及如何做的。只需按照我提供的指南进行操作即可。
      • 在这个客户端需要重新加载才能获取新消息。是这样吗?
      猜你喜欢
      • 2017-10-17
      • 1970-01-01
      • 2021-08-27
      • 1970-01-01
      • 2020-06-17
      • 2021-02-07
      • 1970-01-01
      • 1970-01-01
      • 2017-01-03
      相关资源
      最近更新 更多