美文网首页
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编码加

    近期开始在公司做web前端音视频开发,流程分析:第一步音视频信令登录: 例如获取到的json数据格式如下: 上一步...

  • 好程序员web前端培训分享WebSocket协议

    好程序员web前端培训分享WebSocket协议,WebSocket协议简介 一.WebSocket协议简介 1....

  • 2018-05-16

    web前端开发 什么是web前端 web前端开发也戏称“web前端开发攻城狮”,目前这个职位也叫“大前端”。这个职...

  • HTML基础

    新的征程 web前端介绍 web前端开发做什么 pc端web开发;移动端web开发;混合app开发;网页游戏/移动...

  • Web前端工程师成长之路

    web前端工程师 分类: Web开发应用 一、何为Web前端工程师? 前端工程师,也叫Web前端开发工程师。他是随...

  • 第01阶段:第一小节第一天

    Web前端+移动开发——大前端的定义 一、 Web前端和全...

  • Web前端发展现状

    Web前端开发现状 到目前为止,web前端开发正处于发展的高峰期。由于各互联网公司都注意到站点的可用性问题,为了加...

  • 07-HTML标签

    前端开发 前端开发也叫做web前端开发,它指的是基于web的互联网产品的页面开发及功能开发。 html HTML是...

  • Day006 - 感性认识JavaScript (2018-11

    知识点梳理: Web应用后端开发 -- web前端网络数据采集 -- 爬虫和前端开发人员交互 web前端 = HT...

  • 前端资源教程

    资源教程原文 综合类前端知识体系前端知识结构Web前端开发大系概览Web前端开发大系概览-中文版Web Front...

网友评论

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

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