skip to Main Content

I’m trying to build my current project it in a way so my friends can create servers on one network so we can connect to each other (LAN communication, pretty much). The only problem is when I run my program in two terminal windows, my second run fails and throws errors at me because ports are already taken, which is logical.

I am working with Vite, express, and ws. This is one of my server’s code.

const { WebSocketServer } = require('ws')

const sockserver1 = new WebSocketServer({ port: 311 })
sockserver1.on('connection', ws => {
    sockserver1.clients.forEach(client => {
        client.send(`A new player connection has been established`);
    })

    ws.on('message', data => {
        sockserver1.clients.forEach(client => {
            client.send(`${data}`)
        })
    })

    ws.on('close', () => {
        sockserver1.clients.forEach(client => {
            client.send(`A player has disconnected`)
        })
    })

    ws.onerror = function () {
        console.log('websocket error')
    }
});

When I run this the second time, it will fail and say that port ::311 is taken. How can I add 1 to the port number till I find one that isn’t taken? I have notice Vite does this.

2

Answers


  1. Let the OS determine the port.

    If you set the port to 0, the OS will assign it a random ephemeral port, so that way you don’t have to worry about assigning the port at all. No taken ports, no testing for ports, and no race conditions.

    Here is what that would look like:

    const { WebSocketServer } = require('ws')
    
    const sockserver1 = new WebSocketServer({ port: 0 })
    sockserver1.on('listening', () => {
        const address = sockserver1.address();
        console.log(`WebSocket server is listening on port ${address.port}`);
    });
    
    sockserver1.on('connection', ws => {
        sockserver1.clients.forEach(client => {
            client.send(`A new player connection has been established`);
        })
    
        ws.on('message', data => {
            sockserver1.clients.forEach(client => {
                client.send(`${data}`)
            })
        })
    
        ws.on('close', () => {
            sockserver1.clients.forEach(client => {
                client.send(`A player has disconnected`)
            })
        })
    
        ws.onerror = function () {
            console.log('websocket error')
        }
    });
    

    I also, of course, added a method that prints the port.

    Login or Signup to reply.
  2. Inspired by @AnstonSorensen answer, with the knowledge that WebSocketServer can take a server option (created using node http module) instead of port, here’s a solution that would not risk the mentioned "race condition"

    import { WebSocketServer } from "ws";
    import http from "http";
    
    const createServer = (port, callback) => {
        try {
            const server = http.createServer((req, res) => {
                // Duplicate what new WebSocketServer does
                // when passed a port
                const body = http.STATUS_CODES[426];
    
                res.writeHead(426, {
                    "Content-Length": body.length,
                    "Content-Type": "text/plain",
                });
                res.end(body);
            });
            server.on("error", (err) => {
                const { code, port, syscall } = err;
                if (syscall === "listen" && code === "EADDRINUSE") {
                    return createServer(port + 1, callback);
                }
                callback(err);
            });
            server.listen(port, (err) => {
                callback(err, { server, port });
            });
        } catch (err) {
            callback(err);
        }
    };
    createServer(3111, (err, { server, port }) => {
        if (err) {
            // handle non EADDRINUSE error here
            return;
        }
        console.log(`using port ${port}`);
        const sockserver1 = new WebSocketServer({ server });
        sockserver1.on("connection", (ws) => {
            sockserver1.clients.forEach((client) => {
                client.send(`A new player connection has been established`);
            });
    
            ws.on("message", (data) => {
                sockserver1.clients.forEach((client) => {
                    client.send(`${data}`);
                });
            });
    
            ws.on("close", () => {
                sockserver1.clients.forEach((client) => {
                    client.send(`A player has disconnected`);
                });
            });
    
            ws.onerror = function () {
                console.log("websocket error");
            };
        });
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search