vue websocket 的使用(聊天)

2023-12-11  本文已影响0人  FM_0138

因为h5中提供了websocket模块, 所以我们可以直接使用, 不需要使用其他三方库, 为了方便管理websocket的各种状态, 及消息的发送接受, 我们可以单独创建一个js文件来管理.

vue前段代码

创建websocketManager.js文件

// 定义websocket 地址
let socketurl = "ws://192.168.0.252:8091";
// 重连锁, 防止过多重连
let reconnectLock = false;
// 定义一个消息发送中(包含发送失败的)的字典
window.messageSendingDic = {};
// 定义一个消息websocket连接状态的字段, 并且要绑定到widow上, 方便调用
// 0 未连接, 1 连接成功 2 连接中
window.webSocketState = 0;

// 定义连接服务器方法
function connectWebsocket(){
    //如果用户已登录, 进行连接websoket, 如果没有登陆, 登录后进行连接 用token判断
    // 创建一个websocket连接
    // let webSocket = new WebSocket(socketurl);
    
    // 如果想要传token, 因为ws不支持通过设置header, 所以直接在地址中加参数, 
    // 如ws://192.168.0.252:8091?name=lulu&token=123456
    let name = "lulu";
    let token = "1234"
    let webSocket = new WebSocket(socketurl+`?appname=${name}&token=${token}`);
    // 监听webSocket的各个状态
    // 连接成功
    webSocket.onopen = function() {
        console.log("websocket连接成功")
        // 连接成功后将连接状态改变
        window.webSocketState = 1;
        // 连接成功后, 要将消息队列里面的消息重新发送出去(底层重发, 和页面无关)
        for(let session in window.messageSendingDic){
            session.forEach(message => {
                // 重发消息
                reSendMessage(message)
            });
       }
    }
    // 连接出错
    webSocket.onerror = function(error){
        console.log("websocket连接出错", error);
        // 进行重连
        reconnectWebsocket();
    }
    // 连接关闭
    webSocket.onclose = function(result){
        console.log("websocket连接关闭", result);
        if(result == "退出登录"){
            return
        }
        // 进行重连
        reconnectWebsocket();
    }
    // 接受到消息
    webSocket.onmessage = function(message){
        console.log("websocket接受到消息", message);
        // 将受到的消息进行分类, 分发处理
        formatAcceptMessage(message)
    } 
    // 将webSocket绑定到window上面, 方便后续调用
    window.webSocket = webSocket;
}

// 定义重连方法 如果连接失败, 或者关闭, 
function reconnectWebsocket(){
    // 如果正在重连, 则返回
    if(reconnectLock){
        return;
    }
    // 进行加锁
    reconnectLock = true;
    // 重连时将连接状态改变
    window.webSocketState = 2;
    // 为了防止过多请求, 1s后进行重连
    setTimeout(function(){
        // 解锁
        reconnectLock = false;
        // 进行连接, 如果失败接着重连
        // connectWebsocket();
    }, 1000)

}
/**
 * 关闭websocket 退出时会用到
 *
 */
function closeWebsocket(){
    window.webSocket.onclose("退出登录")
}
// 定义发送消息的方法 message 格式为json
/**
 * 
 * @param {
 *      message: "内容",
 *      id: "xxxxxxx"
 * } message 消息内容
 * @param "1" messageType 消息类型
 * @param "QueryMsg" messageClass 附加字段吗消息类, 这里是以protobufjs的消息类为例
 */
function sendMessage(message, messageType, messageClass) {
    // 这里可以对message做一些格式化处理
    let formaterMessge = message;

    // 将消息添加到发送中的数组中进行记录
    // 先判断该回话有没有对应的数组, 如果没有就创建, 在添加, 如果有直接添加
    if(window.messageSendingDic[message.sessionId]) {
        window.messageSendingDic[message.sessionId].push(formaterMessge);
    } else {
        window.messageSendingDic[message.sessionId] = [];
        window.messageSendingDic[message.sessionId].push(formaterMessge);
    }
    // 如果websocket连接成功, 进行发送消息
    if(window.webSocketState == 1) {
        formaterMessge = JSON.stringify(formaterMessge)
        // 这里就可以直接用window调用了
        window.webSocket.send(formaterMessge);
    } else {
        // 如果websocket没有连接成功, 直接告诉消息发送页面消息发送失败, 模拟接受到消息, 发给对应页面
        let formaterMessge = {};
        // 将处理后的消息进行发送通知, 通知给需要的页面进行处理, 在需要的页面进行监听 
        // 注意: 使用页面添加window.addEventListener("acceptNewMessage", this.testAction)
        window.dispatchEvent(new CustomEvent("acceptNewMessage", formaterMessge));
    }
}

