In nodejs, HTTP protocol and WS protocol reuse the same port

Keywords: Javascript node.js http websocket

00. Preface

Recently, when I was writing a web page, I needed the back-end websocket service. Because I didn't need too complex functions, the back-end chose to use the nodejs websocket module of nodejs. During the development process, it was found that http service was needed, but nodejs websocket could not implement http service, so it began to find a method to solve the problem that the two protocols shared the same port.

01. Relevant issues

Generally speaking, each service needs to occupy a port when starting. For example, nodejs websocket mentioned above is shown in the following code (source: nodejs-websocket):

const ws = require("nodejs-websocket")
 
const server = ws.createServer(function (conn) {
    console.log("New connection")
    conn.on("text", function (str) {
        console.log("Received "+str)
        conn.sendText(str.toUpperCase()+"!!!")
    })
    conn.on("close", function (code, reason) {
        console.log("Connection closed")
    })
}).listen(8001)

Running the above code opens a websocket service to listen to port 8001.

If another http service, such as express, is running at this time, port conflicts will occur when listening to port 8001, as shown in the following code (source: express Chinese network):

const express = require('express')
const app = express()
const port = 8001

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})

If the above two services are run at the same time, an error prompt will appear: error: listen eaddinuse: address already in use::: 8001.

If you don't understand the meaning or purpose of port, you can refer to books related to computer network. Briefly, port is used to mark a service or application in computer communication, so that the transport layer can know which application to send a data packet to the application layer.

When the above two services are started, and the two services are independent and listen to the same port at the same time, port conflict will occur.

02. How to realize multiplexing port

When the client initiates a websocket connection, the client will send an HTTP request to the browser. The request header of the request includes Connection:Upgrade and Upgrade:websocket. It requests the server to modify the protocol to websocket. If the server agrees to modify the protocol, it will respond to an HTTP message with response code 101. So far, the responsibility of HTTP has been completed, and the following communication will be adopted Use websocket protocol. (Note: only when the client uses the instantiated websocket instance to transmit data, the websocket protocol will be used. During this period, the client can still initiate other HTTP requests, and the two do not conflict).

Thus, it can be obtained that the http service only needs to process all requests and replace the websocket service when encountering Connection:Upgrade and Upgrade:websocket. At this time, only the http listening port is needed, and the websocket does not need to listen externally, but is directly forwarded by the http service.

The following is the code to realize "reuse" port by using express, http and ws modules:

const express = require("express");
const WS_MODULE = require("ws");
const http = require("http");

const app = express();
const port = 80;

app.get("/hello", (req, res) => {
  res.send("hello world");
});

const server = http.createServer(app);
ws = new WS_MODULE.Server({ server });

ws.on("connection", (conn) => {
  conn.on("message", (str) => {
      //handle received message
  });
});

server.listen(port, () => {
  console.log("Server turned on, port number:" + port);
});

In the above code, the application can provide websocket and http services at the same time, and any request path under the domain name is handled in the same way when the switching protocol is websocket. That is, in this case, there is no difference between connecting ws://localhost and ws://localhost/walekj/awe/dacz.

If you want to use different websocket services for different websocket request paths, you can use the following code:

const express = require("express");
const WS_MODULE = require("ws");
const http = require("http");

const app = express();
const port = 80;

app.get("/hello", (req, res) => {
  res.send("hello world");
});

const server = http.createServer(app);

const chatWS = new WS_MODULE.Server({ noServer: true }); //noServer is used here
chatWS.on("connection", (conn) => {
  console.log("New client connection chatWS Process");
});

const fileWS = new WS_MODULE.Server({ noServer: true }); //noServer is used here
fileWS.on("connection", (conn) => {
  console.log("New client connection fileWS Process");
});

server.on("upgrade", (req, socket, head) => {
  if (req.url === "/chat") {
    //Handled by chatWS
    chatWS.handleUpgrade(req, socket, head, (conn) => {
      chatWS.emit("connection", conn, req);
    });
  } else if (req.url === "/file") {
    //Processed by fileWS
    fileWS.handleUpgrade(req, socket, head, (conn) => {
      fileWS.emit("connection", conn, req);
    });
  } else {
    //Close connection directly
    socket.destroy();
  }
});

server.listen(port, () => {
  console.log("Server turned on, port number:" + port);
});

Now you can connect to ws://localhost/chat and ws://localhost/file, while other URI s cannot establish a connection.

In this way, you can do other things before connecting, such as user authentication

03. Reference link & & API

ws

express

http

Posted by dev99 on Sat, 20 Nov 2021 09:40:02 -0800