// +---------------------------------------------------------------------- // | 桌子工作室 [webSokcet] V0.12015014 Released // | 上行数据 clinet 下行数据 service // +---------------------------------------------------------------------- // | Copyright (c) 2018-2019 ,MrTable All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: Mr Table // +---------------------------------------------------------------------- import CryptoJS from "../vendor/CryptoJS.js"; import { Base64 } from '../vendor/base64.js'; import Message from "./Message.js"; class WebSokcet { index = 0; //scoket socketTask = null; //事件返回 eventCall = null; //是否已连接 socketOpen = false; //定时器 //心跳 timerId = 0; //重发包 timerId2 = 0; //是否在重连中 isReconnect = false; //临时获取数据 tmpData = []; //临时提交数据包[上行数据] tmpCliData = []; //临时提交数据包[下行数据] tmpSerData = []; //设备标识符 deveicId = 0; //重试次数 retryCount = 5; isClose = false; opts = {}; constructor(opt) { let option = {wsUrl:""}; this.opts = this.extend(option,opt); } /** * 连接socket */ connect() { var that = this; this.isClose = false; this.socketTask = new wxSocket({ wsUrl: this.opts.wsUrl, //打开 onOpen:(res => { console.log("webSokcet连接成功"); that.socketOpen = true; if (that.eventCall != null) that.eventCall('onOpen', ''); //心跳包 that.heartBeat(); }), //获取消息 onMessage:(onMessage) => { var aData = that.asyMessage(onMessage); if (aData == null) return; if (aData == '') return; console.log("接受到消息:" + JSON.stringify(aData)); //确认帧【客户端主包】 if (aData.msgType == 2) { that.ackFrame(aData); return; } //检查重复包帧【服务端主包】 if (that.checkFrame(aData)) { if (that.eventCall != null) that.eventCall('onSendSuccess', aData); return; } that.tmpSerData.push({ time: new Date().getTime(),data: aData, msgId: aData.msgId}); aData.isReSend = true; if(aData.isAsk) { let obj = new Message(); obj.code = aData.code; obj.msgType = 2; obj.askId = aData.msgId; obj.data = that.eventCall == null ? "" : that.eventCall('onMessage', aData); this.send(obj.getData(),false); } else { if (that.eventCall != null) that.eventCall('onMessage', aData); } }, //获取错误信息 onError:res=>{ console.log(res); that.socketOpen = false; if (res.errMsg != 'exceed max task count'){ if (that.eventCall != null) that.eventCall('onError', res.errMsg); if(!this.isClose) that.reConnon(); } else { if (that.eventCall != null) that.eventCall('onStop', res.errMsg); } }, onClose:onClose => { console.log("webSokcet已关闭"); that.socketOpen = false; if (that.eventCall != null) that.eventCall('onClose', ''); this.socketTask.socket.close(); if(!this.isClose) that.reConnon(); } }); } /** * 设置事件 */ setBackCall(call) { this.eventCall = call; } ttClose(){ clearInterval(this.timerId); clearInterval(this.timerId2); this.isClose = true; if(this.socketTask != null) this.socketTask.socket.close(); } /** * 设置生成密钥 * @param key */ setKey(key) { this.opts.key = key; } /** * 发送数据 * @param data 数据 * fn * error * @returns {boolean} * */ send(data,fn = null,error = null) { if (!this.socketOpen) { return null; } //发送数据 try { //生成消息[分配设备ID] if (data.msgId == "") data.msgId = this.createMsgId(); //解析加密数据 console.log("发送数据:" + JSON.stringify(data.getData())); var AES = new CryptoJS.AES(this.opts.key); var jsonStr = AES.encrypt(JSON.stringify(data.getData())); //发送数据到服务器 this.socketTask.send({data:jsonStr}); //确认证就不存到缓存 if (!data.ask) return null; //是否缓存存在 var msgObj = this.findCliMsgId(data.msgId); //记录发送数据 if (msgObj == null){ this.tmpCliData.push({ 'msgId' : data.msgId,//消息ID 'data' : data, 'count' : 1,//尝试次数 'time' : new Date().getTime(),//最红发送时间 'code' : 0, 'fn' : fn, 'error' : error }); } else { msgObj['count']++; msgObj['time'] = new Date().getTime(); } return data.msgId; } catch (e) { console.log(e.message); return null; } } /** * 掉线从连接 */ reConnon() { clearInterval(this.timerId); clearInterval(this.timerId2); var time = new Date().getTime(); for (var i in this.tmpSerData) { var d = this.tmpSerData[i]; if (time - d.time > 10 * 1000){ this.tmpSerData.splice(i, 1); if (this.eventCall != null) this.eventCall('onSendError', d.data); } } setTimeout(function () { if (this.socketTask.readyState == 3) { console.log('webSokcet重连接'); this.connect(this.token); } }.bind(this), 2000); } /** * 心跳包 */ heartBeat() { this.timerId = setInterval(function () { if (this.socketTask.readyState == 1) { this.socketTask.send({ data: '01'}); if (this.eventCall != null) this.eventCall('onHeartBeat', {}); } }.bind(this), 10 * 1000); //重发包 this.timerId2 = setInterval(function () { if (this.socketTask.readyState == 1) { //重发机制 this.reSend(); //清理下行包 this.clerSerData(); } }.bind(this), 5 * 1000); return this; } /** * 数据重发 */ reSend(){ var time = new Date().getTime(); for (var i in this.tmpCliData) { var d = this.tmpCliData[i]; //重发 if(d.count < this.retryCount && time - d.time> 5000) { d.time = time; if (this.eventCall != null) this.eventCall('onReSend', d.data,d.count); this.send(d.data,false,null,null); continue; } if(d.count >= this.retryCount) { //发送失败 if (this.eventCall != null) this.eventCall('onSendError', d.data); if(d.error != null) d.error(); this.tmpCliData.splice(i, 1); } } } /** * 清理下行数据包[超过30秒] */ clerSerData(){ var time = new Date().getTime(); for (var i in this.tmpSerData) { var d = this.tmpSerData[i]; if (time - d.time > 30 * 1000){ this.tmpSerData.splice(i, 1); } } } /** * 查找消息位置[上行数据] */ findCliMsgId(msgId){ for (var i = 0; i < this.tmpCliData.length;i++){ if (this.tmpCliData[i].msgId == msgId) return this.tmpCliData[i]; } return null; } /** * 重复包检测【服务端主包】【服务端重发消息(重复包拦截)】 */ checkFrame(data){ // console.log(this.tmpSerData); for(var i in this.tmpSerData) { if(this.tmpSerData[i].data.msgId == data.msgId) { let sendAr = this.tmpSerData[i].data; if(sendAr.isAsk) { let obj = new Message(); obj.code = sendAr.code; obj.msgType = 2; obj.askId = sendAr.msgId; obj.data = sendAr.data; this.send(obj.getData(),false); return true; } } } return false; } /** * * 确认帧【客户端主包】【取消客户端重发消息】 * */ ackFrame(data) { for (var i = 0; i < this.tmpCliData.length; i++) { if (this.tmpCliData[i].msgId == data.askId){ if(this.tmpCliData[i].fn !== null) this.tmpCliData[i].fn(data.data); this.tmpCliData.splice(i, 1); break; } } } /** * 解析uStr */ asyMessage(uStr) { try{ //不支持这个 if(uStr instanceof ArrayBuffer) { return null; } //是否分割线 var isEof = false; if(uStr.length >= 4) { //\r\n\r\n if(uStr[uStr.length - 1] == "\n" && uStr[uStr.length - 2] == "\r" && uStr[uStr.length - 3] == "\n" && uStr[uStr.length - 4] == "\r" ) { isEof = true; } } //获取全部数据 if (isEof) { var unAr = [],unArStr = ""; //累加之前数据分包 for (var i in this.tmpData) { unAr.push(this.tmpData[i]); } unArStr = unAr.join(''); unArStr += uStr; this.tmpData = []; if(unArStr == "") return null; var AES = new CryptoJS.AES(this.opts.key); var data = AES.decrypt(unArStr.trim()); if(data == "") return null; //console.log(data); return JSON.parse(data); } else { this.tmpData.push(uStr); } } catch(err) { console.log(err); return null; } } /** * 格式化转化 */ Uint8ArrayToString(fileData) { var dataString = ""; for (var i = 0; i < fileData.length; i++) { dataString += String.fromCharCode(fileData[i]); } return dataString } createMsgId(){ this.index ++; return "web" + this.deveicId + new Date().getTime() + this.index; } /** * 配置数据默认匹配 * $a 默认数据比如:[] * $b 现在有 */ extend($a, $b) { let $r = []; //先遍历$b数据 for (var i in $b) { $r[i] = $b[i]; } //遍历默认数据 for (var i in $a) { if ($r[i] == null) $r[i] = $a[i]; } return $r; } } /** * websokcet * @param options * @returns {websocket} */ class wxSocket { readyState = 0; socket = null; opts = { wsUrl: "", onOpen:()=>{}, onMessage:()=>{}, onError:()=>{}, onClose:()=>{} }; constructor(opt) { this.opts = this.extend(this.opts,opt); this.onInit(); } onInit(){ console.log('发起链接'); console.log(this.opts.wsUrl); this.socket = uni.connectSocket({ url:this.opts.wsUrl, complete:(res)=>{} }); if(this.socket == null ){ setTimeout(()=>{ this.onInit(); },1000); return; } else { //监听返回 this.socket.onMessage(onMessage => { this.onMessage(onMessage);}); this.socket.onOpen(res => { this.onOpen(res); }); this.socket.onError(res => {this.onError(res);}); this.socket.onClose(res => {this.onClose(res);}); } } send(data) { this.socket.send({ data : data.data}); } onOpen(event){ this.bOpen = true; if(this.opts.onOpen){ this.readyState= 1; this.opts.onOpen(event); } } /** * * @param msg */ onSend(msg){ if(this.opts.onSend){ this.opts.onSend(msg); } this.socket.send(msg); } /** * * @param msg */ onMessage(msg){ if(this.opts.onMessage){ this.opts.onMessage(msg.data); } } /** * * @param event */ onError(event){ console.log(event); if(this.opts.onError){ this.readyState = 3; this.opts.onError(event); } } /** * * @param event */ onClose(event){ if(this.opts.onClose){ this.readyState = 3; this.opts.onClose(event); } if(this.socket.close() != null){ this.socket = null; } } extend($a, $b){ let $r = [];for (var i in $b) {$r[i] = $b[i];}for (var i in $a) {if ($r[i] == null) $r[i] = $a[i];}return $r; } } export default WebSokcet