express中创建 websocket 接口及问题解答

目录
  • 整理 express-ws 源码
  • 摒弃 express-ws
  • 自己思考实现 express-ws 并解决它存在的问题
  • 为什么要折腾这个?

大多数文章里的方法是直接安装 express-ws 然后进行使用:

const express = require(`express`)
const server = express()
const expressWs = require(`express-ws`)
expressWs(server)

server.ws(`/aaa`, (ws, req) => {
  console.log(`socket`, req.testing)
  ws.send(`aaa`)
  ws.on(`message`, function(msg) {
    console.log(msg)
  })
})
server.get(`/bbb`, (req, res) => {
  res.json(`bbb`)
})
server.listen(3040)

可以看到在上面创建了一个 ws://127.0.0.1:3040/aaa 这样的 websoket 接口, 然而当我们尝试 new WebSocket("ws://127.0.0.1:3040/ccc") 时, 也能连接上, 这虽然不会影响我们的业务, 但没有创建的接口能连上, 显然是很不好的.

在 express-ws 的 github 仓库和问题列表中看了很久没有找到解决方法, 然后就去看它的代码. 发现代码不多, 却拆分了很多文件, 然而代码没多少我却很难看懂.

整理 express-ws 源码

然后先合并一些分散在各个文件里的代码和注释:

function expressWs(app, httpServer, options = {}) {
  addWsMethod(app)
  const server = http.createServer(app)
  app.listen = function serverListen(...args) {
    return server.listen(...args)
  }
  const wsServer = new ws.Server({server})
  wsServer.on(`upgrade`, async (req, socket, upgradeHead) => {
    const res = new http.ServerResponse(req)
    return server(req, res)
  })
  wsServer.on(`connection`, (socket, request) => {
    if (`upgradeReq` in socket) {
      request = socket.upgradeReq
    }
    request.ws = socket
    request.wsHandled = false
    request.url = websocketUrl(request.url)
    const dummyResponse = new http.ServerResponse(request)
    dummyResponse.writeHead = function writeHead(statusCode) {
      if (statusCode > 200) {
        dummyResponse._header = `` // eslint-disable-line no-underscore-dangle
        socket.close()
      }
    }
    app.handle(request, dummyResponse, () => {
      if (!request.wsHandled) {
        socket.close()
      }
    })
  })
}

function trailingSlash(string) {
  let suffixed = string
  if (suffixed.charAt(suffixed.length - 1) !== `/`) {
    suffixed = `${suffixed}/`
  }
  return suffixed
}

function websocketUrl(url) {
  if (url.indexOf(`?`) !== -1) {
    const [baseUrl, query] = url.split(`?`)
    return `${trailingSlash(baseUrl)}.websocket?${query}`
  }
  return `${trailingSlash(url)}.websocket`
}

function addWsMethod(target) {
  if (!target.ws) {
    target.ws = function (route, ...middlewares) {
      const wrappedMiddlewares = middlewares.map((function (middleware) {
        return (req, res, next) => {
          if (req.ws) {
            req.wsHandled = true
            try {
              middleware(req.ws, req, next)
            } catch (err) {
              next(err)
            }
          } else {
            next()
          }
        }
      }))
      const wsRoute = websocketUrl(route)
      this.get(...[wsRoute].concat(wrappedMiddlewares))
      return this
    }
  }
}

把源码整理出来, 看起来确实没多少. 然而还是有些难看懂, 并且没有解决上面所说的没有创建的接口也会连接的问题, 那么我整理源代码有何用?

拿着源码改了半天, 还是没有解决. 所以, 再去一下层看看能不能找到方法.

于是就来到了 express-ws 使用的 ws, 看 ws 的使用量和活跃度都十分可观, 应该能解决我的问题, 如果不能解决, 就是我的问题~~~

摒弃 express-ws

先脱离 express-ws 自身的那个思想, 仔细看了半天文档, 看到 readme 里有有个示例: 演示了从 ws 连接时如何根据路由分发到不同的 ws 服务.

import { createServer } from 'http';
import { parse } from 'url';
import { WebSocketServer } from 'ws';

const server = createServer();
const wss1 = new WebSocketServer({ noServer: true });
const wss2 = new WebSocketServer({ noServer: true });

wss1.on('connection', function connection(ws) {
  // ...
});

wss2.on('connection', function connection(ws) {
  // ...
});