// 定义重发送消息的方法 message 格式为json
/**
 * 
 * @param {
*      message: "内容",
*      id: "xxxxxxx"
* } message 消息内容
* @param "1" messageType 消息类型
* @param "QueryMsg" messageClass 附加字段吗消息类, 这里是以protobufjs的消息类为例
*/
function reSendMessage(message) {
   // 如果websocket连接成功, 进行发送消息
   if(window.webSocketState == 1) {
       // 这里就可以直接用window调用了
       window.webSocket.send(message);
   } 
}

// 定义收到消息进行消息解析的方法
function formatAcceptMessage(message) {
    // 处理消息. 格式化
    let formaterMessge = message;
    // 将发送成功的消息从发送中移除
    if(window.messageSendingDic[message.sessionId]) {
        let sendingArray = window.messageSendingDic[message.sessionId];
        // 过滤发送成功的
        window.messageSendingDic[message.sessionId] = sendingArray.filter(msg => {
            return msg.id != message.id
        });
    } 
    // 将处理后的消息进行发送通知, 通知给需要的页面进行处理, 在需要的页面进行监听 
    // 注意: 使用页面添加window.addEventListener("acceptNewMessage", this.testAction)
    window.dispatchEvent(new CustomEvent("acceptNewMessage", formaterMessge));
}

// 如果服务器端有消息确认, 可以根据消息确认, 添加消息是否发送成功的状态, 
// 需要单独创建一个数组, 用来存放发送中的数据(包含发送失败的数据)


module.exports = {
    connectWebsocket,
    sendMessage
}

在main.js中引入

import {connectWebsocket} from "@/manager/webSocketManager";
//如果用户已登录, 进行连接websoket, 如果没有登陆, 登录后进行连接
if(token){
  connectWebsocket();
}

会话页面chat.vue

import {sendMessage} from "@/manager/webSocketManager"
clickSendButton(){
  console.log("点击了发送消息")
  sendMessage({
       sessionId: "1234567890",
       userId: this.name,
       messageId: "100000",
       message: {
             type: "text",
             content: this.message
       },
                
   })
},

注意: websocket默认如果长时间发消息, 会断开(服务器设置websocket永远不断开除外), 所以需要设置心跳包进行websocket保活.

退出账号是要关闭socket , 主动退出要处理重连问题

// 退出登录
  logout (ctx) {
   // 主动关闭连接
    websocket.onclose("退出登录")
    Message.success('退出成功,请重新登录')
  }

node 后台代码

node 不能直接使用websocket,需要安装支持websocket的包
这里使用的三方库nodejs-websocketws或者socket.io
安装

npm install nodejs-websocket
或
npm I ws
或者
npm install socket.io

在主页面加入以下代码

const ws = require("nodejs-websocket");
//定义一个对象,用于存放正在连接中的socket, 字段名是以token命名
const conningObjet = {};

let webServe = ws.createServer(function (connection) { 
  // console.log('创建成功', connection)
  //连接成功的回调
  
  // 获取连接的token
  let path = connection.path;
  let pathParams = getParamsFromURL(path);
  console.log(pathParams);
  // 不满足服务器条件时服务器主动断开连接
  if(pathParams.token) {
    // 如果token正确进行继续操作
    //如果是第一次连接, 添加到对应的数组, 如果不是, 不用继续添加
    if (!conningObjet[pathParams.token]) { 
      //将用户发来的信息对所有用户发一遍
      conningObjet[pathParams.token] = connection;
    }
    //监听数据,当客户端传来数据时的操作
    connection.on("text", function (result) {
      console.log('接受到消息', result)
      // console.log(result)
      console.log(conningObjet.keys)
      //数据只能是以字符串或buffer形式传递,所以这里要转换成JSON;
      var data = JSON.parse(result); 
      // 收到消息后立即向服务器端发送确认消息
      // 确认消息添加确认消息类型, 1 表示确认消息
      data.type = "1";
      connection.sendText(JSON.stringify(data))
    })
    connection.on('connect', function(code) {
      console.log('开启连接', code)

    })
    connection.on('close', function(code, reason) {
      console.log('关闭连接', code)
      console.log('关闭原因:', reason)
      console.log(conningObjet);
      // 获取连接的token
      let path = connection.path;
      let pathParams = getParamsFromURL(path);
      console.log(pathParams.token);
      // 连接关闭时要将这个连接从连接对象中移除
      delete conningObjet[pathParams.token]
      console.log(conningObjet);
    })
    connection.on('error', function(code) {
      console.log('异常关闭', code)
    })
  } else {
    // 如果token 不合理就进行断开
    connection.close(1, "token不正确");
  }
  
});
webServe.listen(8091);
webServe.on('connection', connection => {
  console.log("游客户端进行连接");
  
})

// 获取url上的参数, 使用的是正则表达式
function getParamsFromURL(url) {
  const regex = /[?&]([^=#]+)=([^&#]*)/g;
  const params = {};
  let match;
  while (match = regex.exec(url)) {
    params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
  }
  return params;
}
上一篇下一篇

猜你喜欢

热点阅读