nodejs中,实现HTTP协议和WS协议复用同一端口

00、前言

​ 最近在写一个网页,需要用到后端的websocket服务,由于不需要太复杂的功能,后端选择使用nodejs的nodejs-websocket模块。在开发过程中发现还需要http服务,但nodejs-websocket无法实现http服务,于是便开始寻求可以解决两个协议共用同一端口的方法。

01、有关问题

​ 一般来说,每个服务启动时都需占用一个端口。例如上面所提到的nodejs-websocket,如下代码所示(代码来源: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)

​ 运行以上代码开启了一个websocket服务,监听8001端口。

​ 如果这时再运行一个http服务,如express,同样监听8001端口就会出现端口冲突,如以下代码(代码来源:express中文网):

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}`)
})

​ 将上述两个服务同时运行,则会出现:Error: listen EADDRINUSE: address already in use :::8001错误提示。

如果你不太明白端口的意义或者用途,可以参考计算机网络相关书籍。简要来说,端口在计算机通讯中用以标记一个服务或应用,以便传输层知道将某数据包具体给应用层的哪个应用。

​ 以上启动了两个服务,而这两个服务又是独立的,且同时监听了同一端口,此时就会出现端口冲突的情况。

02、如何实现复用端口

​ 在客户端发起websocket连接时,客户端会向浏览器发送一个http请求,该请求的请求头包含Connection:UpgradeUpgrade:websocket,向服务器请求修改协议为websocket,如果服务器同意修改协议,则会响应一个响应码为101的HTTP报文,至此HTTP的职责已经完成,下面的通讯则会采用websocket协议。(注意:只有在客户端使用实例化的websocket实例传输数据才会采用websocket协议,在此期间客户端仍可以发起其他的http请求,两者并不冲突)。

​ 由此可得,只需让http服务处理所有的请求,在遇到Connection:UpgradeUpgrade:websocket时换用websocket服务即可,此时只需http监听端口,而websocket不必对外监听,而由http服务直接转发。

​ 下面给出采用expresshttpws模块实现"复用"端口的代码:

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("服务器已开启,端口号:" + port);
});

​ 以上代码中,该应用可同时提供websocket和http服务,且域名下任何的请求路径在切换协议为websocket时都是一样的处理方式。即:在这种情况下连接ws://localhostws://localhost/walekj/awe/dacz没有任何区别。

​ 如果想对不同的websocket请求路径采用不同的websocket服务,可以使用以下代码:

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
chatWS.on("connection", (conn) => {
    
    
  console.log("新的客户端连接 chatWS进行处理");
});

const fileWS = new WS_MODULE.Server({
    
     noServer: true }); //这里采用noServer
fileWS.on("connection", (conn) => {
    
    
  console.log("新的客户端连接 fileWS进行处理");
});

server.on("upgrade", (req, socket, head) => {
    
    
  if (req.url === "/chat") {
    
    
    //由chatWS 进行处理
    chatWS.handleUpgrade(req, socket, head, (conn) => {
    
    
      chatWS.emit("connection", conn, req);
    });
  } else if (req.url === "/file") {
    
    
    //由fileWS 进行处理
    fileWS.handleUpgrade(req, socket, head, (conn) => {
    
    
      fileWS.emit("connection", conn, req);
    });
  } else {
    
    
    //直接关闭连接
    socket.destroy();
  }
});

server.listen(port, () => {
    
    
  console.log("服务器已开启,端口号:" + port);
});

​ 现在可以连接ws://localhost/chatws://localhost/file,而其他的URI则无法建立连接。

​ 采用这种方式则可以在连接前做其他的事情,如用户身份验证

03、参考链接&&API

ws

express

http

猜你喜欢

转载自blog.csdn.net/qq_44856695/article/details/120250286