如何在express中为WebSocket服务器设置路由?

pxy2qtax  于 2022-11-11  发布在  其他
关注(0)|答案(6)|浏览(252)

我有一个类似的设置:

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"。问题是我如何设置路径?可以吗?

l3zydbqr

l3zydbqr1#

您将需要使用path选项:

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

请参阅完整文档here

k4aesqcs

k4aesqcs2#

使用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和互联网之间使用中间服务器(例如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)
}
hmtdttj4

hmtdttj43#

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();
  }
});
xdyibdwo

xdyibdwo4#

您可以通过将传入的套接字请求作为中间件来使用这个简单的想法,我发现这非常有用
在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! :))' ));
        });
});

或者如果您通过www.example.com将其附加到应用程序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的新示例上获得多个连接。
干杯!:)

nom7f22z

nom7f22z5#

为了基于Ivan Kolyhalov的方法进行构建,可以通过将WebSocketServer(或其任何属性)分配给app.locals来从任何端点访问WebSocketServer。因此,您只需管理server.js中WebSocketServer的连接处理。
在下面的代码中,我们将WebSocketServer的clients属性指定为app.locals,这样我们就可以通过向路由端点发出HTTP请求,将自定义消息广播/推送到所有连接的客户端。

服务器.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;
});

./路线/api/宠物.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;
xam8gpfp

xam8gpfp6#

首先,所有关于这个主题的评论对我理解路由和ws都很有帮助,我是个新手。我尝试了所有的方法,没有一个对我有用。我的目标只是把Web Socket添加到已经有路由的server.js中。当我看到Ivan Kolyhalov的解决方案时,他正在使用req.ws来分配路由器中的web套接字。所以为什么不使用req.socket.server来获取路由器文件中的服务器对象。所以,这就是我所做的,但是它花了一些时间来弄清楚。请,让我知道,如果你认为我做错了。PS.我处理客户端的断开连接,并通过nginx代理请求升级。
server.js

express = require('express'),
    route1  = require('./routers/v1'),
    route2  = require('./routers/v2'),
    ws_route = require('./routers/api');
    http = require('http');
    app = express();

    const port = 3001;
    const server = http.createServer(app);

    server.listen(port, () => {
      console.log(`Server is running on port ${port}`);
    });

   app.use('/api', wsroute);
   app.use('/v1',  route1);
   app.use('/v2',  route2);

api.js

const express = require("express");
   const router = express.Router();
   var WebSocketServer = require("ws").Server;
   const wslib =  require('../server/ws_lib');

   router.get('/stats', async function (req, res) {
        wsserver = req.socket.server;
        var wss = new WebSocketServer({server:wsserver});
        wss.on('connection', function connection(ws, request) {
             console.log(new Date() + ' | A new client is connected.');
             console.log("Total Number of clients",wss.clients.size);
             var  connectionParams;
             var [_path, params] = request.url.split("?");
             connectionParams = new URLSearchParams(params);
             console.log('Using path', _path);
             var refresh_rate = 1000;
             if ( ! connectionParams.has("app")){
                 intid = wslib.send_messages_with_interval(wss, refresh_rate, "all", wslib.build_message);
             }
             else{
                 var app=connectionParams.get("app");
                 intid= wslib.send_messages_with_interval(wss, refresh_rate, app, wslib.build_message);
             }
             ws.on('close', () => {
                 console.log('Client has disconnected!');
                 clearInterval(intid);
             });
        });
    });
    module.exports = router;

相关问题