644 lines
15 KiB
JavaScript
644 lines
15 KiB
JavaScript
"use strict";
|
||
cc._RF.push(module, '2ae25g525BKXop4TohwM707', 'SocketService');
|
||
// Scripts/client/socket/SocketService.js
|
||
|
||
"use strict";
|
||
|
||
/**
|
||
* @author: kevin
|
||
* @dateTime: 2018-06-15 10:52:51
|
||
* @description: 封装socket连接(websocket)
|
||
* @copyright: Copyright(c) 2018 League_h5
|
||
* rfc6455#section-7.4
|
||
* close code: 1006,1005,1004
|
||
*/
|
||
|
||
// net error code
|
||
var ErrorCode = {
|
||
SUCC: 0,
|
||
// 成功
|
||
FAIL: 1,
|
||
// 失败
|
||
ERR_ALREADY_CONNECTED: 2,
|
||
// 已经连接
|
||
FAIL_NOT_SUPPORT: 3 // 不支持
|
||
};
|
||
|
||
// net status(ws)
|
||
var NetStatus = {
|
||
UNKNOWN: 0,
|
||
// 未知,尚未连接,或者已经断开
|
||
CONNECTING: 1,
|
||
// 连接中
|
||
CONNECTED: 2 // 已经连接
|
||
};
|
||
|
||
// for connect pool
|
||
var this_connect_pool = [];
|
||
|
||
// for WebSocket
|
||
var WebSocketClass = null;
|
||
|
||
/*
|
||
@method: check_time_num10(i)
|
||
@desc: 检测时间数字,小于10前面加0
|
||
@return: [string]
|
||
*/
|
||
function check_time_num10(i) {
|
||
if (i < 10) {
|
||
i = "0" + i;
|
||
}
|
||
return i;
|
||
}
|
||
|
||
/*
|
||
@method: get_str_curr_time()
|
||
@desc: 当前时间,格式:11:04:26
|
||
@return: [string]
|
||
*/
|
||
function get_str_curr_time() {
|
||
var today = new Date();
|
||
return check_time_num10(today.getHours()) + ":" + check_time_num10(today.getMinutes()) + ":" + check_time_num10(today.getSeconds());
|
||
}
|
||
|
||
/*
|
||
@method: get_str_curr_datetime()
|
||
@desc: 当前日期时间,格式:2022-03-01 11:04:26
|
||
@return: [string]
|
||
*/
|
||
function get_str_curr_datetime() {
|
||
var today = new Date();
|
||
return today.getFullYear() + "-" + check_time_num10(today.getMonth() + 1) + "-" + check_time_num10(today.getDate()) + " " + check_time_num10(today.getHours()) + ":" + check_time_num10(today.getMinutes()) + ":" + check_time_num10(today.getSeconds());
|
||
}
|
||
|
||
/*
|
||
@interface: SocketConnect
|
||
@desc: for socket connect
|
||
@param: [boolean]is_ssl 是否启用ssl加密
|
||
*/
|
||
function SocketConnect(is_ssl) {
|
||
this.is_ssl = !!is_ssl;
|
||
|
||
//是否重连
|
||
this.ip = '127.0.0.1';
|
||
this.port = 0;
|
||
this.ws = null;
|
||
this.status = NetStatus.UNKNOWN;
|
||
|
||
// 通知事件
|
||
this.call_self = null;
|
||
this.fn_on_open = null;
|
||
this.fn_on_message = null;
|
||
this.fn_on_close = null;
|
||
this.fn_on_error = null;
|
||
|
||
// 消息处理函数
|
||
this.fn_on_receive = null;
|
||
this.fn_on_send = null;
|
||
|
||
// 队列
|
||
this.send_msg_queue = [];
|
||
|
||
// 心跳
|
||
this.heart_beat_handle = null;
|
||
this.fn_heart_beat_ms = 0;
|
||
this.fn_heart_beat_msg = null;
|
||
}
|
||
|
||
/*
|
||
@method: SetSSL(is_ssl=false)
|
||
@desc: 设置ssl
|
||
@param: [boolean]is_ssl 是否启用ssl加密
|
||
@return: [boolean]
|
||
*/
|
||
SocketConnect.prototype.SetSSL = function (is_ssl) {
|
||
if (is_ssl == null) {
|
||
is_ssl = false;
|
||
}
|
||
this.is_ssl = is_ssl;
|
||
return true;
|
||
};
|
||
/*
|
||
@method: GetStatus()
|
||
@desc: 返回状态
|
||
@return: [boolean]
|
||
*/
|
||
SocketConnect.prototype.GetStatus = function () {
|
||
return this.status;
|
||
};
|
||
|
||
/*
|
||
@method: CanSendMsg()
|
||
@desc: 是否可发送消息,连接中或者重连也开放(开启发送池)
|
||
@return: [boolean]
|
||
*/
|
||
SocketConnect.prototype.CanSendMsg = function () {
|
||
if (this.status == NetStatus.CONNECTED || this.status == NetStatus.CONNECTING) {
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|
||
/*
|
||
@method: GetIp()
|
||
@desc: 返回: ip
|
||
@return: [string]
|
||
*/
|
||
SocketConnect.prototype.GetIp = function () {
|
||
return this.ip;
|
||
};
|
||
|
||
/*
|
||
@method: GetPort()
|
||
@desc: 返回: port
|
||
@return: [int]
|
||
*/
|
||
SocketConnect.prototype.GetPort = function () {
|
||
return this.port;
|
||
};
|
||
|
||
/*
|
||
@method: Init(call_self,fn_on_open,fn_on_message,fn_on_close,fn_on_error)
|
||
@desc: 初始化,附加处理函数
|
||
@param: [object]call_self 操作句柄
|
||
@param: [fn]fn_on_open on open,原型: function(self,is_reconnect,err)
|
||
@param: [fn]fn_on_message on message,原型:function(self,object,data)
|
||
@param: [fn]fn_on_close on close,原型:function(self,need_reconnect,err)
|
||
@param: [fn]fn_on_error on error,原型:function(self,err)
|
||
@return: [boolean]
|
||
*/
|
||
SocketConnect.prototype.Init = function (call_self, fn_on_open, fn_on_message, fn_on_close, fn_on_error) {
|
||
this.call_self = call_self;
|
||
this.fn_on_open = fn_on_open;
|
||
this.fn_on_message = fn_on_message;
|
||
this.fn_on_close = fn_on_close;
|
||
this.fn_on_error = fn_on_error;
|
||
return true;
|
||
};
|
||
|
||
/*
|
||
@method: AttachMessage(fn_on_receive=null,fn_on_send=null)
|
||
@desc: 附加消息处理函数
|
||
@param: [fn]fn_on_receive on receive,原型: function(data,function(is_success,data,dst_data){})
|
||
@param: [fn]fn_on_send on send,原型:function(data,function(is_success,dst_data){})
|
||
@return: [boolean]
|
||
*/
|
||
SocketConnect.prototype.AttachMessage = function (fn_on_receive, fn_on_send) {
|
||
this.fn_on_receive = fn_on_receive;
|
||
this.fn_on_send = fn_on_send;
|
||
return true;
|
||
};
|
||
|
||
/*
|
||
@method: AttachBeartbeat(fn_heart_beat_ms=0,fn_heart_beat_msg=null)
|
||
@desc: 启用服务器客户端心跳
|
||
@param: [int]fn_heart_beat_ms 心跳时间,单位ms,0表示不启用
|
||
@param: [fn]fn_heart_beat_msg on send,原型:function(call_self)
|
||
@return: [boolean]
|
||
*/
|
||
SocketConnect.prototype.AttachBeartbeat = function (fn_heart_beat_ms, fn_heart_beat_msg) {
|
||
if (fn_heart_beat_ms == null) {
|
||
fn_heart_beat_ms = 0;
|
||
}
|
||
this.fn_heart_beat_ms = fn_heart_beat_ms;
|
||
this.fn_heart_beat_msg = fn_heart_beat_msg;
|
||
if (this.fn_heart_beat_msg == null) {
|
||
this.fn_heart_beat_ms = 0;
|
||
}
|
||
return true;
|
||
};
|
||
|
||
/*
|
||
@method: Release()
|
||
@desc: 释放
|
||
@return: [boolean]
|
||
*/
|
||
SocketConnect.prototype.Release = function () {
|
||
this.ReleseHeart_Beat();
|
||
this.fn_heart_beat_ms = 0;
|
||
this.fn_heart_beat_msg = null;
|
||
this.status = NetStatus.UNKNOWN;
|
||
this.ws = null;
|
||
this.fn_on_open = null;
|
||
this.fn_on_message = null;
|
||
this.fn_on_close = null;
|
||
this.fn_on_error = null;
|
||
this.fn_on_receive = null;
|
||
this.fn_on_send = null;
|
||
this.send_msg_queue = [];
|
||
return true;
|
||
};
|
||
|
||
/*
|
||
@method: ReleseHeart_Beat()
|
||
@desc: 释放心跳
|
||
@return: [boolean]
|
||
*/
|
||
SocketConnect.prototype.ReleseHeart_Beat = function () {
|
||
if (this.heart_beat_handle != null) {
|
||
clearTimeout(this.heart_beat_handle);
|
||
this.heart_beat_handle = null;
|
||
}
|
||
return true;
|
||
};
|
||
|
||
/*
|
||
@method: StartHeart_Beat()
|
||
@desc: 启动心跳
|
||
@return: [boolean]
|
||
*/
|
||
SocketConnect.prototype.StartHeart_Beat = function () {
|
||
this.ReleseHeart_Beat();
|
||
var self = this;
|
||
self.heart_beat_handle = setTimeout(function () {
|
||
self.CallHeart_Beat(self);
|
||
}, self.fn_heart_beat_ms);
|
||
return true;
|
||
};
|
||
|
||
/*
|
||
@method: CallHeart_Beat(self)
|
||
@desc: 回调心跳
|
||
@return: [boolean]
|
||
*/
|
||
SocketConnect.prototype.CallHeart_Beat = function (self) {
|
||
self.heart_beat_handle = null;
|
||
var p_msg = self.fn_heart_beat_msg(self.call_self);
|
||
if (p_msg == null) {
|
||
return false;
|
||
}
|
||
var ret = self.Send(p_msg);
|
||
if (!ret) {
|
||
return false;
|
||
}
|
||
self.heart_beat_handle = setTimeout(function () {
|
||
self.CallHeart_Beat(self);
|
||
}, self.fn_heart_beat_ms);
|
||
return true;
|
||
};
|
||
|
||
/*
|
||
@method: Connect(ip=null,port=null)
|
||
@desc: 连接服务器
|
||
@param: [string]ip
|
||
@param: [int]port
|
||
@return: [int] ErrorCode.SUCC;
|
||
*/
|
||
SocketConnect.prototype.Connect = function (ip, port) {
|
||
if (ip == null || port == null) {
|
||
return ErrorCode.FAIL;
|
||
}
|
||
|
||
// 重复连接
|
||
if (this.status == NetStatus.CONNECTED && this.ip == ip && this.port == port) {
|
||
console.log('[ws_connect] 重复连接,忽略本次操作, ip=%s,port=%d', ip, port);
|
||
return ErrorCode.ERR_ALREADY_CONNECTED;
|
||
}
|
||
var self = this;
|
||
self.ws = null;
|
||
self.DisConnect();
|
||
self.ip = ip;
|
||
self.port = parseInt(port);
|
||
if (self.port <= 0) {
|
||
console.log('[ws_connect] invalid, ip=%s,port=%d', ip, port);
|
||
return ErrorCode.FAIL;
|
||
}
|
||
|
||
// 开始连接
|
||
self.status = NetStatus.CONNECTING;
|
||
var remote_url = "";
|
||
if (self.is_ssl) {
|
||
// remote_url = "wss://"+self.ip+":"+self.port+ "/websocket";//旧版本
|
||
remote_url = "wss://" + self.ip + ":443/wss" + self.port; //新版本
|
||
} else {
|
||
remote_url = "ws://" + self.ip + ":" + self.port + "/websocket";
|
||
}
|
||
if (WebSocketClass == null) {
|
||
console.log('[ws_connect] not support, ip=%s,port=%d', ip, port);
|
||
self.status = NetStatus.UNKNOWN;
|
||
return ErrorCode.FAIL_NOT_SUPPORT;
|
||
}
|
||
self.ws = new WebSocketClass(remote_url);
|
||
if (self.ws == null) {
|
||
console.log('[ws_connect] failure, ip=%s,port=%d', ip, port);
|
||
self.status = NetStatus.UNKNOWN;
|
||
return ErrorCode.FAIL;
|
||
}
|
||
self.ws.binaryType = "arraybuffer";
|
||
|
||
// 连接成功
|
||
self.ws.onopen = function (e) {
|
||
self.msg_queue = [];
|
||
if (self.queue_ticker) {
|
||
clearInterval(self.queue_ticker);
|
||
self.queue_ticker = null;
|
||
}
|
||
self.status = NetStatus.CONNECTED;
|
||
if (self.fn_on_open != null) {
|
||
self.fn_on_open(self.call_self, false, e);
|
||
}
|
||
|
||
//启动心跳
|
||
if (self.fn_heart_beat_ms > 0) {
|
||
self.StartHeart_Beat();
|
||
}
|
||
// //延迟发送
|
||
self.Try_Delay();
|
||
};
|
||
|
||
// 消息接收
|
||
self.ws.onmessage = function (e) {
|
||
if (!self.fn_on_message) {
|
||
return;
|
||
}
|
||
if (!self.fn_on_receive) {
|
||
self.fn_on_message(self.call_self, e.data, e.data);
|
||
return;
|
||
}
|
||
self.fn_on_receive(e.data, function (is_success, s_data, p_data) {
|
||
if (!is_success) {
|
||
console.error("[%s][recv] is failure(data)", get_str_curr_time());
|
||
return false;
|
||
}
|
||
self.fn_on_message(self.call_self, p_data, s_data);
|
||
return true;
|
||
});
|
||
};
|
||
|
||
// 关闭
|
||
self.ws.onclose = function (e) {
|
||
self.ws = null;
|
||
self.status = NetStatus.UNKNOWN;
|
||
self.msg_queue = [];
|
||
if (self.queue_ticker) {
|
||
clearInterval(self.queue_ticker);
|
||
self.queue_ticker = null;
|
||
}
|
||
if (self.fn_on_close) {
|
||
self.fn_on_close(self.call_self, false, e);
|
||
}
|
||
return true;
|
||
};
|
||
|
||
// 错误
|
||
self.ws.onerror = function (e) {
|
||
self.DisConnect();
|
||
if (self.fn_on_error) {
|
||
self.fn_on_error(self.call_self, e);
|
||
}
|
||
};
|
||
console.log('[ws_connect] error, ip=%s,port=%d', ip, port, ErrorCode.SUCC);
|
||
return ErrorCode.SUCC;
|
||
};
|
||
/*
|
||
@method: ReConnect()
|
||
@desc: 重新连接
|
||
@return: [int] ErrorCode.SUCC;
|
||
*/
|
||
SocketConnect.prototype.ReConnect = function () {
|
||
if (this.port <= 0) {
|
||
console.log('[ws_reconnect] failure, ip=%s,port=%d', this.ip, this.port);
|
||
return ErrorCode.FAIL;
|
||
}
|
||
return this.Connect(this.ip, this.port);
|
||
};
|
||
|
||
/*
|
||
@method: DisConnect()
|
||
@desc: 断开连接
|
||
@return: [boolean]
|
||
*/
|
||
SocketConnect.prototype.DisConnect = function () {
|
||
this.status = NetStatus.UNKNOWN;
|
||
this.send_msg_queue = [];
|
||
this.ReleseHeart_Beat();
|
||
if (this.ws != null) {
|
||
this.ws.close();
|
||
this.ws = null;
|
||
}
|
||
return true;
|
||
};
|
||
|
||
/*
|
||
@method: Send(data)
|
||
@desc: 发送消息
|
||
@param: [var]data 发送报文
|
||
@return: [boolean]
|
||
*/
|
||
SocketConnect.prototype.Send = function (data) {
|
||
var _this = this;
|
||
if (data == null || this.status != NetStatus.CONNECTED) {
|
||
console.warn("$Socket:发送失败..a");
|
||
return false;
|
||
}
|
||
if (!this.ws || this.ws.readyState != WebSocketClass.OPEN) {
|
||
console.warn("$Socket:发送失败..b");
|
||
return false;
|
||
}
|
||
this.msg_queue.push(data);
|
||
if (!this.queue_ticker) {
|
||
this.queue_ticker = setInterval(function () {
|
||
var data = _this.msg_queue.shift();
|
||
if (_this.ws && data) {
|
||
_this.ws.send(data);
|
||
}
|
||
}, 20);
|
||
}
|
||
// this.Try_Send( data );
|
||
|
||
return true;
|
||
};
|
||
// SocketConnect.prototype.Send = function( data ) {
|
||
|
||
// if( data == null ) {
|
||
// return false;
|
||
// }
|
||
|
||
// if( this.status != NetStatus.CONNECTED ) {
|
||
// if( this.status == NetStatus.CONNECTING ) {
|
||
// this.Send_Delay( data );
|
||
// return true;
|
||
// }
|
||
// return false;
|
||
// }
|
||
|
||
// if( this.ws == null ) {
|
||
// this.Send_Delay( data );
|
||
// this.DisConnect();
|
||
// return true;
|
||
// }
|
||
|
||
// if( this.ws.readyState === WebSocketClass.OPEN ) {
|
||
// this.Try_Send( data );
|
||
// }
|
||
// else {
|
||
// this.Send_Delay( data );
|
||
// // this.DisConnect();
|
||
// }
|
||
// return true;
|
||
// };
|
||
|
||
//丢弃处理,否则造成内存膨胀,导致内存溢出
|
||
SocketConnect.prototype.Send_Delay = function (data) {
|
||
if (this.send_msg_queue.length >= 10000) {
|
||
var p_data = this.send_msg_queue.pop();
|
||
p_data = null;
|
||
}
|
||
this.send_msg_queue.unshift(data);
|
||
console.log("[%s][send_delay] size =%d", this.send_msg_queue.length);
|
||
return true;
|
||
};
|
||
SocketConnect.prototype.Try_Send = function (data) {
|
||
if (this.send_msg_queue.length <= 0) {
|
||
if (data != null) {
|
||
this.Try_One_Send(data);
|
||
}
|
||
return true;
|
||
}
|
||
var p_data = null;
|
||
if (data != null) {
|
||
this.send_msg_queue.unshift(data);
|
||
}
|
||
for (var i = 0; i < this.send_msg_queue.length; i++) {
|
||
p_data = this.send_msg_queue.pop();
|
||
if (p_data == null) {
|
||
continue;
|
||
}
|
||
this.Try_One_Send(p_data);
|
||
}
|
||
return true;
|
||
};
|
||
SocketConnect.prototype.Try_One_Send = function (data) {
|
||
if (this.fn_on_send == null) {
|
||
if (this.ws != null) {
|
||
// console.log("[%s][send][0] is succ(data) fn_on_send 1",get_str_curr_time());
|
||
this.ws.send(data);
|
||
} else {
|
||
// console.error("[%s][send][0] is failure(data)",get_str_curr_time());
|
||
}
|
||
} else {
|
||
var self = this;
|
||
self.fn_on_send(data, function (is_success, p_data) {
|
||
if (is_success) {
|
||
if (self.ws != null) {
|
||
// console.log("[%s][send][1] is succ(data) fn_on_send 2",get_str_curr_time());
|
||
self.ws.send(p_data);
|
||
}
|
||
} else {
|
||
// console.error("[%s][send][1] is failure(data)",get_str_curr_time());
|
||
}
|
||
});
|
||
}
|
||
data = null;
|
||
return true;
|
||
};
|
||
SocketConnect.prototype.Try_Delay = function () {
|
||
// return true;
|
||
if (this.send_msg_queue.length <= 0) {
|
||
return false;
|
||
}
|
||
// if (this.status != NetStatus.CONNECTED)
|
||
// {
|
||
// return false;
|
||
// }
|
||
if (this.ws == null) {
|
||
return false;
|
||
}
|
||
if (this.ws.readyState === WebSocketClass.OPEN) {
|
||
this.Try_Send(null);
|
||
return true;
|
||
}
|
||
//延迟发送
|
||
// var self = this;
|
||
// self.Try_Finish(self);
|
||
// setTimeout(function(){
|
||
// if (self.ws.readyState === WebSocketClass.OPEN)
|
||
// {
|
||
// self.Try_Send(null);
|
||
// }
|
||
// return true;
|
||
// },30);
|
||
return true;
|
||
};
|
||
SocketConnect.prototype.Try_Finish = function (self) {
|
||
var _this2 = this;
|
||
if (self.send_msg_queue.length <= 0) return false;
|
||
if (self.ws.readyState == WebSocketClass.OPEN) {
|
||
self.Try_Send(null);
|
||
return true;
|
||
} else {
|
||
setTimeout(function () {
|
||
_this2.Try_Finish(self);
|
||
}, 50);
|
||
}
|
||
};
|
||
|
||
/*
|
||
@interface: SocketService
|
||
@desc: for Socket服务
|
||
*/
|
||
var SocketService = {};
|
||
// module.exports 适配js 和 nodejs
|
||
if (typeof module != "undefined") {
|
||
SocketService = module.exports;
|
||
}
|
||
|
||
/*
|
||
@method: InitWebSocket(websocket_class=null)
|
||
@desc: 初始化websocket,兼容chrome,firefox,cocos
|
||
@param: [object]websocket_class 句柄
|
||
@return: [boolean]
|
||
*/
|
||
SocketService.InitWebSocket = function (websocket_class) {
|
||
//导入第三方websocket
|
||
if (websocket_class != null) {
|
||
WebSocketClass = websocket_class;
|
||
} else {
|
||
//自动适配浏览器websocket(chrome,firefox)
|
||
if (typeof window != "undefined") {
|
||
if (typeof window.WebSocket == "undefined") {
|
||
WebSocketClass = window.MozWebSocket;
|
||
} else {
|
||
WebSocketClass = window.WebSocket;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
/*
|
||
@method: CreateConnect(is_ssl=false)
|
||
@desc: 创建连接
|
||
@param: [boolean]is_ssl 是否为ssl
|
||
@return: [boolean]
|
||
*/
|
||
SocketService.CreateConnect = function (is_ssl) {
|
||
var p_connect = new SocketConnect(!!is_ssl);
|
||
if (p_connect) {
|
||
this_connect_pool.push(p_connect);
|
||
}
|
||
return p_connect;
|
||
};
|
||
|
||
/*
|
||
@method: Release()
|
||
@desc: 系统退出,关闭所有连接
|
||
@return: [boolean]
|
||
*/
|
||
SocketService.Release = function () {
|
||
var p_connect = null;
|
||
for (var i = 0; i < this_connect_pool.length; i++) {
|
||
p_connect = this_connect_pool[i];
|
||
p_connect.Release();
|
||
}
|
||
this_connect_pool = [];
|
||
return true;
|
||
};
|
||
/*
|
||
@method: ErrCode()
|
||
@desc: 返回错误码定义
|
||
@return: [object]
|
||
*/
|
||
SocketService.ErrCode = function () {
|
||
return ErrorCode;
|
||
};
|
||
|
||
cc._RF.pop(); |