后端:koa2+sequelize中使用websocket,可以使用的库有ws
和koa-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 })
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)
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 => {
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,比如afterCreate
、afterDestroy
、afterUpdate
、beforeCreate
、beforeDestroy
等

后端代码示例:
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',
data: '我是服务端定义的数据'
}
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()
function getWebscoket () {
ws = new WebSocket(`ws://${base_url}`)
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('连接失败:可能后台未启动')
}
}
}
function reWsConnect () {
setTimeout(() => {
if (CONNECTED < MAX_CONNECTION) {
getWebscoket()
CONNECTED++
}
}, 2000)
}