server.on('upgrade', function upgrade(request, socket, head) {
  const { pathname } = parse(request.url);

  if (pathname === '/foo') {
    wss1.handleUpgrade(request, socket, head, function done(ws) {
      wss1.emit('connection', ws, request);
    });
  } else if (pathname === '/bar') {
    wss2.handleUpgrade(request, socket, head, function done(ws) {
      wss2.emit('connection', ws, request);
    });
  } else {
    socket.destroy();
  }
});

server.listen(8080);

分发服务例子里有了演示, 但是能不能做到 express 那样通过 app.ws('/a') app.ws('/b') 的形式进行分发呢? 能不能实现像 express 一样支持路径参数呢?

当前 express-ws 是没有支持路径参数的, 并且已经有一年没有活跃了.

自己思考实现 express-ws 并解决它存在的问题

首先 app.ws 可以直接在 express 实例是通过 app.ws = fn 的实现. 然后 ws 对应的不同路由对应到不同的处理方法如何实现呢?

那就先以路由为 key, 然后处理方法为 value 保存起来, 到时候根据访问的 url 查找对应的 key/value 执行就行.

如何根据 url 查找 key? 我们在 ws 提供的示例中发现在 server.on('upgrade') 这一层可以进行查找和分发, 然后好像思路上没什么大问题, 测试了一下确实可以.

以下是代码:

function expressWs(app) {
  const server = http.createServer(app)
  server.on(`upgrade`, (req, socket, head) => {
    const obj = app.wsRoute[req.url]
    obj ? obj.wss.handleUpgrade(req, socket, head, ws => obj.mid(ws, req)) : socket.destroy()
  })
  app.listen = (...arg) => server.listen(...arg)
  app.ws = (route, mid) => {
    app.wsRoute = app.wsRoute || {}
    app.wsRoute[route] = {
      wss: new Server({ noServer: true }),
      mid,
    }
  }
}

使用方式依然是:

const express = require(`express`)
const server = express()
expressWs(server)

server.ws(`/aaa`, (ws, req) => {
  console.log(`socket`, req.testing)
  ws.send(`aaa`)
  ws.on(`message`, function(msg) {
    console.log(msg)
  })
})
server.get(`/bbb`, (req, res) => {
  res.json(`bbb`)
})
server.listen(3040)

测试了一下连接不存在的 ws 接口, 能按预期工作, 不存在就连接不上, 存在的能连接上, http 接口也能正常工作.

但是, 我的代码好像更少呢~~~

接下来要实现其他的功能就很简单了: 例如

  • 支持 express 的路由模式, 例如 /ws/:id
  • 支持读取 params, query 参数

为什么要折腾这个?

因为在重构 https://github.com/wll8/mockm

