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