简体   繁体   English

如何在快递中为 websocket 服务器设置路由?

[英]How to setup route for websocket server in express?

I have a setup similar to this one:我有一个类似于这个的设置:

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){
   // ...
});

I would like to put the WebSocketServer under a specific path which may for instance be "...com/whatever" .我想将 WebSocketServer 放在特定路径下,例如"...com/whatever" The question is how can I set the path?问题是如何设置路径? Is it possible?可能吗?

You'll want to use the path option:您将要使用path选项:

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

See full documentation here在此处查看完整文档

Update: As of 06/12/2020 this package no longer works with newer versions of Express.更新:自 2020 年 6 月 12 日起,此软件包不再适用于较新版本的 Express。 Suggestion is to use the ws or socket.io packages.建议使用 ws 或 socket.io 包。

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

Installation:安装:

npm i express-ws -S

HTTP server example: 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 server example: HTTPS 服务器示例:

NOTICE I strongly recommend making such features as HTTPS, compression and caching using an intermediate server between NodeJS and Internet, for example Nginx, it works much more efficiently and its configuration will be easier to change in the future注意我强烈建议使用 NodeJS 和 Internet 之间的中间服务器来制作 HTTPS、压缩和缓存等功能,例如 Nginx,它的工作效率更高,并且将来更容易更改其配置

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)

Browser client example:浏览器客户端示例:

// 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)
}

UPDATE Paths are valid in the ws server options. 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;
    }

The accepted answer is no longer valid and will throw Frame Header Invalid errors.接受的答案不再有效,并且会抛出Frame Header Invalid错误。 Pull Request #885 .拉取请求 #885

WS Paths were removed as Lpinca puts it:正如Lpinca所说,WS 路径已被删除:

The problem here is that each WebSocketServer adds a new listener for the upgrade event on the HTTP server and when that event is emitted, handleUpgrade is called on all servers.这里的问题是每个 WebSocketServer 都会为 HTTP 服务器上的升级事件添加一个新的侦听器,当该事件发出时,所有服务器上都会调用 handleUpgrade。

Here is the work around:这是解决方法:

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

you could use this simple idea by putting incoming socket requests as a middleware, which I found to be pretty useful您可以通过将传入的套接字请求作为中间件来使用这个简单的想法,我发现这非常有用

in your app.js在你的 app.js 中

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

now put middleware there现在把中间件放在那里

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

or, which obviously is a bit simpler, this instead:或者,这显然有点简单,而是:

app.ws=ws;

now your ws construct is available in your routers, for example:现在您的 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! :))' ));
        });
});

or if you attached it to your app by app.ws:或者如果您通过 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)
        });
});

pay very close attention to usage of "ws.once", not "ws.on", or you'll get multiple connections at new instances of websocket.server on each request.非常注意“ws.once”的使用,而不是“ws.on”,否则你会在每个请求的 websocket.server 的新实例上获得多个连接。

Cheers!干杯! :) :)

To build upon Ivan Kolyhalov's approach, it's possible to access the WebSocketServer from any endpoint by assigning it (or any of its properties) to app.locals .为了建立在 Ivan Kolyhalov 的方法之上,可以通过将 WebSocketServer(或其任何属性)分配给app.locals来从任何端点访问 WebSocketServer。 Therefore, you only have to manage handling connections to the WebSocketServer in server.js .因此,您只需在server.js管理与 WebSocketServer 的处理连接。

In the code below we assign the clients property of the WebSocketServer to app.locals , which allows us to broadcast/push a custom message to all connected clients simply by making an HTTP request to the routed endpoints.在下面的代码中,我们将 WebSocketServer 的clients属性分配给app.locals ,这允许我们通过向路由端点发出 HTTP 请求来向所有连接的客户端广播/推送自定义消息。

server.js服务器.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 ./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;

First, all comments on this topic was very helpful for me to understand routing and ws, I'm kind off newbie there.首先,关于这个主题的所有评论对我理解路由和ws非常有帮助,我对那里的新手很友好。 I tried all, none of them was working for me.我尝试了所有,没有一个对我有用。 My objectives was simply to add web socket to server.js which already have routes there.我的目标只是将 web 套接字添加到已经有路由的 server.js 中。 And when I see solution by Ivan Kolyhalov, he was using req.ws for assigning web sockets in routers, so why not to use req.socket.server to get server obj there in router file.当我看到 Ivan Kolyhalov 的解决方案时,他正在使用 req.ws 在路由器中分配 web sockets,那么为什么不使用 req.socket.server 在路由器文件中获取服务器 obj。 So, this is what I did, but it take sometime to figure it out.所以,这就是我所做的,但需要一些时间才能弄清楚。 Please, let me know, if you think I did it wrong.请告诉我,如果你认为我做错了。 PS. PS。 I handle disconnect on the client side and request upgrade via nginx proxy.我在客户端处理断开连接并通过 nginx 代理请求升级。

server.js服务器.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 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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM