如何在开发模式下在下一个 js 自定义服务器中运行 websocket 服务器

Posted

技术标签:

【中文标题】如何在开发模式下在下一个 js 自定义服务器中运行 websocket 服务器【英文标题】:How can I run a websocket server in next js custom server in dev mode 【发布时间】: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 服务器,我知道这很奇怪,但是可以。我真的很想避免分配另一个端口 【参考方案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")

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

【讨论】:

以上是关于如何在开发模式下在下一个 js 自定义服务器中运行 websocket 服务器的主要内容,如果未能解决你的问题,请参考以下文章

如何使用像filezilla这样的ftp服务器将没有vercel的下一个js应用程序部署到自定义服务器

使用JSP+Servlet+JavaBean的模式开发一个Web程序,将表单提交的数据显示在下一页面中。

如何在下一个 js 中从节点模块外部的文件夹中导入模块

带有自定义 webpack 构建的 vscode 节点调试器

将自定义元素添加到 ngRepeat 列表

nextjs - 在开发模式下使用 _error.js