vue websocket 的使用(聊天)

因为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;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容