websocket使用

185 阅读2分钟

JavaScript部分

<script>
export default {
    data() {
        return {
            Ws: null, //用于接收websocket实例
            connctState: false, //连接状态
            pingpong: null, //心跳检测定时器变量
            pongCount: 0,  //超时次数
            maxPongCount: 5, //最大超时次数,超出后执行重新连接
        }
    },
    mounted() {
        //初始化websocket
        this.init()
    },
    method: {
        init() {
            if(this.Ws) {
                //已存在连接
                return
            }
            //创建websocket实例
            this.Ws = new WebSocket(`ws://locolhost:8000?userId=${userId}`)
            //将四个钩子函数open(连接)/close(断开)/message(消息接收)/error(错误),添加到实例Ws上
            this.Ws.addEventListener('open', this.handleOpen.bind(this), false)
            this.Ws.addEventListener('close', this.handleClose.bind(this), false)
            this.Ws.addEventListener('message', this.handleMessage.bind(this), false)
            this.Ws.addEventListener('error', this.handleError.bind(this), false)
        },
        handleOpen() {
            console.log('ws open')
            const self = this
            self.connctState = true
            //创建心跳机制检测
            self.pingpong = setInterval(() => {
                this.Ws.send(JSON.stringify({
                    userId,
                    action: 'ping'  //做为心跳检测辨识
                }))
                
                self.pongCount++ //超时次数+1
                if(self.pongCount > self.maxPongCount) {
                    self.stopPong()
                    self.reconnect()
                }
            }, 30000)
        },
        handleClose() {
            //连接状态修改为false
            this.connctState = false
            //停止心跳检测
            this.stopConnect()
            //尝试重连
            this.reconnect()
        },
        handleMessage(e) {
            //客户端接收到消息,触发message钩子函数
            console.log(e)
            const data = JSON.parse(e.data)
            if(data.action === 'pong') {
                //心跳机制检测标识,正常返回,超时次数重置
                this.pongCount = 0
            } else {
                //否则处理消息
            }
        },
        handleError() {
            console.log('ws error')
        },
        sendMessage() {
            //通过send方法将需要发送的消息传给后端,由后端发送给接收者
            this.Ws.send(
                JSON.stringify({
                    userId, //发送者ID
                    targetUserId,  //接收者ID
                    message  //需要发送的消息
                })
            )
        },
        onClickClose() {
            //正常关闭,将实例赋值为null,并执行close钩子函数
            this.Ws = null
            this.handleClose()
        }
        stopConnect() {
            //停止心跳检测
            this.pongCount = 0
            clearInterval(this.pingpong)
        },
        reconnect() {
            if(this.connctState) { //连接状态为true时正常,直接返回
                return
            } else if(this.Ws) { //ws实例有值,重连
                this.handleOpen()
            }
        }
    }
}
</script>

node部分

//引入ws模块
const Ws = require('ws')

//创建一个立即执行函数,并将Ws传入
((Ws) => {
    //创建ws服务实例,并设置端口号
    const Server = new Ws.Server({ port: 8000 })
    
    //初始化ws服务
    const init = () => {
        Server.on('open', handleOpen)
        Server.on('close', handleClose)
        Server.on('error', handleError)
        Server.on('connection', handleConnection)
    }
    
    function handleOpen() {
        console.log('ws server open')
    }
    function handleClose() {
        console.log('ws server close')
    }
    function handleError() {
        console.log('ws server error')
    }
    function handleConnection(data, req) {
        //获取用户ID参数
        console.log(req.url)
        //连接成功时,会生成一个唯一的标识ws._socket.remotePort
        //将用户ID和连接标识存储
        console.log('唯一标识:'+ ws._socket.remotePort)
        //绑定message事件
        ws.on('message', handleMessage)
    }
    fuction handleMessage(data) {
        data = JSON.parse(data)
        if(data.action === 'ping') {  //心跳检测机制标识
            //遍历所有连接,定向发送消息
            Server.clients.forEach(c => {
                // c._socket._peername.port 为用户连接唯一标识,用于判断是否向该用户发送消息
                // 使用c.send() 方法向用户发送消息
                if(c._socket._peername.port === userPort) {
                    c.send(JSON.stringify({
                        action: 'pong'
                    }))
                }
            })
        } else {
            //否则获取目标用户,向目标用户发送消息
            //查询targetUserId对应的连接标识,遍历判断是否发送
            Server.clients.forEach(c => {
                if(c._socket._peername.port === targetUserPort) {
                    c.send(JSON.stringify({
                        msg: data.msg
                    }))
                }
            })
        }
    }
    
    init()
})(Ws)