> 文章列表 > koa2+sequelize中websocket的使用

koa2+sequelize中websocket的使用

koa2+sequelize中websocket的使用

后端:koa2+sequelize中使用websocket,可以使用的库有wskoa-websocket,此处使用ws

安装ws

npm i --save ws

在koa2中使用ws

const Koa = require('koa')
const app = new Koa()
const WebSocket = require('ws')const server = app.listen(8081)wss = new WebSocket.Server({ server }) // 这样可以实现websocket连接和http请求连接使用同一个端口wss.on('connection', (ws) => {ws.on('message', async message => {console.log(`服务端接收到客户端的消息:${message}`)ws.send(`服务端说:${message}`) // 发送的消息需要是一个字符串})ws.on('close', () => {console.log('客户端关闭连接')})
})

在Vue中使用WebSocket

const ws = new WebSocket('ws://localhost:8081')
ws.onopen = () => {// 客户端主动向服务端请求数据console.log('连接成功')ws.send('我是客户端发送的消息')
}
ws.onmessage = (event) => {// 这是从服务器接收到的消息console.log(event.data) // 这是一个字符串
}

如何在ws连接中获取信息,比如登录用户id

在客户端建立websocket连接时将查询参数包含用户id

const uid = 'ourworwo3292lmflkp'
const ws = new WebSocket(`ws://localhost:8081?uid=${uid}`)

在服务端的websocket连接时处理请求url,拿到uid

const WebSocket = require('ws') // 需要安装
const querystring = require('querystring') // 需要安装
const url = require('url') // 不需要安装wss.on('connection', (ws, req) => {const urlObj = url.parse(req.url)const query = querystring.parse(urlObj.query) // 解析URL参数const uid = query.uidws.on('message', async message => {// ...})ws.on('close', () => {// ...})
})

如何实现ws连接通道独立

方式1:比如可以使用uid为每个用户建立单独的连接通道

let cache = {}wss.on('connection', (ws, req) => {const urlObj = url.parse(req.url)const query = querystring.parse(urlObj.query)const uid = query.uidcache[uid] = wscache[uid].on('message', async message => {console.log(`服务端接收到客户端的消息:${message}`)cache[uid].send(`服务端说:${message}`)})cache[uid].on('close', () => {console.log('客户端关闭连接')})
})

客户端主动向服务端推送数据

const ws = new WebSocket(`ws://${base_url}`)
ws.onopen = () => {// 客户端主动向服务端请求数据const msg = {cmd: 'init',type: 'log'}ws.send(JSON.stringify(msg))
}

服务端接收客户端推送的数据

  wss.on('connection', async (ws, req) => {ws.on('message', async message => {// console.log(`接收消息:${message}`)try {const clientMsg = JSON.parse(message)if (clientMsg.type === 'log' && clientMsg.cmd === 'init') {const msg = {type: 'log',data: ''}// 服务端主动向客户端发送数据ws.send(JSON.stringify(msg))}} catch (error) {}})ws.on('close', () => {console.log('客户端关闭连接')})})

服务端的主动向客户端推送数据

wss.on('connection', async (ws, req) => {const msg = {type: 'log',data: ''}// 服务端主动向客户端发送数据ws.send(JSON.stringify(msg))ws.on('close', () => {console.log('客户端关闭连接')})})

示例:定义一个日志表,记录用户登录情况,使用ws连接后保证在日志表数据变化后服务端主动向客户端推送数据

sequelize中监听表的数据变化可以使用hook,比如afterCreateafterDestroyafterUpdatebeforeCreatebeforeDestroy
koa2+sequelize中websocket的使用
后端代码示例:

const Log = require('../models/Log')wss.on('connection', (ws) => {ws.on('message', async message => {try {const clientMsg = JSON.parse(message)if (clientMsg.type === 'log' && clientMsg.cmd === 'init') {const msg = {type: 'log', // 避免多个地方使用ws连接,定义一个类型作区别data: '我是服务端定义的数据' // 这个地方可以处理需要向客户端发送的ws数据}cache[uid].send(JSON.stringify(msg))}} catch (error) {}})// 监听日志表新增数据后的生命周期Log.addHook('afterCreate', async (log, options) => {const msg = {type: 'log',data: '我是服务端定义的数据'}ws.send(JSON.stringify(msg))})// 监听日志表删除数据后的生命周期Log.addHook('afterDestroy', async (log, options) => {const msg = {type: 'log',data: '我是服务端定义的数据'}ws.send(JSON.stringify(msg))})
})

前端代码示例:

const ws = new WebSocket('ws://localhost:8081')
ws.onopen = () => {const msg = {cmd: 'init',type: 'log'}// 客户端主动向服务端请求数据ws.send(JSON.stringify(msg))
}
ws.onmessage = (event) => {try {// 客户端接收服务端数据const { data } = JSON.parse(event.data)} catch (error) {}
}

注意:后端代码示例中的afterDestroy方法如果不能正确触发的原因,需要检查是否正确触发destroy方法(确保在数据库中正确查询到了要删除的数据,并且在查询记录的基础上调用了destroy方法)
示例:

const log= await Log.findByPk(logId)
if (log) {await log.destroy()
}

如果使用Log.destroy()不能正确触发afterDestroy方法

await Log.destroy({where: {id}
})

websocket连接失败和重连次数限制处理

连接次数是客户端请求服务端ws连接的次数,所以需要在客户端即前端处理

let MAX_CONNECTION = 5 // 最大重连次数
let CONNECTED = 0 // 连接次数let ws = null
getWebscoket() // 初始化websocket连接function getWebscoket () {ws = new WebSocket(`ws://${base_url}`) // 这里new之后指向了新的内存地址,所以后续的open等事件都会重新监听,连接失败后判断重连次数,需要保证ws的内存地址指向一致ws.onopen = () => {// 客户端主动向服务端请求数据const msg = {cmd: 'init',type: 'log'}ws.send(JSON.stringify(msg))}ws.onmessage = (event) => {try {const { data } = JSON.parse(event.data)weekData.value = data} catch (error) {ElMessage.error(error)}}ws.onerror = (error) => {// 连接错误操作}ws.onclose = () => {// 连接关闭操作reWsConnect()if (CONNECTED >= MAX_CONNECTION) {ElMessage.error('连接失败:可能后台未启动')}}
}
// ws连接失败后重新连接处理,连接次数限制处理
function reWsConnect () {setTimeout(() => {if (CONNECTED < MAX_CONNECTION) {getWebscoket()CONNECTED++}}, 2000)
}