到此这篇关于express中创建 websocket 接口及问题解答的文章就介绍到这了,更多相关express创建 websocket 接口内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • node.js基于express使用websocket的方法

    本文实例讲述了node.js基于express使用websocket的方法.分享给大家供大家参考,具体如下: 这个效果我也是翻了好长时间的资料,测试才成功的,反正成功,大家看看吧 首先你需要安装socket.io模块 npm install socket.io --save 然后打开express的app.js将模块引入,在12行左右的 var app = express(); 下面添加两行 var server = require('http').Server(app); var io = r

  • express框架实现基于Websocket建立的简易聊天室

    最近想写点有意思的,所以整了个这个简单的不太美观的小玩意 首先你得确认你的电脑装了node,然后就可以按照步骤 搞事情了~~ 1.建立一个文件夹 2.清空当前文件夹地址栏,在文件夹地址栏中输入cmd.exe 3.我们需要下载点小东西 ,需要在命令行输入 npm install express 回车 等待一会 npm install express-session 回车 等待一会 npm install ejs 回车 等待一会 npm install socket.io 回车 等待一会 叮~~~

  • express中创建 websocket 接口及问题解答

    目录 整理 express-ws 源码 摒弃 express-ws 自己思考实现 express-ws 并解决它存在的问题 为什么要折腾这个? 大多数文章里的方法是直接安装 express-ws 然后进行使用: const express = require(`express`) const server = express() const expressWs = require(`express-ws`) expressWs(server) server.ws(`/aaa`, (ws, req

  • 使用Go语言创建WebSocket服务的实现示例

    今天介绍如何用 Go 语言创建 WebSocket 服务,文章的前两部分简要介绍了 WebSocket 协议以及用 Go 标准库如何创建 WebSocket 服务.第三部分实践环节我们使用了 gorilla/websocket 库帮助我们快速构建 WebSocket 服务,它帮封装了使用 Go 标准库实现 WebSocket 服务相关的基础逻辑,让我们能从繁琐的底层代码中解脱出来,根据业务需求快速构建 WebSocket 服务. Go Web 编程系列的每篇文章的源代码都打了对应版本的软件包,供

  • node.js中express模块创建服务器和http模块客户端发请求

    首先下载express模块,命令行输入 npm install express 1.node.js中express模块创建服务端 在js代码同文件位置新建一个文件夹(www_root),里面存放网页文件等,就可以在浏览器中访问了 var express = require("express"); var path = require("path"); var app = express(); //目录 (当前目录下的www_root目录) app.use(expre

  • php中使用websocket详解

    在PHP中,开发者需要考虑的东西比较多,从socket的连接.建立.绑定.监听等都需要开发者自己去操作完成,对于初学者来说,难度方面也挺大的,所以本文的思路如下: 1.socket协议的简介 2.介绍client与server之间的连接原理 3.PHP中建立socket的过程讲解 4.用一个聊天室作为实例详细讲解在PHP中如何使用socket 一.socket协议的简介 WebSocket是什么,有什么优点 WebSocket是一个持久化的协议,这是相对于http非持久化来说的. 举个简单的例子

  • vue中对接Graphql接口的实现示例

    说明: 本文是本人正在搞nestjs+graphql+serverless训练营中对Graphql讲解的基础知识点,可能有点前后没对接上,文中提到的Graphql授权也是下小节介绍的 一.对原来的Express返回Graphql项目修改 本章节使用的代码是express返回Graphql的代码,在使用前要先对代码进行基本的配置,比如处理跨域问题(Graphql本质也是发送一个http请求,既然是这样在vue项目中自然存在跨域的问题,需要先处理) 1.安装跨域的包,并且配置中间件 npm inst

  • nodejs使用Express框架写后端接口的全过程

    目录 路由和接口 接口传参 GET接口 POST接口-普通键值对 POST接口-JSON POST接口-form-data文件上传 总结 在写接口前,我们要明白什么是接口,在前端我们叫接口,而后端就叫路由,这其实是指同一个东西. 路由和接口 路由(Routing)是由一个URL(或者叫路径标识)和一个特定的HTTP方法(GET.POST等)组成的,涉及到应用如何处理响应客户端请求.每一个路由都可以有一个或多个处理器函数,当匹配到路由时,这些个函数将被执行. // 引入express const

  • NodeJs Express框架实现服务器接口详解

    目录 CORS 跨域资源共享 1. 接口的跨域问题 2. 使用 cors 中间件解决跨域问题 创建基本的服务器 创建 API 路由模块 编写 GET 接口 编写 POST 接口 注意:如果要获取 URL-encoded 格式的请求体数据,必须配置中间件 app.use(express.urlencoded({ extended: false })) CORS 跨域资源共享 1. 接口的跨域问题 <body> <button id="btnGET">GET<

  • Android中使用WebSocket实现群聊和消息推送功能(不使用WebView)

    WebSocket protocol 是HTML5一种新的协议.它实现了浏览器与服务器全双工通信(full-duplex).WebSocket是Web2.0时代的新产物,用于弥补HTTP协议的某些不足,不过他们之间真实的关系是兄弟关系,都是对socket的进一步封装,其目前最直观的表现就是服务器推送和聊天功能.更多知识参考:如何理解 TCP/IP, SPDY, WebSocket 三者之间的关系? 今天的重点是讲如何在Android中脱离WebView使用WebSocket,而不是在Web浏览器

  • c#中抽象类和接口的详细介绍

    一.抽象类:抽象类是特殊的类,只是不能被实例化:除此以外,具有类的其他特性:重要的是抽象类可以包括抽象方法,这是普通类所不能的.抽象方法只能声明于抽象类中,且不包含任何实现,派生类必须覆盖它们.另外,抽象类可以派生自一个抽象类,可以覆盖基类的抽象方法也可以不覆盖,如果不覆盖,则其派生类必须覆盖它们. 二.接口:接口是引用类型的,类似于类,和抽象类的相似之处有三点: 1.不能实例化: 2.包含未实现的方法声明: 3.派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他

  • PHP中抽象类、接口的区别与选择分析

    本文实例分析了PHP中抽象类.接口的区别与选择.分享给大家供大家参考,具体如下: 区别: 1.对接口的使用是通过关键字implements.对抽象类的使用是通过关键字extends.当然接口也可以通过关键字extends继承. 2.接口中不可以声明成员变量(包括类静态变量),但是可以声明类常量.抽象类中可以声明各种类型成员变量,实现数据的封装.(另JAVA接口中的成员变量都要声明为public static final类型) <?php abstract class Father { funct

随机推荐