本文记录一次koa2环境下使用原生node ws模块的过程
-
首先需要引入ws模块(本人使用的是koa-generator初始化的项目,对应createserver操作在/bin/www.js文件中),ws模块是nodejs原生模块,不太依赖任何库,文档地址:https://www.npmjs.com/package/ws
const WebSocket = require('ws')
-
创建一个wss服务器,挂载到现有server服务器上,目的是和原有server使用同一端口,同源带来的好处很多,会为你减少很多麻烦
var server = http.createServer(app.callback());//koa脚手架创建的服务器 let wss = new WebSocket.Server({ server: server//挂载到原有服务器上,不然你得重新开一个端口,详见文档 })
-
写一个ws模块并引入
//外部 var server = http.createServer(app.callback()); let wss = new WebSocket.Server({ server: server }) WSOrder.createWss(wss)//使用createWss方法开始开启监听 //内部 module.exports = { createWss: function (wss) { wss.on('connection', function (ws) { this.numClients++ ws.isAlive = true; ws.on('pong', this.heartbeat);//测速激活连接 this.handle(wss)//连接第一次广播 ws.on('close', function () {//监听连接优雅断开 this.numClients-- }.bind(this)) }.bind(this)) this.wss = wss//讲第一次创建连接时导入的传入的wss保存起来,以便在其他路由中使用时可以直接传入,因为handle函数必须传入wss,而普通路由中没有这个wss,只又www.js文件中有 }, numClients: 0,//连接计数器 async handle (wss) { let data = {} //在此处可以写业务逻辑,如查询数据库设置返回内容 console.log('webSocket connectClients: ' + this.numClients) wss.clients.forEach(function each(client) { if (client.isAlive === false) {//如果非优雅断开 则强制停止 如网线被拔掉 this.numClients-- return client.terminate() } client.isAlive = false;//先设置成false client.ping(this.noop)//尝试ping如果响应则true client.send(JSON.stringify(data));//发送消息 }) }, //当然 你也可以写其他很多不同handle,方便在不同场景使用 heartbeat () { this.isAlive = true }, noop() { } }
-
在其他文件中使用时如在route页面中需要广播
//其他文件中 const WSOrder = require('../wss/order') //导入后便可使用handle方法进行广播 WSOrder.handle (WSOrder.wss) //需要传入一直之前保存的wss对象
-
如果你需要创建多个wss服务,如开启两个不相干的聊天室,可以是这样设置
const wss1 = new WebSocket.Server({ noServer: true }); const wss2 = new WebSocket.Server({ noServer: true }); WSOrder1.createWss(wss1) WSOrder2.createWss(wss2) //你可以创建两个WSOrder 当然 你可以共用一个WSOrder前提是在封装的时候 module.exports = function (){ return {} } WSOrder().createWss(wss1) WSOrder().createWss(wss2) //如果你共用一个WSOrder需要将wss对象保存起来,因为你在route内require也将会是一个新的对象 而不是原来的wss server.on('upgrade', function upgrade(request, socket, head) { const pathname = url.parse(request.url).pathname; 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(); } });
-
在客户端可以这样操作
window.ws = new WebSocket(`wss://***.***.***/order`) ws.onopen = () => { console.log('WebSocket onopen') } ws.onmessage = e => { //接收消息并处理 }
注意:wss服务是独立于koa执行的一个服务,虽然他们共用一个端口,但是并不会经过koa中间件,所以的koa中间件控制登陆状态将失效,也就是说客户端有可能绕过你的登陆检测就可连接wss,所以你有必要使用cookies判断当前用户(在ws.upgradeReq中找)
-
如果项目中使用了nginx的proxy反向代理,需要单独设置ws连接,因为这是一个101状态的http请求,主要在代理是设置header
location /order { proxy_pass http://localhost:3001; proxy_http_version 1.1; proxy_read_timeout 1800s; //设置连接有效事件为30min(自行设置)如不设置连接1分钟,如不发心跳包即断开,这里直接设置连接时间方便 proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
经过以上设置即可完成ws在koa2项目上的基本所有配置,再调用一些如.send onmessage等api即可完成在实际项目中的应用