Как настроить маршрут для сервера websocket в экспресс?

У меня есть настройка, подобная этой:

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". Вопрос в том, как я могу установить путь? Возможно ли это?

Ответы

Ответ 1

Вы хотите использовать параметр path:

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

Смотрите полную документацию здесь

Ответ 2

Используйте экспресс-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-сервера:

УВЕДОМЛЕНИЕ Я настоятельно рекомендую использовать такие функции, как HTTPS, сжатие и кэширование, используя промежуточный сервер между NodeJS и Интернетом, например, 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)

Пример клиента браузера:

// 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\ some text that the server is urgently awaiting!'); 
}

socket.onmessage = e => {
  console.log('Message from server:', event.data)
}

Ответ 3

Принятый ответ более недействителен и будет вызывать ошибки Frame Header Invalid. Pull Request # 885.

WS Paths были удалены как Lpinca:

Проблема заключается в том, что каждый 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

Вы могли бы использовать эту простую идею, поместив входящие запросы сокетов в качестве промежуточного программного обеспечения, что я нашел довольно полезным

в вашем 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 при каждом запросе.

Ура! :)