近期开始在公司做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
用户2收到用户1的邀请
用户2收到用户1好友邀请.png
接受好友邀请
接收好友邀请.png
对方成功接受了我的房间邀请
对方成功接受了我的房间加入邀请.png
拒绝好友邀请
拒绝好友.png
获取音视频服务器地址
获取音视频服务器地址.png
网友评论