【问题标题】:How can I run a websocket server in next js custom server in dev mode如何在开发模式下在下一个 js 自定义服务器中运行 websocket 服务器
【发布时间】:2021-11-04 14:17:28
【问题描述】:

假设我想运行一个自定义的下一个 js 服务器,并在同一台服务器上接受 websocket 连接,我怎样才能避免破坏下一个 js 开发服务器热重载,它也在同一台服务器上使用 websockets...

const { createServer } = require('http')
const WebSocket = require("ws")
const { parse } = require('url')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
    const server = createServer((req, res) => handle(req, res, parse(req.url, true)))
    // pass the same server instance that is used by next js to the websocket server
    const wss = new WebSocket.Server({ server })

    wss.on("connection", async function connection(ws) {
      console.log('incoming connection', ws);    
      ws.onclose = () => {
        console.log('connection closed', wss.clients.size);
      };
    });

    server.listen(port, (err) => {
        if (err) throw err
        console.log(`> Ready on http://localhost:${port} and ws://localhost:${port}`)
    })
})

我相信该服务器应该在生产构建版本中工作,以便在用于处理下一个 js 请求的同一服务器实例上创建 websocket 服务器,但是当我尝试这样做时,热模块重新加载停止工作,并且错误出现在 chrome 开发工具控制台中,因为它期望由 webpack 处理的 websocket 连接现在由我的自定义 websocket 服务器处理。

我怎样才能以某种方式将开发服务器的 websocket 连接路由到 next 以及 webpack 和其他服务器到我自己的处理程序?

我知道我可以在另一个端口上运行我的 websocket 服务器,但我想在与下一个 js 相同的服务器实例和相同的端口上运行它。

【问题讨论】:

  • 我知道您提到您希望在与 Next.js 相同的服务器实例和端口上运行 WS,如何使用 http-proxy 将您的自定义 WS 请求转发到您的 WS 服务器?
  • @PsyGik 这意味着在它自己的端口上运行 WS 服务器,然后在另一个端口上运行 Next,然后在将它们联系在一起的第三个端口上运行 http-proxy?我期望它会起作用(不确定代理 WS 的细节),但我希望做一些更简单的事情,这样我就可以在一个端口上启动一台服务器。
  • 您只需要 2 台服务器。 1. Next.js 在它自己的端口上。 2. WS 服务器在它自己的端口上。然后在 Next.js 自定义服务器中使用 http-proxy 之类的东西将您的 WS 请求转发到您的自定义 WS 服务器。
  • 为了让 DX 更简单,您可以使用 npm-run-all 或类似的东西一次性启动所有服务..
  • 我实际上发现我可以从next.config.js 内启动 websocket 服务器,我知道这很奇怪,但是可以。我真的很想避免分配另一个端口

标签: node.js websocket next.js


【解决方案1】:

所以诀窍是创建一个 noServer 属性设置为 true 的 websocket 服务器,然后监听服务器升级事件,并根据路径名,不做任何事情以允许下一个 js 做它的事情,或者传递请求到我们创建的 websocket 服务器...

const wss = new WebSocket.Server({ noServer: true })

server.on('upgrade', function (req, socket, head) {
    const { pathname } = parse(req.url, true);
    if (pathname !== '/_next/webpack-hmr') {
        wss.handleUpgrade(req, socket, head, function done(ws) {
            wss.emit('connection', ws, req);
        });
    }
});

...所有这些都像这样...

const { createServer } = require('http')
const WebSocket = require("ws")
const { parse } = require('url')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
    const server = createServer((req, res) => handle(req, res, parse(req.url, true)))
    const wss = new WebSocket.Server({ noServer: true })

    wss.on("connection", async function connection(ws) {
      console.log('incoming connection', ws);
      ws.onclose = () => {
        console.log('connection closed', wss.clients.size);
      };
    });

    server.on('upgrade', function (req, socket, head) {
        const { pathname } = parse(req.url, true);
        if (pathname !== '/_next/webpack-hmr') {
            wss.handleUpgrade(req, socket, head, function done(ws) {
                wss.emit('connection', ws, req);
            });
        }
    });

    server.listen(port, (err) => {
        if (err) throw err
        console.log(`> Ready on http://localhost:${port} and ws://localhost:${port}`)
    })
})

【讨论】:

  • 感谢 @PsyGik 帮助我找到这个解决方案
【解决方案2】:

这是我使用 api 路由而不是创建自定义服务器在 Next.js 上创建 webSocket 服务器的答案。

/pages/api/websocketserverinit.js:
import { WebSocketServer } from 'ws';

const SocketHandler = async (req, res) => {
    if (res.socket.server.wss) {
        console.log('Socket is already running')
    } else {
        console.log('Socket is initializing')
        const server = res.socket.server
        const wss = new WebSocketServer({ noServer: true })
        res.socket.server.wss = wss
        
        server.on('upgrade', (req, socket, head) => {
            console.log("upgrade", req.url)
        
            if (!req.url.includes('/_next/webpack-hmr')) {
                wss.handleUpgrade(req, socket, head, (ws) => {
                    wss.emit('connection', ws, req);
                });
            }

        });

        wss.on('connection', (ws)=> {
            console.log("connection", ws);
            ws.on('message', (data) => {
                console.log('received: %s', data);
            })

            ws.send('something');
        });

    }
    res.end()
}

export default SocketHandler

您必须调用 api 路由才能从客户端(或服务器初始化脚本)启动 websocket 服务器:

fetch("http://localhost:3000/api/websocketserverinit")  

然后连接到它:

const ws = new WebSocket("ws://localhost:3000")

不是很好,但在某些情况下可能有用

【讨论】:

    猜你喜欢
    • 2011-11-17
    • 2022-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-02
    • 2012-03-08
    • 2012-01-18
    相关资源
    最近更新 更多