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.uid
   
   ws.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.uid
   
   cache[uid] = ws
   
   cache[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
在这里插入图片描述
后端代码示例:

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)
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值