Files
fc/dev/project/library/imports/a4/a4cf6ebe-157c-46e6-b778-87c4ed097a99.js
T

627 lines
18 KiB
JavaScript
Raw Normal View History

2026-05-24 10:21:26 +08:00
"use strict";
cc._RF.push(module, 'a4cf66+FXxG5rd4h8TtCXqZ', 'socket');
// Scripts/client/socket/socket.js
"use strict";
/*-----------------------------------------------------+
* socket相关处理
* @author whjing2012@163.com
+-----------------------------------------------------*/
var Proto = require("proto_mate");
var SocketSvr = require("SocketService");
var _require = require("config"),
SSL = _require.SSL;
// 心跳间隔&超时次数
var HEARTBEAT_ID = 1199;
var HEARTBEAT_PERIOD = 10000;
var HEARTBEAT_LIMIT = 2;
// 数据类型
var DataType = cc.Enum({
None: 0,
Int8: 1,
UInt8: 2,
Int16: 3,
UInt16: 4,
Int32: 5,
UInt32: 6,
String: 7,
Byte: 8,
Array: 9,
Float: 10,
Int64: 11,
UInt64: 12
});
var fmtNow = function fmtNow() {
var dt = new Date();
var date = dt.getHours() + ":" + dt.getMinutes() + ":" + dt.getSeconds();
return date;
};
var output = function output(_content) {
if (window.nx) {
nx.info("" + _content);
} else {
cc.log("" + fmtNow() + _content);
}
};
var invoke = function invoke(_fn) {
if (!!_fn && typeof _fn == 'function') {
_fn.apply(null, Array.prototype.slice.call(arguments, 1));
}
};
var CliSocket = {
getTime: function getTime() {
return this.diff_time + parseInt(cc.sys.now() / 1000);
},
getMsTime: function getMsTime() {
return this.diff_time * 1000 + cc.sys.now();
},
setTime: function setTime(time) {
this.diff_time = time - parseInt(cc.sys.now() / 1000);
},
init: function init() {
this.net_connect = null;
this.buffer = null;
this.listeners = {};
this.msg_list = [];
this.diff_time = 0;
this.connecting = false;
this.check_time = 0;
//初始化网络接口
SocketSvr.InitWebSocket(WebSocket);
//创建一个连接实例: 支持wss/ws
this.net_connect = SocketSvr.CreateConnect(SSL);
// 初始化网络事件
this.net_connect.Init(this, this.on_open, this.on_message, this.on_close, this.on_error);
// 网络协议转换,当前不使用
//this.net_connect.AttachMessage(fn_on_receive,fn_on_send);
// 注册心跳
this.readyHeartBeat();
if (!Uint8Array.prototype.slice) {
Uint8Array.prototype.slice = Array.prototype.slice;
}
},
// ======================================================================
// 心跳处理
// ======================================================================
// 心跳监听
readyHeartBeat: function readyHeartBeat() {
var _this = this;
// cc.log( "$心跳:开始" );
this.hb_caches = 0;
this.bindCmd(HEARTBEAT_ID, this.on1199.bind(this), "hb");
this.net_connect.AttachBeartbeat(HEARTBEAT_PERIOD, function () {
_this.tickHeartBeat();
return _this.structMsg(HEARTBEAT_ID, {});
});
},
// 心跳检查
tickHeartBeat: function tickHeartBeat() {
// cc.log( "$心跳:触发, 次数: %s -> %s", this.hb_caches, this.hb_caches + 1 );
// let old = nx.bridge.vget( "connFlag" );
// if( old != "good" ) {
// return;
// }
// 缓存未回复次数
this.hb_caches += 1;
if (this.hb_caches >= HEARTBEAT_LIMIT) {
cc.log("$心跳:超时,主动断开网络!");
this.breakws();
}
},
// 心跳 5发1收
on1199: function on1199(data) {
// cc.log( "$心跳:有效回复, 当前:", this.hb_caches );
this.hb_caches = 0;
},
// ======================================================================
// 连接处理
// ======================================================================
// 连接网络
connect: function connect(host, port, ws, _cb) {
output(cc.js.formatStr("$网络:连接开始... 地址:%s,端口:%s,SSL:%s", host, port, ws || "ws"));
this.buffer = null;
this.hb_caches = 0;
this.closeFlag = "";
// 连接开始
this.net_connect.SetSSL(ws == "wss");
var code = this.net_connect.Connect(host, port);
if (code == SocketSvr.ErrCode().SUCC) {
this.setConnecting(true);
this.setLinkFlag("linking");
this.cbConnect = _cb;
return;
}
// 重复连接
if (code == SocketSvr.ErrCode().ERR_ALREADY_CONNECTED) {
invoke(_cb, true, "repeat");
this.setConnecting(true);
this.setLinkFlag("good");
this.cbConnect = null;
return;
}
// 连接失败
invoke(_cb, false, "TipConnectFailed");
this.setConnecting(false);
this.setLinkFlag("close");
this.cbConnect = null;
},
// 关闭网络
close: function close(_flag) {
if (_flag === void 0) {
_flag = "close";
}
output(cc.js.formatStr("$网络:连接关闭... %s", this._curInfo()));
this.closeFlag = _flag;
if (this.net_connect) {
this.net_connect.DisConnect();
}
},
// 断开websocket
breakws: function breakws() {
output(cc.js.formatStr("$网络:连接断开... %s", this._curInfo()));
this.close();
},
// 发送消息
send: function send(cmd, data) {
// 完整日志
output(cc.js.formatStr("[send] >>>[%d]:%s", cmd, JSON.stringify(data || {})));
// 参数封装
var p_data = this.structMsg(cmd, data);
if (!p_data) {
return false;
}
return this.net_connect.Send(p_data);
},
// 协议监听
bindCmd: function bindCmd(cmd, func, key) {
if (!func) {
cc.error("$Socket:无效监听函数!", cmd, key);
return;
}
if (!this.listeners[cmd]) {
this.listeners[cmd] = [];
}
// 重复判定
var flag = key || "none";
if (CC_DEBUG) {
for (var i in this.listeners[cmd]) {
var tm = this.listeners[cmd][i];
if (tm && tm.key == flag) {
cc.error("$Socket:重复监听!", cmd, flag);
return;
}
}
}
// 加入列表
this.listeners[cmd].push({
cb: func,
key: flag
});
},
// 协议监听解除
unbindCmd: function unbindCmd(_cmd, _key) {
var arr = this.listeners[_cmd];
if (!arr || arr.length == 0) {
return;
}
var flag = _key || "none";
var i = arr.length;
while (i--) {
if (arr[i].key == flag) {
arr.splice(i, 1);
}
}
},
// 发送消息组装
structMsg: function structMsg(cmd, data) {
try {
var bytes = this.packData(cmd, data);
var buffer = new ArrayBuffer(bytes.length + 4);
var view = new DataView(buffer);
view.setUint32(0, bytes.length);
for (var i = 0; i < bytes.length; i++) {
view.setUint8(i + 4, bytes[i]);
}
return view.buffer;
} catch (error) {
cc.error("[send] send_msg_error:" + error.message, error.stack);
if (error && cc.sys.isNative && window && window.__errorHandler) {
window.__errorHandler(error.message, "", "", error.stack);
}
return null;
}
},
handleMsg: function handleMsg() {
//这里不需要,网络层自驱动,如需特殊处理,打开即可
//
// if( this.msg_list.length == 0 ) return;
// for( var i = 0; i < 5; i++ ) {
// var msg = this.msg_list.shift();
// this.onCmdCallback( msg.cmd, msg.data );
// if( this.msg_list.length == 0 ) return;
// }
},
// ======================================================================
// 监听处理
// ======================================================================
// 事件:网络打开准备就绪
on_open: function on_open(self, is_reconnect, e) {
cc.log("$网络监听:已打开... %s", self._curInfo());
self.setConnecting(false);
gcore.GlobalEvent.fire_x(gcore.GlobalEvent, gcore.GlobalEvent.EVT_SOCKET_CONNECT);
// 视图标记
self.setLinkFlag("good");
// 回调
if (self.cbConnect) {
invoke(self.cbConnect, true);
self.cbConnect = null;
}
},
// 事件:网络接收消息
on_message: function on_message(self, msg, data) {
self.doRecv(data, self);
},
// 事件:网络关闭
on_close: function on_close(self) {
cc.log("$网络监听:已关闭... %s", self._curInfo());
self.setConnecting(false);
gcore.GlobalEvent.fire_x(gcore.GlobalEvent, gcore.GlobalEvent.EVT_SOCKET_DISCONNECT, self.closeFlag);
// 视图标记
self.setLinkFlag(self.closeFlag);
return true;
},
// 事件:网络错误
on_error: function on_error(self, e) {
cc.error("$网络监听:错误... %s", self._curInfo());
console.error(e);
// 回调
if (self.cbConnect) {
invoke(self.cbConnect, false, "TipConnectFailed");
self.cbConnect = null;
}
},
onCmdCallback: function onCmdCallback(cmd, data) {
if (!window.nx) {
return;
}
try {
var arr = this.listeners[cmd];
if (arr && arr.length > 0) {
arr.forEach(function (_hook) {
if (_hook.cb) {
invoke(_hook.cb, data, cmd);
}
});
}
} catch (error) {
cc.error("handle_msg_error:" + cmd + ", err=" + error.message + ", data=" + data, error, error.stack);
if (error && cc.sys.isNative && window && window.__errorHandler) {
window.__errorHandler(error.message, "", "", error.stack);
}
}
},
// 设置连接状态
setConnecting: function setConnecting(_doing) {
this.connecting = _doing;
},
// ======================================================================
// 设置连接状态
// ======================================================================
// 设置
setLinkFlag: function setLinkFlag(_flag) {
if (!window.nx) {
return;
}
var flag = _flag || "close";
var old = nx.bridge.vget("connFlag");
if (old == flag) {
return;
}
output(cc.js.formatStr("$SOCKET: %s -> %s", old, flag));
nx.bridge.vset("connFlag", flag);
},
// ======================================================================
// 数据处理
// ======================================================================
doRecv: function doRecv(buffer, self) {
// 重置心跳包
this.check_time = 0;
if (typeof buffer == "string")
//服务器传过来的可能是字符串,判断是不是
{
output("[recv] =text= " + buffer);
return;
}
if (self.buffer == null) {
buffer = new Uint8Array(buffer);
} else {
var a = Array.from(new Uint8Array(buffer));
var b = Array.from(new Uint8Array(self.buffer));
buffer = b.concat(a);
}
this.unpackBuffer(buffer);
},
unpackBuffer: function unpackBuffer(buffer) {
if (buffer.length < 6) {
this.buffer = buffer;
return;
}
var dataview = new DataView(new Uint8Array(buffer).buffer);
var len = dataview.getUint32(0, false);
//cc.log("======= " + len);
if (len > 80000) {
cc.error("[recv] data_to_long:" + len);
this.net_connect.DisConnec();
}
var data_len = buffer.length;
if (len + 4 > data_len) {
return;
}
var cmd = 0;
try {
cmd = dataview.getUint16(4, false);
this.unpackData(dataview, cmd, buffer, len);
} catch (error) {
cc.error("[recv] " + cmd + ": unpackData_error:" + error.message, buffer, error.stack);
if (error && cc.sys.isNative && window && window.__errorHandler) {
window.__errorHandler(error.message, "", "", error.stack);
}
}
if (data_len > len + 4) {
// cc.log( "data_length==", cmd, len );
this.unpackBuffer(buffer.slice(len + 4));
} else {
this.buffer = null;
// if( cmd != HEARTBEAT_ID ) {
// cc.error( "协议空内容:" + cmd );
// }
}
},
unpackData: function unpackData(dataview, cmd, buffer, len) {
if (!Proto.recv.hasOwnProperty(cmd)) {
throw new Error("[recv] unpackData查找不到协议定义:" + cmd);
}
var data = {};
var mate = Proto.recv[cmd];
this.unpackData_(dataview, buffer, data, mate, 6, len);
// 屏蔽心跳
if (cmd != '1199') {
output(cc.js.formatStr("[recv] <<<[%d]:%s", cmd, JSON.stringify(data || {}, function (key, value) {
return typeof value === 'bigint' ? value.toString() : value;
} // return everything else unchanged
)));
}
if (!this.listeners[cmd]) {
cc.warn("[recv] 协议处理函数未定义:" + cmd);
return;
}
this.onCmdCallback(cmd, data);
return data;
},
unpackData_: function unpackData_(dataview, buffer, data, mate, pos, len) {
for (var i = 0, n = mate.length; i < n; i++) {
var field = mate[i];
//cc.log("field=" +field.s +" i=" + i + " t=" + field.t + " n=" +n + " pos=" + pos);
switch (field.t) {
case DataType.Int8:
{
data[field.s] = dataview.getInt8(pos, false);
pos += 1;
}
break;
case DataType.UInt8:
{
data[field.s] = dataview.getUint8(pos, false);
pos += 1;
}
break;
case DataType.Int16:
{
data[field.s] = dataview.getInt16(pos, false);
pos += 2;
}
break;
case DataType.UInt16:
{
data[field.s] = dataview.getUint16(pos, false);
pos += 2;
}
break;
case DataType.Int32:
{
data[field.s] = dataview.getInt32(pos, false);
pos += 4;
}
break;
case DataType.UInt32:
{
data[field.s] = dataview.getUint32(pos, false);
pos += 4;
}
break;
case DataType.String:
{
var s_len = dataview.getUint16(pos, false);
pos += 2;
var unit8Arr = new Uint8Array(buffer.slice(pos, pos + s_len));
var encodedString = String.fromCharCode.apply(null, unit8Arr);
data[field.s] = decodeURIComponent(escape(encodedString)); //没有这一步中文会乱码
pos += s_len;
}
break;
case DataType.Byte:
{
var s_len = dataview.getUint32(pos, false);
pos += 4;
data[field.s] = buffer.slice(pos, pos + s_len); //没有这一步中文会乱码
pos += s_len;
}
break;
case DataType.Array:
{
var s_len = dataview.getUint16(pos, false);
pos += 2;
var arr = [];
data[field.s] = arr;
for (var j = 0; j < s_len; j++) {
var data1 = {};
arr[j] = data1;
pos = this.unpackData_(dataview, buffer, data1, field.f, pos, len);
}
}
break;
case DataType.Float:
{
data[field.s] = dataview.getFloat64(pos, false);
pos += 8;
}
break;
case DataType.Int64:
{
data[field.s] = dataview.getInt64(pos, false);
pos += 8;
}
break;
case DataType.UInt64:
{
data[field.s] = dataview.getUint64(pos, false);
pos += 8;
}
break;
default:
{
cc.warn("无效数据类型:" + field.t);
}
break;
}
//cc.log("unpack_data:" + field.s + ", val=" + data[field.s]);
}
return pos;
},
packData: function packData(cmd, data) {
if (!Proto.send.hasOwnProperty(cmd)) {
throw new Error("unpackData查找不到协议定义:" + cmd);
}
// cc.log( "pack_data2:" + cmd );
var mate = Proto.send[cmd];
var bytes = [];
this.i16ToBytes(bytes, cmd);
this.packData_(bytes, mate, data);
return bytes;
},
packData_: function packData_(bytes, mate, data) {
for (var i = 0, n = mate.length; i < n; i++) {
var field = mate[i];
var val = data[field.s];
// cc.log("pack_data2:" + field.s + ", val=" + val);
switch (field.t) {
case 1:
case 2:
bytes.push(new Uint8Array([val]));
break;
case 3:
case 4:
this.i16ToBytes(bytes, val);
break;
case 5:
case 6:
this.i32ToBytes(bytes, val);
break;
case 7:
this.strToBytes(bytes, val);
break;
case 8:
var s_len = val.length;
this.i32ToBytes(bytes, s_len);
for (var j = 0; j < s_len; j++) {
bytes.push(val[j]);
}
break;
case 9:
var s_len = val.length;
// cc.log("pack_data2:" + field.s + ", val=" + val + ", len:" + s_len);
this.i16ToBytes(bytes, s_len);
for (var j = 0; j < s_len; j++) {
if (val[0] == null && val.length > 0) {
j = j + 1;
}
this.packData_(bytes, field.f, val[j]);
}
break;
}
}
},
strToBytes: function strToBytes(bytes, string) {
var str = unescape(encodeURIComponent(string));
var len = str.length;
this.i16ToBytes(bytes, len);
for (var i = 0; i < len; i++) {
bytes.push(str.charCodeAt(i));
}
},
i16ToBytes: function i16ToBytes(bytes, num) {
var a = new Uint8Array(new Uint16Array([num]).buffer);
bytes.push(a[1]);
bytes.push(a[0]);
},
i32ToBytes: function i32ToBytes(bytes, num) {
var a = new Uint8Array(new Uint32Array([num]).buffer);
bytes.push(a[3]);
bytes.push(a[2]);
bytes.push(a[1]);
bytes.push(a[0]);
},
// 格式化当前连接信息
_curInfo: function _curInfo(_self) {
var txt = "无连接";
var self = _self || this;
if (self.net_connect) {
var ip = self.net_connect.GetIp();
var port = self.net_connect.GetPort();
txt = cc.js.formatStr("连接地址:%s,端口号: %d", ip, port);
}
return txt;
}
};
// 载入
var install = function install() {
CliSocket.init();
client.socket = CliSocket;
};
// 导出
module.exports = {
install: install
};
cc._RF.pop();