/*eslint-disable*/ /** * 用于处理websocket实时通讯使用 */ export class MyWebSocket { // 每次 new 出对象则会调用这个构造函数 constructor() { // 端口号 this.prot = 3000; // new出webSocket通讯对象 this.objWebSocket = null; // webSocket 服务端ip this.ip = "127.0.0.1"; // 如果是平时开发后,发布到服务器,域名访问,那么就用这个 // this.ip = document.domain; // 用于触发重连的计时器 this.webSocketRestConnectTime = null; // 记录当前进入的房间号 this.room = null; // 记录当前自己的socketId值 this.socketId = null; /** * 记录当前的userId,用户登陆后 * 就会固定唯一的 userId, 之后就不会在改变 * 这里的userId 可以是数据库的,没有数据库的 * 可以用第一次登陆的 socketId 作为记录 * * 用于后续统一通讯告诉大家例如角色,还是别的数据使用 * 不会因为后续每次断开重连,导致 socketId 的变化 * 而要重置已经存在在别的地方的渲染数据使用 */ this.userId = null; /** * 记录当前用户自己本身的参数 */ this.myConfig = null; // 昵称 this.nickname = null; // 记录最后一次进入的房间号,用于后续触发重连的逻辑 this.roomId = null; // 当断开连接,重置false,后面触发重连进入房间逻辑 this.joinRoomBool = false; /** * 记录当前房间号的用户列表 json 结构 * 每当用户进入,或者退出,该参数优先更新逻辑 */ this.roomListJson = { // "唯一cid" : "对应json数据", }; /** * 记录当前房间号的用户列表 * 对应 cid 的数据记录在 this.roomListJson */ this.roomList = []; /** * 每当房间里发送变化的时候触发回调 * * type 类型 * roomUserExit 有用户退出 * roomUserJoin 有用户加入 * roomUserDbUpdate 用户数据发送了变化 * * cidJson 加入或者退出的用户的 json数据 * dbUpdate 当 type 是 roomUserDbUpdate 的时候,才有的参数回调,具体变化的字段数据 * */ this.roomUpdateCallback = null; /** * 每次房间里有群发消息的时候回调 * cidJson 用户的 json数据 * message 消息的内容 */ this.roomChatAllCallback = null; /** * 每次有其他用户单独发送消息给你回调 * sendCidJson 发起消息用户的json数据 * getCidJson 接收消息用户的json数据 * message 消息内容 */ this.cidSendCallback = null; } // 实现单例模式 static find() { if (!MyWebSocket.instance) { MyWebSocket.instance = new MyWebSocket(); } return MyWebSocket.instance; } /** * webSocket 初始化连接 * */ webSocketInit = function() { let thisClass = this; // 只有是空的时候 if (thisClass.objWebSocket == null || thisClass.objWebSocket == 'undefined') { var webSocketUrl = "ws://" + this.ip + ":" + this.prot; // 连接 webSocket thisClass.objWebSocket = new WebSocket(webSocketUrl); thisClass.webSocketEvent(); } } /** * 断开后,自动触发重新连接通讯逻辑 */ webSocketRestConnect = function() { let thisClass = this; if (thisClass.webSocketRestConnectTime != null && thisClass.webSocketRestConnectTime != 'undefined') { return this; } if (thisClass.objWebSocket != null && thisClass.objWebSocket != 'undefined') { return this; } thisClass.joinRoomBool = false; thisClass.webSocketRestConnectTime = setInterval(function() { clearInterval(thisClass.webSocketRestConnectTime); thisClass.webSocketRestConnectTime = null; thisClass.webSocketInit(); }, 1000); } /** * 连接成功后,调用该方法,设置 objWebSocket 的事件 * */ webSocketEvent = function() { let thisClass = this; /** * 实例对象的 onopen 属性 * 当WebSocket客户端与服务器建立连接并完成握手后会回调此函数。 * 参数 evt 就是 数据的返回 * */ thisClass.objWebSocket.onopen = function(evt) { console.log([ "连接成功", evt ]); } /** * 实例对象的 onmessage 属性 * 当服务器收到来自客户端的数据帧时会回调此函数。 * 参数 evt 就是 数据的返回 * */ thisClass.objWebSocket.onmessage = function(evt) { // console.log("onmessage 回调数据"); // console.log(evt); // console.log(evt.data); thisClass.commonMessage(evt.data); } /** * 实例对象的 onclose 属性 * 目标客户端id 退出关闭的时候,会触发该事件 * 服务端关闭也会触发该事件 * 参数 evt 就是 数据的返回 * */ thisClass.objWebSocket.onclose = function(evt) { // console.log("onclose 回调数据"); // console.log(evt); // 此时将目标对象重置为null,方便后续重新连接 thisClass.objWebSocket = null; thisClass.webSocketRestConnect(); } /** * 报错的时候执行这里,例如连接失败 * */ thisClass.objWebSocket.onerror = function(evt, e) { // console.log("onerror 回调数据"); // console.log(evt); // // console.log(evt.data); // 此时将目标对象重置为null,方便后续重新连接 thisClass.objWebSocket = null; thisClass.webSocketRestConnect(); } } /** * 统一处理接收到的消息 * serverData 接收服务端的消息 */ commonMessage = function(serverData) { let thisClass = this; let json = {}; try { json = JSON.parse(serverData); } catch(e) { return thisClass; } // console.log("commonMessage", json); // 当前是哪个cid发来的消息 let cid = json["cid"]; let data = json["data"]; let dataType = data["type"]; console.log("commonMessage", json, dataType, data); if (typeof dataType == "string") { switch (dataType) { // 心跳包的ping,用于验证用户是否还在正常连接,没有回复pong,则服务端强制退出 case "ping": thisClass.commonSend("pong", ""); break; // 第一次连接通讯后,服务端发来的消息 case "login": thisClass.loginEvent(json); break; // 每次用户自己在服务器成功更新了哪些字段数据,则得到对应更新的字段数据 case "myDbUpdate": thisClass.myDbUpdateEvent(json); // 如果自己已经加入过房间号,则自动再触发进入房间逻辑,实现重连后进入房间的逻辑 if ( thisClass.roomId != null && thisClass.roomId != undefined && thisClass.joinRoomBool == false ) { thisClass.joinRoom(room); } break; // 当房间里有新用户进入的时候,给当前房间号的所有用户发送消息 case "roomUserJoin": thisClass.roomUserJoinEvent(json); break; // 当房间里有用户退出离开的时候的时候,给当前房间号的所有用户发送消息 case "roomUserExit": thisClass.roomUserExitEvent(json); break; // 当房间里某个用户更新了数据字段的时候 case "roomUserDbUpdate": thisClass.roomUserDbUpdateEvent(json); break; // 当前房间接收到聊天群发的消息 case "roomChatAll": thisClass.roomChatAllEvent(json); break; // 给指定用户私聊 case "cidSend": thisClass.cidSendEvent(json); break; } } return thisClass; } /** * 统一发送消息给服务端格式 * 服务端会根据接收不同的类型,执行操作,例如更新用户数据,或者是群发消息,或者是进入房间,或者是给指定的用户单独发送消息等逻辑触发 * * type 类型,不同的类型,会触发的逻辑不一样 * upUser 更新当前用户的参数,例如昵称,或者坐标点 * joinRoom 用户进入房间或者切换到另外一个房间 * roomSendAll 房间里群发消息,房间里所有用户可以接收到消息,是一个通用的传输数据 * 这里只做实时通讯消息发送,具体最后前端进行逻辑处理 * cidSend 给指定用户私聊 * * message 给服务端发送的消息,可以是字符串,也可以是json结构 * * * */ commonSend = function(type, message) { let thisClass = this; if (thisClass.objWebSocket == null || thisClass.objWebSocket == undefined) { return thisClass; } // 设置发送消息的时间戳,精确到毫秒 let thisTime = new Date().getTime(); // 消息传输统一格式,格式不正确则无法正常通讯 var submitJson = { "type" : type, "message" : message, // // 客户端发送时间 // "sendTime" : new Date().Format("yyyy-MM-dd hh:mm:ss"), "thisTime" : thisTime, }; thisClass.objWebSocket.send(JSON.stringify(submitJson)); return thisClass; } /** * 处理当前用户第一次登陆的时候的逻辑 * json 接收服务端的参数 */ loginEvent = function(json) { let thisClass = this; // 当前是哪个cid发来的消息 let cid = json["cid"]; let data = json["data"]; let dataMessage = data["message"]; let dataMessageUserDb = dataMessage["userDb"]; let dataMessageUserDbCid = dataMessageUserDb["cid"]; thisClass.socketId = dataMessageUserDbCid; if (thisClass.userId == null || thisClass.userId == undefined) { thisClass.userId = dataMessageUserDbCid; } thisClass.myConfig = dataMessageUserDb; // console.log( // thisClass.socketId, // thisClass.myConfig, // thisClass.userId // ); thisClass.loginUserUpdate(); return thisClass; } /** * 登陆后,先更新初始的用户数据 */ loginUserUpdate = function() { let thisClass = this; thisClass.commonSend( "upUser", { "name" : thisClass.nickname, "userId" : thisClass.userId, } ); return thisClass; } /** * 每次用户自己在服务器成功更新了哪些字段数据,则得到对应更新的字段数据 * json */ myDbUpdateEvent = function(json) { let thisClass = this; // 当前是哪个cid发来的消息 let cid = json["cid"]; let data = json["data"]; let dataMessage = data["message"]; let updateJson = dataMessage["updateJson"]; // 更新的字段数据 for (let key in updateJson) { let val = updateJson[key]; thisClass.myConfig[key] = val; } return thisClass; } /** * 进入房间 * room 房间号 */ joinRoom = function(room) { let thisClass = this; if (thisClass.userId == null && thisClass.userId == undefined) { console.log("连接通讯后请先更新 userId 字段才可进入房间"); return; } var submitJson = { "roomId" : room, }; thisClass.roomId = room; thisClass.commonSend( "joinRoom", submitJson ); thisClass.joinRoomBool = true; return thisClass; } /** * 当房间里有新用户进入的时候,给当前房间号的所有用户发送消息 * json */ roomUserJoinEvent = function(json) { let thisClass = this; // 当前是哪个cid发来的消息 let cid = json["cid"]; let data = json["data"]; let dataMessage = data["message"]; // 此时是该cid进入房间 let sendCid = dataMessage["sendCid"]; // 得到当前房间列表的所有用户信息 let roomList = dataMessage["message"]; // 加入用户的数据 let joinUser = null; // 循环更新对应房间数据 for (let i = 0; i < roomList.length; i++) { let objRoomList = roomList[i]; let objRoomListCid = objRoomList["cid"]; // 此时说明是该用户加入的房间 if (objRoomListCid == sendCid) { // console.log("用户加入房间", objRoomList); joinUser = objRoomList; } thisClass.roomListJson[objRoomListCid] = objRoomList; } // 更新房间列表用户数据 thisClass.roomList = roomList; if (thisClass.roomUpdateCallback != null && thisClass.roomUpdateCallback != undefined) { thisClass.roomUpdateCallback("roomUserJoin", joinUser, null); } return thisClass; } /** * 当房间里有用户退出离开的时候的时候,给当前房间号的所有用户发送消息 * json */ roomUserExitEvent = function(json) { let thisClass = this; // 当前是哪个cid发来的消息 let cid = json["cid"]; let data = json["data"]; let dataMessage = data["message"]; // 此时是该cid退出房间 let sendCid = dataMessage["sendCid"]; // 得到当前房间列表的所有用户信息 let roomList = dataMessage["message"]; // 循环更新对应房间数据 for (let i = 0; i < roomList.length; i++) { let objRoomList = roomList[i]; let objRoomListCid = objRoomList["cid"]; thisClass.roomListJson[objRoomListCid] = objRoomList; } // 更新房间列表用户数据 thisClass.roomList = roomList; // 退出的用户数据 let userExit = null; // 找到哪个用户退出 if (thisClass.roomListJson[sendCid] != null && thisClass.roomListJson[sendCid] != undefined) { // 这里进行一个转换,让它变成独立的对象 userExit = JSON.parse(JSON.stringify(thisClass.roomListJson[sendCid])); // console.log("用户退出房间", userExit); // 退出用户的数据清除 thisClass.roomListJson[sendCid] = null; delete thisClass.roomListJson[sendCid]; } if (thisClass.roomUpdateCallback != null && thisClass.roomUpdateCallback != undefined) { thisClass.roomUpdateCallback("roomUserExit", userExit, null); } return thisClass; } /** * 当房间里某个用户更新了数据字段的时候 * json */ roomUserDbUpdateEvent = function(json) { let thisClass = this; // 当前是哪个cid发来的消息 let cid = json["cid"]; let data = json["data"]; let dataMessage = data["message"]; // 此时是该cid更新的字段数据 let sendCid = dataMessage["sendCid"]; // 更新了哪些字段数据 let dbUpdate = dataMessage["message"]; console.log( "当房间里某个用户更新了数据字段的时候 roomUserDbUpdateEvent ", // json, sendCid, dbUpdate, // this.roomListJson, // this.roomList ); let dbUpdateUserId = null; // 如果更新的字段带有 userId if (dbUpdate["userId"] != null && dbUpdate["userId"] != null) { dbUpdateUserId = dbUpdate["userId"]; } // 找到被更新的用户数据 let updateUser = null; // 循环房间的所有数据 for (let i = 0; i < this.roomList.length; i++) { let objRoomList = this.roomList[i]; let objRoomListCid = objRoomList["cid"]; let objRoomListUserId = objRoomList["userId"]; // console.log( // "roomUserDbUpdateEvent objRoomList ===》", objRoomList // ); // 是否找到 let objBool = false; // 优先以 userId 进行寻找对象 if ( dbUpdateUserId != null && dbUpdateUserId != undefined && dbUpdateUserId == objRoomListUserId ) { objBool = true; } else if (objRoomListCid == sendCid) { objBool = true; } if (objBool == true) { // console.log( // "roomUserDbUpdateEvent objBool ===》", objRoomList // ); // 开始更新对应的字段数据 for (var key in dbUpdate) { objRoomList[key] = dbUpdate[key]; } // 更新指定目标cid的用户相关数据 if (this.roomListJson[objRoomListCid] != null && this.roomListJson[objRoomListCid] != undefined) { // 开始更新对应的字段数据 for (var key in dbUpdate) { this.roomListJson[objRoomListCid][key] = dbUpdate[key]; } } updateUser = objRoomList; } } // console.log( // "roomUserDbUpdateEvent ===》", this.roomListJson, this.roomList // ); if (thisClass.roomUpdateCallback != null && thisClass.roomUpdateCallback != undefined) { thisClass.roomUpdateCallback("roomUserDbUpdate", updateUser, dbUpdate); } return thisClass; } /** * 给当前自己所在房间号发送消息 * type 自定义类型,该类型你定义的是什么,在返回的房间里,就会返回什么类型 * 如果你是通知所有房间群发聊天填写 chatAll * 否则你填写别的类型,然后再根据这个类型,单独处理逻辑 * * message 给服务端发送的消息,可以是字符串,也可以是json结构 * 该参数后续根据 type 参数,来配合开发新的逻辑 * 例如我填写 type = chatAll , 该参数就传字符串聊天的内容 * 如果传别的类型,可以传json结构,然后通过该接收的消息,进行特殊处理别的逻辑 */ roomSend = function(type, message) { let thisClass = this; if (this.myConfig == null || this.myConfig == undefined) { return thisClass; } if (this.myConfig["roomId"] == null || this.myConfig["roomId"] == undefined) { return thisClass; } let roomId = this.myConfig["roomId"]; // console.log("roomSend myConfig", this.myConfig, type, message, roomId); let submit = { "roomId" : roomId, "type" : type, "content" : message, }; thisClass.commonSend("roomSendAll", submit); return thisClass; } /** * 给指定cid私聊发送消息 * type 自定义类型,该类型你定义的是什么,就会返回什么类型 * 如果你是单独处理聊天则填写 chat * 否则你填写别的类型,然后再根据这个类型,单独处理逻辑 * * cid 跟对方私聊的cid值 * * message 给服务端发送的消息,可以是字符串,也可以是json结构 * 该参数后续根据 type 参数,来配合开发新的逻辑 * 例如我填写 type = chat , 该参数就传字符串聊天的内容 * 如果传别的类型,可以传json结构,然后通过该接收的消息,进行特殊处理别的逻辑 */ cidSend = function(type, cid, message) { let thisClass = this; let submit = { "cid" : cid, "type" : type, "content" : message, }; thisClass.commonSend("cidSend", submit); return thisClass; } /** * 当前房间接收到聊天群发的消息 * json */ roomChatAllEvent = function(json) { let thisClass = this; // console.log("roomChatAllEvent", json); let cid = json["cid"]; let data = json["data"]; let dataMessage = data["message"]; let dataMessageSendCid = dataMessage["sendCid"]; let dataMessageMessage = dataMessage["message"]; // console.log("roomChatAllEvent", dataMessageSendCid, dataMessageMessage); if (thisClass.roomListJson[dataMessageSendCid] == null || thisClass.roomListJson[dataMessageSendCid] == undefined) { return thisClass; } let cidJson = thisClass.roomListJson[dataMessageSendCid]; if (typeof thisClass.roomChatAllCallback == 'function') { thisClass.roomChatAllCallback(cidJson, dataMessageMessage); } return thisClass; } /** * 给指定用户发送消息 * json */ cidSendEvent = function(json) { let thisClass = this; // console.log( "cidSendEvent", json ); let cid = json["cid"]; let data = json["data"]; let dataMessage = data["message"]; // 哪个cid发起的 let dataMessageSendCid = dataMessage["sendCid"]; // 接收消息的cid let dataMessageGetCid = dataMessage["getCid"]; let dataMessageSubmitJson = dataMessage["submitJson"]; let dataMessageSubmitJsonMessage = dataMessageSubmitJson["message"]; if (thisClass.roomListJson[dataMessageSendCid] == null || thisClass.roomListJson[dataMessageSendCid] == undefined) { return thisClass; } if (thisClass.roomListJson[dataMessageGetCid] == null || thisClass.roomListJson[dataMessageGetCid] == undefined) { return thisClass; } let sendCidJson = thisClass.roomListJson[dataMessageSendCid]; let getCidJson = thisClass.roomListJson[dataMessageGetCid]; // console.log( // "cidSendEvent", // dataMessageSendCid, // dataMessageSubmitJson, // dataMessageSubmitJsonMessage, // dataMessageGetCid, // sendCidJson, // getCidJson // ); if (typeof thisClass.cidSendCallback == 'function') { thisClass.cidSendCallback(sendCidJson, getCidJson, dataMessageSubmitJsonMessage); } return thisClass; } }