美文网首页
2020-10-30 web前端im开发websocket编码加

2020-10-30 web前端im开发websocket编码加

作者: gdlooker | 来源:发表于2020-10-30 20:58 被阅读0次

    近期开始在公司做web前端音视频开发,流程分析:
    第一步音视频信令登录:

       //音视频信令登录
        videoMuteLogin() {
          // 175tryg0ghd5k5bagmsyq  //175tmh70myccl6arpx480
          // let psd = aes.Encrypt(this.ruleForm.password); // 加密
          let params = {
            biz_uid: this.biz_uid, //当前账号biz_uid1即账号1 biz_uid2即为账号2
            device_id: this.keystrDeviceId, //设备id
            manufacturer: "xiaomi",
            options: "",
            platform: "web",
            push_token: this.push_token,
            version: "1.0.0",
          };
          let app_id = encodeURIComponent(
            "1299a97acab59b7a28df696a58200b72#1596507757722"
          );
          console.log("信令登录的参数", JSON.stringify(params));
          this.axios({
            url: `/mgacct/v1/mgacct/v1/${app_id}/sdkacct/livslogin`,
            method: "POST",
            data: params,
            headers: {
              "Content-Type": "application/x-www-form-urlencoded",
            },
          }).then((res) => {
            console.log(
              "===================信令登录返回的用户信息===================="
            );
            console.log("lvesLogin", JSON.stringify(res));
            console.log(
              "===================信令登录返回的用户信息===================="
            );
            if (!!res && res.code === "0") {
              const { data } = res;
              let xinlingData = {};
              this.userId = data.user_id;
              this.access_token = data.access_token; //
              xinlingData.user_id = data.user_id;
              xinlingData.expire_in = data.expire_in;
              xinlingData.access_token = data.access_token;
              this.$store.state.xinlingData = xinlingData;
              //console.log("信令登录的token", data.access_token);
              console.log("=========开始获取connectInfo========");
              this.getConnectInfo(data.access_token);
            } else {
              console.log("音视频信令登录失败");
            }
          });
        }
    

    例如获取到的json数据格式如下:

     {"biz_uid":"user1",
    "device_id":"device1",
    "manufacturer":"xiaomi",
    "options":"",
    "platform":"web",
    "push_token":"token1",
    "version":"1.0.0"}
    

    上一步骤的目的是为了获取token,接下来通过token放在头部来获取
    websocket的服务器端的地址,示例代码如下:

      //获取连接info
        getConnectInfo(token) {
          console.log("token", token);
          let app_id = encodeURIComponent(
            "1299a97acab59b7a28df696a58200b72#1596507757722"
          );
          //let app_id="1299a97acab59b7a28df696a58200b72#1596507757722"
          //let access_token=encodeURIComponent(token)
          let access_token = token;
          this.$http({
            url: `/${app_id}/sdklivs/${app_id}/sdklivs/connect/info?app_id=${app_id}`,
            method: "GET",
            headers: {
              "Content-Type": "application/x-www-form-urlencoded",
              access_token: access_token,
            },
          })
            .then((res) => {
              console.log("信令登录获取connectInfo", res);
              if (!!res && res.code === "0") {
                const { data } = res;
                console.log("im_data", data);
                const { server_addr, socket_port, wssocket_port } = data; //获取服务器地址
                console.log("server_addr", server_addr);
                console.log("socket_port", socket_port);
                console.log("wssocket_port", wssocket_port);
                //放到当前vue的im_module里面
                this.im_module.server_addr = server_addr;
                this.im_module.socket_port = socket_port;
                this.im_module.wssocket_port = wssocket_port;
                //放置到当前的store的state里面
                this.$store.state.server_addr = server_addr;
                this.$store.state.socket_port = socket_port;
                this.$store.state.wssocket_port = wssocket_port;
                this.command = COMMAND_LIVS_BIND_USER_REQ; //信令登录 13
                this.appId = "1299a97acab59b7a28df696a58200b72#1596507757722";
                this.reqBindUserChannel();
              } else {
                console.log("请求connectInfo失败");
              }
            })
            .then((error) => {
              console.log("error", error);
            });
        }
    

    例如返回的json数据格式如下:

    {
        "code":"0",
        "msg":"操作成功",
        "hasError":false,
        "data":{
            "socket_port":12101,
            "wssocket_port":12201,
            "server_addr":"192.168.1.105"
        },
        "serverTimestamp":1604063133606
    }
    

    接下来开始绑定用户,为啥需要绑定呢,因为我们在用websocket跟用户通讯的时候,后台不知道是谁在跟我握手,所以我们需要先通过登录传入当前的userId根据发送的数据格式来让后台知道是谁跟来登录握手了,示例代码如下:

      reqBindUserChannel() {
          // 请求绑定用户
          let protoName = "ReqBindUserChannel";
          // let sdkInfo=this.$store.state.sdkData
          // let appId = encodeURIComponent(
          //   "9eb745a14c44b7f5badbbb92092ee8e9#Jiao-IM-Demo"
          // );
          let params = {
            appId: this.appId,
            userId: this.userId, //拿到userid
            token: this.access_token, //以及access_token
            manufacturer: "web",
            deviceId: this.keystrDeviceId, //绑定user的时候的deviceId 等下发送消息的时候要用的
            osVersion: "1.0.0",
          };
          const { proto } = proToObject;
          //console.log("绑定user的时候proto对象", proto);
          console.log("reqBindUserChannel_params", JSON.stringify(params));
          this.templateFun(params, proto.ReqBindUserChannel, protoName);
        }
    

    模板函数这里要用到protobuffer(不细说了),示例代码如下:

     //proto模板函数
        templateFun(param, methodName, protoName) {
          //console.log("methodName", methodName, protoName);
          // 函数模板
          let createProto = methodName.create(param); //  开始创建
          console.log("编码前createProto", createProto);
          let encodeProto = methodName.encode(createProto).finish(); // 对proto对象进行编码
          console.log("编码后的东西", encodeProto);
          const { proto } = proToObject;
          let protoData = proto[protoName].decode(encodeProto);
          console.log("解码protoData", protoData);
          let encryptionResult = aes.socketEncrypt(encodeProto); // 编码后的结果拿来加密
          console.log("aes解密", aes.socketDecrypt(encryptionResult));
          //console.log("encryptionResult解码前的长度", encryptionResult.length);
          console.log("encryptionResult加密后", encryptionResult);
          //let Base64 = require("js-base64").Base64;
          //encryptionResult = Base64.decode(encryptionResult); //Base64解密
          //console.log("base64解码后", encryptionResult);
          //console.log("encryptionResult解码后的长度",encryptionResult.length);
          //this.decodeMessage(encryptionResult); //解密消息
          const ret = ArrayBufferUtils.packTotalLength(
            param,
            protoName,
            encryptionResult,
            this.userId,
            this.keystrDeviceId
          );
         
          const ret2 = ArrayBufferUtils.generateDataPacket(
            param,
            protoName,
            encryptionResult,
            this.userId,
            this.keystrDeviceId,
            this.command
          );
          // const blob = new Blob([ret]);
          // console.log('是不是转成功了',typeof blob)
          // console.log(blob)
          // this.parseMsg(blob)
          // return ;
          console.log("最后要发射的数据", ret);
          console.log("最后要发射的数据", ret2);
          if (!!this.im_module) {
            if (!this.im_module.server_addr || !this.im_module.wssocket_port) {
              console.log("websocket的服务器地址或者端口号不存在");
              return;
            }
            //编解码 获取到websocket的长度
            this.createWebSocket(
              this.im_module.server_addr,
              this.im_module.wssocket_port,
              ret
            );
          }
        }
    

    上面有用到websocket的示例代码如下:

     //创建websocket
        createWebSocket(longLink, port, inputMsg) {
          // 初始化一个 WebSocket 对象
          console.log("websocket对象", longLink);
          console.log("port", port);
          console.log("inputMsg", inputMsg);
          try {
            if ("WebSocket" in window) {
              if (!ws) {
                console.log("ws对象为空 创建w视频ebsocket对象");
                ws = new WebSocket(`ws://${longLink}:${port}`);
                this.initWebsocket(longLink, port, inputMsg);
              } else {
                console.log("websocket实例存在", ws.readyState);
                switch (ws.readyState) {
                  case WebSocket.CONNECTING:
                    // do something
                    break;
                  case WebSocket.OPEN:
                    // do something
                    //发送消息
                    console.log("websocket的readyState状态是否为1:", ws.readyState);
                    console.log("发送的消息", inputMsg);
                    ws.send(inputMsg);
                    break;
                  case WebSocket.CLOSING:
                    // do something
                    console.log("websocket正在关闭:", ws.readyState);
                    break;
                  case WebSocket.CLOSED:
                    // do something
                    console.log("websocket已经关闭:", ws.readyState);
                    break;
                  default:
                    // this never happens
                    break;
                }
              }
            } else {
              alert("当前浏览器不支持WebSocket!");
            }
          } catch (e) {
            console.log("异常catch", e);
            this.reqBindUserChannel(); //重新绑定用户
          }
        }
       //初始化websocket
        initWebsocket(longLink, port, inputMsg) {
          // 建立 web socket 连接成功触发事件
          ws.onopen = () => {
            console.log("打开websocket发送消息", inputMsg);
            ws.send(inputMsg);
          };
          let that = this; //主要是回调函数 无法拿到this
          // 接收服务端数据时触发事件
          ws.onmessage = (evt) => {
            console.log("接收服务器端信息", evt);
            //that.onReceiverMessage(evt);
            ParseMsgUtil.parseMsg(evt.data, function (protoData, command) {
              console.log("回调收到消息", protoData, command);
              //解析服务器消息
              if (command == COMMAND_FORCE_LOGOUT && !!protoData) {
                //被踢了
                let key = that.userId + command;
                localStorage.setItem(key, JSON.stringify(protoData));
                alert(JSON.stringify(protoData));
              }
              if (command == COMMAND_LIVS_BIND_USER_RESP && !!protoData) {
                //COMMAND_LIVS_BIND_USER_RESP=14 绑定用户
                //{"resp":{"errorCode":"success"}}
                const { resp } = protoData;
                const { errorCode } = resp;
                if (!!errorCode && errorCode === "success") {
                  //表示绑定成功 存储到store
                  let key = that.userId + command;
                  localStorage.setItem(key, COMMAND_LIVS_BIND_USER_RESP);
                  alert(errorCode);
                } else {
                  alert(errorCode);
                }
              }
              if (command == COMMAND_LIVS_SERVER_INFO_RSP && !!protoData) {
                let serverData = JSON.stringify(protoData);
                //获取分配音视频服务的IP+端口
                console.log("获取到janus服务器端的地址跟端口号", serverData);
                alert(serverData);
                let key = that.userId + command;
                localStorage.setItem(key, JSON.stringify(protoData));
              }
              if (command == COMMAND_LIVS_CALL_ROOM_INVITE_REQ && !!protoData) {
                //接到19
                //如果接收到后台是 19的情况 表示对方邀请我加入会议
                let key = that.userId + command;
                //存储数据
                localStorage.setItem(key, JSON.stringify(protoData));
                let string = localStorage.getItem(key); //根据当前音视频用户id缓存数据
                //console.log('接收到对方的请求缓存成功',string)
                alert(string);
              }
              if (command == COMMAND_LIVS_CALL_ROOM_INVITE_RSP && !!protoData) {
                //如果这里返回20表示我刚刚发送给后台服务器邀请是成功的
                console.log("房间邀请成功", protoData);
                alert("房间邀请成功", JSON.stringify(protoData));
                //加入房间
                //that.$store.state.call_room = COMMAND_LIVS_BIND_USER_RESP;
              }
              if (command == COMMAND_LIVS_CALL_ROOM_ACCEPT_REQ && !!protoData) {
                //接收到23
                let key = that.userId + command;
                console.log("我收到了对方要我加入房间的邀请", command);
                //我知道了 对方接收了邀请
                localStorage.setItem(key, JSON.stringify(protoData));
                //
              }
              if (command == COMMAND_LIVS_CALL_ROOM_ACCEPT_RSP && !!protoData) {
                //接收到24
                //我接收到了 服务器给我的响应
                console.log(
                  "服务器帮我成功转发了我接收了对方的好友邀请24",
                  protoData
                );
                alert(
                  "你已经成功收到了服务器响应的command",
                  JSON.stringify(protoData)
                );
              }
              if (command == COMMAND_LIVS_CALL_ROOM_DESTROY_REQ && !!protoData) {
                console.log("我收到了服务器端的接收当前解散通话", protoData);
                //接收到27
                let key = that.userId + command;
                //我知道了 对方接收了邀请
                localStorage.setItem(key, JSON.stringify(protoData));
              }
              if (command == COMMAND_LIVS_CALL_ROOM_DESTROY_REQ && !!protoData) {
                //服务器响应到了28
                console.log("服务器帮我转发成功", protoData);
              }
            });
          };
          ws.onerror = () => {
            console.log("连接发生错误了,要心跳重连");
            //heartCheck.reset().start(); //心跳检测重置
            //this.reconnect(longLink, port, inputMsg);
            console.log("llws连接成功!" + new Date().toUTCString());
          };
          // 断开 web socket 连接成功触发事件
          ws.onclose = () => {
            console.log("连接已关闭...");
          };
        }
    

    接下来再贴出以上用到的一些工具类ArrayBufferUtils的代码如下:

    
    //ArrayBuffer转字符串
    function ab2str(u, f) {
        const b = new Blob([u]);
        const reader = new FileReader();
        reader.readAsText(b, "utf-8");
        reader.onload = function () {
            if (f) {
                f.call(null, reader.result);
            }
        };
    }
    //字符串转字符串ArrayBuffer
    function str2ab(s, f) {
        var b = new Blob([s], { type: "text/plain" });
        var r = new FileReader();
        r.readAsArrayBuffer(b);
        r.onload = function () {
            if (f) f.call(null, r.result);
        };
    }
    
    //进行编码移位 ab表示容器字节数组 value表示字节数组总长度
    //offset表示偏移量
    function int2ab(ab, value, offset, littleEndian) {
        const view = new Int8Array(ab, offset || 0, 4);
        if (littleEndian) {
            view[3] = (value >>> 24) & 0xff;
            view[2] = (value >>> 16) & 0xff;
            view[1] = (value >>> 8) & 0xff;
            view[0] = value & 0xff;
        } else {
            view[0] = (value >>> 24) & 0xff;
            view[1] = (value >>> 16) & 0xff;
            view[2] = (value >>> 8) & 0xff;
            view[3] = value & 0xff;
        }
    }
    function str2abUint8(ab, str, offset) {
        const bufView = new Uint8Array(ab, offset || 0, str.length);
        for (let i = 0; i < str.length; i++) {
            bufView[i] = str.charCodeAt(i);
        }
    }
    //组装包 获取包的长度
    function packTotalLength(
        param,  //参数json
        protoName, //protoName
        encryptionResult, //解码解密后的ret字节数组
        userId, //用户的userId用来拼接成keyStr
        deviceId //deviceId
    ) {
        let dataBuffer = encryptionResult; //之前的组装包 这个可能是字符串也可能是对象
        let dataLength = encryptionResult.length;
        //遍历object对象
        let keys = Object.keys(param);
        console.log("keys", keys);
        //console.log('userInfo',this.userInfo)
        //let keystr = param.appId + ";" + param.userId + ";" + param.deviceId;
        let keystr = param.appId + ";" + userId + ";" + deviceId;
        console.log("=============keyStr=====================");
        console.log("keyStr", keystr);
        console.log("=============keyStr=====================");
        //console.log(keystr2);
        console.log(keystr.length);
        //编码
        //keystr = encodeURIComponent(keystr);
       //protoName = encodeURIComponent(protoName);
        //计算出包的长度
        let totalLength =
            4 + //包体长度 4个字节
            1 + //命令码一个字节
            1 + //keystr的长度一个字节
            keystr.length + //keystr的长度
            1 + //protoName的长度一个字节
            protoName.length + //protoName对应的类名的长度
            encryptionResult.length; // 消息体body的长度
        console.log("计算出来的包的长度:" + totalLength); //整个消息体的长度
    
        const ret = new ArrayBuffer(totalLength); //根据总长度输出一个字节数组
        console.log("编码总长度字节数组", ret);
        let offset = 0; //初始化默认的一个偏移量
        //这个编码总长度
        int2ab(
            ret, //这个表示字节数组 用来装整个消息的所有内容
            totalLength, //这个是整个消息容器的总长度
            offset
        ); //这个是偏移量
        offset += 4;
        //这个是编码指令码
        const noPointer2 = new Int8Array(ret, offset, 1);
        noPointer2[0] = this.command; // 指令
        offset += 1;
        const noPointer3 = new Int8Array(ret, offset, 1);
        noPointer3[0] = keystr.length;
        offset += 1;
        //这个是编码keyStr
        str2abUint8(ret, keystr, offset);
        offset += keystr.length;
        //创建一个8个字节的有符号的整型数组
        const protoNamePointer = new Int8Array(ret, offset, 1);
        protoNamePointer[0] = protoName.length;
        offset += 1;
        //这个地方是编码protoName
        str2abUint8(ret, protoName, offset);
        offset += protoName.length;
        if (dataLength > 0) {
            //console.log(dataLength);
            //console.log(ret);
            //console.log(offset);
            //二进制数组
            const dataPointer = new Int8Array(ret, offset);
            //console.log(dataPointer);
            //console.log(dataBuffer);
            for (let i = 0; i < dataBuffer.length; i++) {
                if (typeof dataBuffer == "object") {
                    dataPointer[i] = dataBuffer[i]; //字符串转二进制
                } else {
                    dataPointer[i] = dataBuffer.charCodeAt(i); //字符串转二进制
                }
            }
            //console.log(dataPointer);
        } else {
            //todo:command is empty
        }
        return ret;
    }
    
    //根据截取数组的那一段 来获取对应的
    function getByteString(tmpUint8Arrayss) {
        let dataString = "";
        for (let i = 0; i < tmpUint8Arrayss.length; i++) {
            dataString += String.fromCharCode(tmpUint8Arrayss[i]);
        }
        console.log("获取到截取的字符串", dataString);
        return dataString;
    }
    //获取消息体
    function getMessageBody(resultString) {
        const arr = []; // 将解密后的字符串结果转换为数组
        for (let i = 0, j = resultString.length; i < j; ++i) {
            arr.push(resultString.charCodeAt(i));
        }
        console.log("消息包的数据", arr);
    
        const tmpUint8Arrayss = new Uint8Array(arr);
        console.log("tmpUint8Arrayss", tmpUint8Arrayss); //数组长度
        // const arrayBuffer=tmpUint8Arrayss.buffer
        // console.log('arrayBuffer',arrayBuffer)
        // const uint8Array=Array.from(tmpUint8Arrayss);
        // console.log('uint8Array',uint8Array)
        //获取总长度
        //let totalLen=tmpUint8Arrayss.length; //获取长度
        //console.log('获取总长度',totalLen)
        // let testArray = ["1", "2", "3", "4", "5"];
        // console.log(testArray.slice(2, 4));
        // console.log(testArray);
        //取出总长度
        let lastStartIndex = 0; //最后的index
        let totalByteCount = 4; //占字节数
        let offset = 0;
        //截取从0到4
        const packageTotalArray = tmpUint8Arrayss.slice(
            lastStartIndex,
            totalByteCount
        );
        let totalLength =
            packageTotalArray[3] +
            (packageTotalArray[2] << 8) +
            (packageTotalArray[1] << 16) +
            (packageTotalArray[0] << 24);
        console.log("获取到总长度", totalLength);
        lastStartIndex += totalByteCount; // +4
        let comandByteCount = 1;
        console.log("command命令码角标", lastStartIndex);
        let cmd = tmpUint8Arrayss[lastStartIndex];
        console.log("cmd", cmd);
        console.log("cmd命令码", cmd);
        //cmd
        lastStartIndex += comandByteCount;
        let keyStrLenByteCount = 1;
        console.log("keyStr长度的角标", lastStartIndex);
        const keyStrLen = tmpUint8Arrayss[lastStartIndex]; //因为是一个字节
        console.log("获取到keyStr的长度", keyStrLen); //eg:78
    
        lastStartIndex += keyStrLenByteCount;
    
        let keyStrContentEndIndex = lastStartIndex + keyStrLen;
        //5,5+78
        let keyStrArray = tmpUint8Arrayss.slice(
            lastStartIndex,
            keyStrContentEndIndex
        ); //截取78个
    
        console.log("keyStr", keyStrArray);
        let keyString = getByteString(keyStrArray);
        console.log("keyString", keyString);
    
        // 83开始 取keyStr的len的长度
        console.log(
            "=============================开始取出keyStr的len的长度======================"
        );
        lastStartIndex += keyStrLen;
        let protoNameByteCount = 1;
        let protoNameLen = tmpUint8Arrayss[lastStartIndex];
        console.log("protoName的长度:", protoNameLen);
        console.log(
            "=============================开始取出keyStr的len的长度======================"
        );
        lastStartIndex += protoNameByteCount; //从84开始 截取 protoNameLen
        let protoNameEndIndex = lastStartIndex + protoNameLen;
        const protoNameArray = tmpUint8Arrayss.slice(
            lastStartIndex,
            protoNameEndIndex
        );
        console.log("protoNameArray", protoNameArray);
        let protoName = getByteString(protoNameArray);
        console.log("获取到的protoName", protoName);
    
        console.log(
            "================================开始取出消息体的内容==================================="
        );
        lastStartIndex += protoNameLen;
        console.log("消息体的起始值", lastStartIndex);
        console.log("uint8ArrayLength:", tmpUint8Arrayss.length);
        const msgBodyArray = tmpUint8Arrayss.slice(
            lastStartIndex,
            tmpUint8Arrayss.length
        );
        let msgBodyString = getByteString(msgBodyArray);
        console.log("消息体", typeof msgBodyString);
        console.log("消息体长度", msgBodyString.length);
        return msgBodyArray;
    }
    
    // 组装数据包
    function generateDataPacket(params, protoName, encryPorotData, userId, deviceId,command) {
        // 加密后的proto消息体数据
        let dataBuffer = encryPorotData;
    
        // proto消息体长度
        let dataLength = encryPorotData.length;
    
        // keySession内容
        let keystr = params.appId + ";" + userId + ";" + deviceId;
        //keystr = encodeURIComponent(keystr)
        console.log("keyStr", keystr);
        // 数据包的总长度
        let totalLength =
            4 +
            1 +
            1 +
            keystr.length +
            1 +
            protoName.length +
            encryPorotData.length; // 计算总长度
    
        // 初始化缓存
        const ret = new ArrayBuffer(totalLength);
        let offset = 0;
    
        // 将总长度写入到前四个字节中
        int2ab(ret, totalLength, offset);
        offset += 4;
    
        // 写入一个字节的命令
        const noPointer2 = new Int8Array(ret, offset, 1);
        noPointer2[0] = command;
        offset += 1;
    
        // 写入keysession的长度值,1个字节
        const noPointer3 = new Int8Array(ret, offset, 1);
        noPointer3[0] = keystr.length;
        offset += 1;
    
        // 写入keySession的内容
        str2abUint8(ret, keystr, offset);
        offset += keystr.length;
    
        // 写入proto简称的长度值,1个字节,例如BindChannelReqProto,长度为19,就把19转换成8位的字节,写入到缓存中
        const protoNamePointer = new Int8Array(ret, offset, 1);
        protoNamePointer[0] = protoName.length;
        offset += 1;
    
        // 写入protoName的值,即把BindChannelReqProto字符串写入到缓存中
        str2abUint8(ret, protoName, offset);
        offset += protoName.length;
    
        // 写入proto消息体
        if (dataLength > 0) {
            const dataPointer = new Int8Array(ret, offset);
            for (let i = 0; i < dataBuffer.length; i++) {
                dataPointer[i] = dataBuffer[i];
            }
        }
        return ret;
    }
    export default {
        ab2str, //字节数组转字符串
        str2ab, //字符串转字符串ArrayBuffer
        packTotalLength, //组装发送消息长度
        generateDataPacket,//同上 也是组装消息数据
        getByteString, //字节数组转字符串
        getMessageBody,//获取消息体
        str2abUint8,// 
    
    }
    

    下面是收到websocket进行解析消息 因为啥先编码再加密,
    所以我们收到数据的时候要进行先解密再解码,ParsetMsgUtil.js文件示例代码如下:

    import aes from "./aes.js";
    import proToObject from "../proto/proto.js";
    //解析消息
    function parseMsg(blobMsg, callFunction) {
         let reader = new FileReader();
         reader.readAsArrayBuffer(blobMsg);//这个blobMsg是Blob类型
         reader.onload = function (e) {
              // 消息的结构和发送消息是一样的
              let dataBuffer = reader.result;
              const data = new DataView(dataBuffer);
              var pos = 0;
              // 读取总长度
              var totalLength = data.getInt32(pos);
              pos += 4;
              // 读取命令
              let command = data.getInt8(pos);
              pos += 1;
              // 读取keySession长度值
              var keyStrLen = data.getInt8(pos);
              pos += 1;
              // 读取keySession内容邀请大佬,加入房间一下
              var keyStrArray = new Uint8Array(dataBuffer, pos, keyStrLen);
              var keyStr = String.fromCharCode.apply(null, keyStrArray);
              pos += keyStrLen;
    
              // 读取protoName长度值
              var protoNameLen = data.getInt8(pos);
              pos += 1;
    
              // 读取protoName值
              var protoNameArray = new Uint8Array(dataBuffer, pos, protoNameLen);
              var protoName = String.fromCharCode.apply(null, protoNameArray);
              pos += protoNameLen;
    
              // 计算proto消息体长度
              var protoDataLen = totalLength - (1 + 1 + keyStrLen + 1 + protoNameLen);
              // 读取proto消息体数据
              var protoDataArray = new Uint8Array(dataBuffer, pos, protoDataLen);
              console.log(protoDataArray);
              var protoDataOrig = String.fromCharCode.apply(null, protoDataArray);
    
              // 对proto进行AES解码
              var decData = aes.socketDecrypt(protoDataOrig);
              const { proto } = proToObject;
              // 对proto解码
              var protoData = proto[protoName].decode(decData);
              if (!!callFunction && typeof callFunction === 'function') {
                   callFunction(protoData, command)
              } else {
                   console.log('Ni 传递的不是回调函数对象')
              }
         };
    }
    export default {
         parseMsg, //解析消息
    }
    

    aes的加解密示例代码如下:

    /* eslint-disable */
    const CryptoJS = require('crypto-js') // 引用AES源码js
    
    let array = [2, 5, 2, 1, 8, 3, 0, 8, 9, 8, 3, 6, 2, 3, 2, 5]
    let result = String.fromCharCode.apply(String, array);
    
    const key = CryptoJS.enc.Utf8.parse('kdodl224434k3k22') // 十六位十六进制数作为秘钥
    const iv = CryptoJS.enc.Utf8.parse(result) // 十六位十六进制数作为秘钥偏移量
    
    // 解密方法
    function Decrypt(word) {
      // let encryptedHexStr = CryptoJS.enc.Hex.parse(word)
      // let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr)
      let srcs = word.toString()
      let decrypt = CryptoJS.AES.decrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 })
      return decrypt.toString(CryptoJS.enc.Utf8)
      // return decryptedStr
    }
    // 加密方法
    function Encrypt(word) {
      let srcs = CryptoJS.enc.Utf8.parse(word)
      let encrypted = CryptoJS.AES.encrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 })
      return encrypted.toString()
    }
    
    // 加密
    function socketEncrypt(word) {
      let word2 = String.fromCharCode.apply(String, word);
      let srcs = CryptoJS.enc.Utf8.parse(word2)
      let encrypted = CryptoJS.AES.encrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 })
      var aesData = encrypted.toString();
      // Array to base64
      var raw = atob(aesData);
      var rawLength = raw.length;
      var array = new Int8Array(new ArrayBuffer(rawLength));
    
      for (var i = 0; i < rawLength; i++) {
        var chari = raw.charCodeAt(i);
        array[i] = chari;
      }
      return array;
    }
    
    // 解密
    function socketDecrypt(word) {
      let srcs = btoa(word);
      let decrypt = CryptoJS.AES.decrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 })
      console.log("decrypt result: ");
      var result = decrypt.toString(CryptoJS.enc.Base64);
      var raw = atob(result);
      var rawLength = raw.length;
      var array = new Uint8Array(new ArrayBuffer(rawLength));
    
      for (var i = 0; i < rawLength; i++) {
        array[i] = raw.charCodeAt(i);
      }
      console.log(array);
      return array;
    }
    
    export default {
      Decrypt,
      Encrypt,
      socketEncrypt,
      socketDecrypt
    }
    /* eslint-disable */
    

    以上大体逻辑代码贴出,省略了一些发出邀请,接受邀请 拒绝,逻辑相同
    最终的运行各种效果如下:

    音视频信令登录之后的绑定.png
    邀请好友
    邀请好友发出.png
    用户2收到用户1的邀请
    用户2收到用户1好友邀请.png
    接受好友邀请
    接收好友邀请.png
    对方成功接受了我的房间邀请
    对方成功接受了我的房间加入邀请.png
    拒绝好友邀请
    拒绝好友.png
    获取音视频服务器地址
    获取音视频服务器地址.png

    相关文章

      网友评论

          本文标题:2020-10-30 web前端im开发websocket编码加

          本文链接:https://www.haomeiwen.com/subject/lwqjvktx.html