如何在express中设置websocket服务器的路由?

010*_*101 32 javascript websocket node.js express

我有一个类似于这个的设置:

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){
   // ...
});
Run Code Online (Sandbox Code Playgroud)

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

Aar*_*our 40

您将要使用该path选项:

var wss = new WebSocketServer({server: server, path: "/hereIsWS"});
Run Code Online (Sandbox Code Playgroud)

请参阅此处的完整文档

  • 这个答案不再是最新的. (6认同)
  • 谢谢,但是是否可以将其完全从 app.js 移走并将其放入 app.get("/hereIsWS", runServer) 或者我基本上必须向服务器请求文件? (2认同)

uje*_*tor 27

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

安装:

npm i express-ws -S
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

Browser客户端示例:

// 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)
}
Run Code Online (Sandbox Code Playgroud)

  • @Ujeenator我发现[本页](https://github.com/HenningM/express-ws/blob/master/examples/https.js)并且从推断出我必须在使用https时构建expressWs有点不同,即``var server = https.createServer(credentials,app); var expressWs = require("express-ws")(app,server)`. (4认同)
  • 当我尝试从Web浏览器连接时出现此错误:`WebSocket连接到'wss://xxx.xxx.xxx:8080/echo'失败:连接在收到握手响应之前关闭. (3认同)
  • 从 2020 年起避免使用 express-ws - 该软件包已 2 年内未更新 (3认同)
  • 感谢您的建议!我开始到处使用这个。`const enableWs = require('express-ws'); enableWs(app);` 比“正常”方式更具可读性。 (2认同)

Que*_*fat 18

接受的答案不再有效,会引发Frame Header Invalid错误.请求#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();
  }
});
Run Code Online (Sandbox Code Playgroud)


The*_*978 10

为了建立在 Ivan Kolyhalov 的方法之上,可以通过将 WebSocketServer(或其任何属性)分配给app.locals. 因此,您只需在server.js.

在下面的代码中,我们将clientsWebSocketServer的属性分配给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;
});
Run Code Online (Sandbox Code Playgroud)

./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;
Run Code Online (Sandbox Code Playgroud)


Iva*_*lov 6

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

在您的app.js中

const server = http.createServer(app)
const WebSocket = require('ws');
const ws = new WebSocket.Server({server});
Run Code Online (Sandbox Code Playgroud)

现在把中间件放在那里

app.use(function (req, res, next) {
    req.ws = ws;
    return next();
});
Run Code Online (Sandbox Code Playgroud)

或者,这显然更简单一些:

app.ws=ws;
Run Code Online (Sandbox Code Playgroud)

现在您的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! :))' ));
        });
});
Run Code Online (Sandbox Code Playgroud)

或者如果您通过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)
        });
});
Run Code Online (Sandbox Code Playgroud)

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

干杯! :)