express中創(chuàng)建 websocket 接口及問(wèn)題解答
大多數(shù)文章里的方法是直接安裝 express-ws
然后進(jìn)行使用:
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)
可以看到在上面創(chuàng)建了一個(gè) ws://127.0.0.1:3040/aaa
這樣的 websoket 接口, 然而當(dāng)我們嘗試 new WebSocket("ws://127.0.0.1:3040/ccc")
時(shí), 也能連接上, 這雖然不會(huì)影響我們的業(yè)務(wù), 但沒(méi)有創(chuàng)建的接口能連上, 顯然是很不好的.
在 express-ws 的 github 倉(cāng)庫(kù)和問(wèn)題列表中看了很久沒(méi)有找到解決方法, 然后就去看它的代碼. 發(fā)現(xiàn)代碼不多, 卻拆分了很多文件, 然而代碼沒(méi)多少我卻很難看懂.
整理 express-ws 源碼
然后先合并一些分散在各個(gè)文件里的代碼和注釋:
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 } } }
把源碼整理出來(lái), 看起來(lái)確實(shí)沒(méi)多少. 然而還是有些難看懂, 并且沒(méi)有解決上面所說(shuō)的沒(méi)有創(chuàng)建的接口也會(huì)連接的問(wèn)題, 那么我整理源代碼有何用?
拿著源碼改了半天, 還是沒(méi)有解決. 所以, 再去一下層看看能不能找到方法.
于是就來(lái)到了 express-ws
使用的 ws
, 看 ws
的使用量和活躍度都十分可觀, 應(yīng)該能解決我的問(wèn)題, 如果不能解決, 就是我的問(wèn)題~~~
摒棄 express-ws
先脫離 express-ws
自身的那個(gè)思想, 仔細(xì)看了半天文檔, 看到 readme 里有有個(gè)示例: 演示了從 ws 連接時(shí)如何根據(jù)路由分發(fā)到不同的 ws 服務(wù).
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);
分發(fā)服務(wù)例子里有了演示, 但是能不能做到 express 那樣通過(guò) app.ws('/a')
app.ws('/b')
的形式進(jìn)行分發(fā)呢? 能不能實(shí)現(xiàn)像 express 一樣支持路徑參數(shù)呢?
當(dāng)前 express-ws
是沒(méi)有支持路徑參數(shù)的, 并且已經(jīng)有一年沒(méi)有活躍了.
自己思考實(shí)現(xiàn) express-ws 并解決它存在的問(wèn)題
首先 app.ws 可以直接在 express 實(shí)例是通過(guò) app.ws = fn
的實(shí)現(xiàn). 然后 ws 對(duì)應(yīng)的不同路由對(duì)應(yīng)到不同的處理方法如何實(shí)現(xiàn)呢?
那就先以路由為 key, 然后處理方法為 value 保存起來(lái), 到時(shí)候根據(jù)訪問(wèn)的 url 查找對(duì)應(yīng)的 key/value 執(zhí)行就行.
如何根據(jù) url 查找 key? 我們?cè)?ws 提供的示例中發(fā)現(xiàn)在 server.on('upgrade')
這一層可以進(jìn)行查找和分發(fā), 然后好像思路上沒(méi)什么大問(wèn)題, 測(cè)試了一下確實(shí)可以.
以下是代碼:
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)
測(cè)試了一下連接不存在的 ws 接口, 能按預(yù)期工作, 不存在就連接不上, 存在的能連接上, http 接口也能正常工作.
但是, 我的代碼好像更少呢~~~
接下來(lái)要實(shí)現(xiàn)其他的功能就很簡(jiǎn)單了: 例如
- 支持 express 的路由模式, 例如
/ws/:id
- 支持讀取 params, query 參數(shù)
為什么要折騰這個(gè)?
因?yàn)樵谥貥?gòu) https://github.com/wll8/mockm
到此這篇關(guān)于express中創(chuàng)建 websocket 接口及問(wèn)題解答的文章就介紹到這了,更多相關(guān)express創(chuàng)建 websocket 接口內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
NodeJS項(xiàng)目如何打包成可執(zhí)行文件
這篇文章主要介紹了NodeJS項(xiàng)目如何打包成可執(zhí)行文件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10Nodejs中怎么實(shí)現(xiàn)函數(shù)的串行執(zhí)行
今天小編就為大家分享一篇關(guān)于Nodejs中怎么實(shí)現(xiàn)函數(shù)的串行執(zhí)行,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03實(shí)現(xiàn)一個(gè)完整的Node.js RESTful API的示例
本篇文章主要介紹了實(shí)現(xiàn)一個(gè)完整的Node.js RESTful API的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09詳解如何利用Nodejs構(gòu)建多進(jìn)程應(yīng)用
這篇文章主要為大家介紹了如何利用Nodejs構(gòu)建多進(jìn)程應(yīng)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10PHPStorm中如何對(duì)nodejs項(xiàng)目進(jìn)行單元測(cè)試詳解
這篇文章主要給大家介紹了關(guān)于PHPStorm中如何對(duì)nodejs項(xiàng)目進(jìn)行單元測(cè)試的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02詳解nodejs的express如何自動(dòng)生成項(xiàng)目框架
本篇文章主要介紹了nodejs的express如何自動(dòng)生成項(xiàng)目框架,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07Nodejs中koa2連接mysql的實(shí)現(xiàn)示例
本文主要介紹了Nodejs中koa2連接mysql的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07