mirror of
https://gitee.com/dgflash/oops-plugin-framework.git
synced 2026-05-08 03:16:49 +08:00
491 lines
18 KiB
TypeScript
491 lines
18 KiB
TypeScript
import { error, warn } from "cc";
|
||
import { Logger } from "../../core/common/log/Logger";
|
||
import { CallbackObject, INetworkTips, IProtocolHelper, IRequestProtocol, ISocket, NetCallFunc, NetData, RequestObject } from "./NetInterface";
|
||
|
||
/*
|
||
* CocosCreator网络节点基类,以及网络相关接口定义
|
||
* 1. 网络连接、断开、请求发送、数据接收等基础功能
|
||
* 2. 心跳机制
|
||
* 3. 断线重连 + 请求重发
|
||
* 4. 调用网络屏蔽层
|
||
*/
|
||
|
||
type ExecuterFunc = (callback: CallbackObject, buffer: NetData) => void;
|
||
type CheckFunc = (checkedFunc: VoidFunc) => void;
|
||
type VoidFunc = () => void;
|
||
type BoolFunc = () => boolean;
|
||
|
||
var NetNodeStateStrs = ["已关闭", "连接中", "验证中", "可传输数据"];
|
||
|
||
/** 网络提示类型枚举 */
|
||
export enum NetTipsType {
|
||
Connecting,
|
||
ReConnecting,
|
||
Requesting,
|
||
}
|
||
|
||
/** 网络状态枚举 */
|
||
export enum NetNodeState {
|
||
Closed, // 已关闭
|
||
Connecting, // 连接中
|
||
Checking, // 验证中
|
||
Working, // 可传输数据
|
||
}
|
||
|
||
/** 网络连接参数 */
|
||
export interface NetConnectOptions {
|
||
host?: string, // 地址
|
||
port?: number, // 端口
|
||
url?: string, // url,与地址+端口二选一
|
||
autoReconnect?: number, // -1 永久重连,0不自动重连,其他正整数为自动重试次数
|
||
}
|
||
|
||
/** 网络节点 */
|
||
export class NetNode {
|
||
protected _connectOptions: NetConnectOptions | null = null;
|
||
protected _autoReconnect: number = 0;
|
||
protected _isSocketInit: boolean = false; // Socket是否初始化过
|
||
protected _isSocketOpen: boolean = false; // Socket是否连接成功过
|
||
protected _state: NetNodeState = NetNodeState.Closed; // 节点当前状态
|
||
protected _socket: ISocket | null = null; // Socket对象(可能是原生socket、websocket、wx.socket...)
|
||
|
||
protected _networkTips: INetworkTips | null = null; // 网络提示ui对象(请求提示、断线重连提示等)
|
||
protected _protocolHelper: IProtocolHelper | null = null; // 包解析对象
|
||
protected _connectedCallback: CheckFunc | null = null; // 连接完成回调
|
||
protected _disconnectCallback: BoolFunc | null = null; // 断线回调
|
||
protected _callbackExecuter: ExecuterFunc | null = null; // 回调执行
|
||
|
||
protected _keepAliveTimer: any = null; // 心跳定时器
|
||
protected _receiveMsgTimer: any = null; // 接收数据定时器
|
||
protected _reconnectTimer: any = null; // 重连定时器
|
||
protected _heartTime: number = 10000; // 心跳间隔
|
||
protected _receiveTime: number = 6000000; // 多久没收到数据断开
|
||
protected _reconnetTimeOut: number = 8000000; // 重连间隔
|
||
protected _requests: RequestObject[] = Array<RequestObject>(); // 请求列表
|
||
protected _listener: { [key: string]: CallbackObject[] | null } = {} // 监听者列表
|
||
|
||
/********************** 网络相关处理 *********************/
|
||
init(socket: ISocket, protocol: IProtocolHelper, networkTips: INetworkTips | null = null, execFunc: ExecuterFunc | null = null) {
|
||
Logger.instance.logNet(`网络初始化`);
|
||
this._socket = socket;
|
||
this._protocolHelper = protocol;
|
||
this._networkTips = networkTips;
|
||
this._callbackExecuter = execFunc ? execFunc : (callback: CallbackObject, buffer: NetData) => {
|
||
callback.callback.call(callback.target, buffer);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 请求连接服务器
|
||
* @param options 连接参数
|
||
*/
|
||
connect(options: NetConnectOptions): boolean {
|
||
if (this._socket && this._state == NetNodeState.Closed) {
|
||
if (!this._isSocketInit) {
|
||
this.initSocket();
|
||
}
|
||
this._state = NetNodeState.Connecting;
|
||
if (!this._socket.connect(options)) {
|
||
this.updateNetTips(NetTipsType.Connecting, false);
|
||
return false;
|
||
}
|
||
if (this._connectOptions == null && typeof options.autoReconnect == "number") {
|
||
this._autoReconnect = options.autoReconnect;
|
||
}
|
||
this._connectOptions = options;
|
||
this.updateNetTips(NetTipsType.Connecting, true);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
protected initSocket() {
|
||
if (this._socket) {
|
||
this._socket.onConnected = (event) => { this.onConnected(event) };
|
||
this._socket.onMessage = (msg) => { this.onMessage(msg) };
|
||
this._socket.onError = (event) => { this.onError(event) };
|
||
this._socket.onClosed = (event) => { this.onClosed(event) };
|
||
this._isSocketInit = true;
|
||
}
|
||
}
|
||
|
||
protected updateNetTips(tipsType: NetTipsType, isShow: boolean) {
|
||
if (this._networkTips) {
|
||
if (tipsType == NetTipsType.Requesting) {
|
||
this._networkTips.requestTips(isShow);
|
||
}
|
||
else if (tipsType == NetTipsType.Connecting) {
|
||
this._networkTips.connectTips(isShow);
|
||
}
|
||
else if (tipsType == NetTipsType.ReConnecting) {
|
||
this._networkTips.reconnectTips(isShow);
|
||
}
|
||
}
|
||
}
|
||
|
||
/** 网络连接成功 */
|
||
protected onConnected(event: any) {
|
||
Logger.instance.logNet("网络已连接")
|
||
this._isSocketOpen = true;
|
||
// 如果设置了鉴权回调,在连接完成后进入鉴权阶段,等待鉴权结束
|
||
if (this._connectedCallback !== null) {
|
||
this._state = NetNodeState.Checking;
|
||
this._connectedCallback(() => { this.onChecked() });
|
||
}
|
||
else {
|
||
this.onChecked();
|
||
}
|
||
Logger.instance.logNet(`网络已连接当前状态为【${NetNodeStateStrs[this._state]}】`);
|
||
}
|
||
|
||
/** 连接验证成功,进入工作状态 */
|
||
protected onChecked() {
|
||
Logger.instance.logNet("连接验证成功,进入工作状态");
|
||
this._state = NetNodeState.Working;
|
||
// 关闭连接或重连中的状态显示
|
||
this.updateNetTips(NetTipsType.Connecting, false);
|
||
this.updateNetTips(NetTipsType.ReConnecting, false);
|
||
|
||
// 重发待发送信息
|
||
var requests = this._requests.concat();
|
||
if (requests.length > 0) {
|
||
Logger.instance.logNet(`请求【${this._requests.length}】个待发送的信息`);
|
||
|
||
for (var i = 0; i < requests.length;) {
|
||
let req = requests[i];
|
||
this._socket!.send(req.buffer);
|
||
if (req.rspObject == null || req.rspCmd != "") {
|
||
requests.splice(i, 1);
|
||
}
|
||
else {
|
||
++i;
|
||
}
|
||
}
|
||
// 如果还有等待返回的请求,启动网络请求层
|
||
this.updateNetTips(NetTipsType.Requesting, this._requests.length > 0);
|
||
}
|
||
}
|
||
|
||
/** 接收到一个完整的消息包 */
|
||
protected onMessage(msg: any): void {
|
||
// Logger.logNet(`接受消息状态为【${NetNodeStateStrs[this._state]}】`);
|
||
|
||
var json = JSON.parse(msg);
|
||
|
||
// 进行头部的校验(实际包长与头部长度是否匹配)
|
||
if (!this._protocolHelper!.checkResponsePackage(json)) {
|
||
error(`校验接受消息数据异常`);
|
||
return;
|
||
}
|
||
|
||
// 处理相应包数据
|
||
if (!this._protocolHelper!.handlerResponsePackage(json)) {
|
||
if (this._networkTips)
|
||
this._networkTips.responseErrorCode(json.code);
|
||
}
|
||
|
||
// 接受到数据,重新定时收数据计时器
|
||
this.resetReceiveMsgTimer();
|
||
// 重置心跳包发送器
|
||
this.resetHearbeatTimer();
|
||
// 触发消息执行
|
||
let rspCmd = this._protocolHelper!.getPackageId(json);
|
||
|
||
Logger.instance.logNet(`接受到命令【${rspCmd}】的消息`);
|
||
// 优先触发request队列
|
||
if (this._requests.length > 0) {
|
||
for (let reqIdx in this._requests) {
|
||
let req = this._requests[reqIdx];
|
||
if (req.rspCmd == rspCmd && req.rspObject) {
|
||
Logger.instance.logNet(`触发请求命令【${rspCmd}】的回调`);
|
||
this._callbackExecuter!(req.rspObject, json.data);
|
||
this._requests.splice(parseInt(reqIdx), 1);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (this._requests.length == 0) {
|
||
this.updateNetTips(NetTipsType.Requesting, false);
|
||
}
|
||
else {
|
||
Logger.instance.logNet(`请求队列中还有【${this._requests.length}】个请求在等待`);
|
||
}
|
||
}
|
||
|
||
let listeners = this._listener[rspCmd];
|
||
if (null != listeners) {
|
||
for (const rsp of listeners) {
|
||
Logger.instance.logNet(`触发监听命令【${rspCmd}】的回调`);
|
||
this._callbackExecuter!(rsp, json.data);
|
||
}
|
||
}
|
||
}
|
||
|
||
protected onError(event: any) {
|
||
error(event);
|
||
}
|
||
|
||
protected onClosed(event: any) {
|
||
this.clearTimer();
|
||
|
||
// 执行断线回调,返回false表示不进行重连
|
||
if (this._disconnectCallback && !this._disconnectCallback()) {
|
||
Logger.instance.logNet(`断开连接`);
|
||
return;
|
||
}
|
||
|
||
// 自动重连
|
||
if (this.isAutoReconnect()) {
|
||
this.updateNetTips(NetTipsType.ReConnecting, true);
|
||
this._reconnectTimer = setTimeout(() => {
|
||
this._socket!.close();
|
||
this._state = NetNodeState.Closed;
|
||
this.connect(this._connectOptions!);
|
||
if (this._autoReconnect > 0) {
|
||
this._autoReconnect -= 1;
|
||
}
|
||
}, this._reconnetTimeOut);
|
||
}
|
||
else {
|
||
this._state = NetNodeState.Closed;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 断开网络
|
||
* @param code 关闭码
|
||
* @param reason 关闭原因
|
||
*/
|
||
close(code?: number, reason?: string) {
|
||
this.clearTimer();
|
||
this._listener = {};
|
||
this._requests.length = 0;
|
||
if (this._networkTips) {
|
||
this._networkTips.connectTips(false);
|
||
this._networkTips.reconnectTips(false);
|
||
this._networkTips.requestTips(false);
|
||
}
|
||
if (this._socket) {
|
||
this._socket.close(code, reason);
|
||
}
|
||
else {
|
||
this._state = NetNodeState.Closed;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 只是关闭Socket套接字(仍然重用缓存与当前状态)
|
||
* @param code 关闭码
|
||
* @param reason 关闭原因
|
||
*/
|
||
closeSocket(code?: number, reason?: string) {
|
||
if (this._socket) {
|
||
this._socket.close(code, reason);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发起请求,如果当前处于重连中,进入缓存列表等待重连完成后发送
|
||
* @param buf 网络数据
|
||
* @param force 是否强制发送
|
||
*/
|
||
send(buf: NetData, force: boolean = false): number {
|
||
if (this._state == NetNodeState.Working || force) {
|
||
return this._socket!.send(buf);
|
||
}
|
||
else if (this._state == NetNodeState.Checking ||
|
||
this._state == NetNodeState.Connecting) {
|
||
this._requests.push({
|
||
buffer: buf,
|
||
rspCmd: "",
|
||
rspObject: null
|
||
});
|
||
Logger.instance.logNet(`当前状态为【${NetNodeStateStrs[this._state]}】,繁忙并缓冲发送数据`);
|
||
return 0;
|
||
}
|
||
else {
|
||
error(`当前状态为【${NetNodeStateStrs[this._state]}】,请求错误`);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发起请求,并进入缓存列表
|
||
* @param reqProtocol 请求协议
|
||
* @param rspObject 回调对象
|
||
* @param showTips 是否触发请求提示
|
||
* @param force 是否强制发送
|
||
*/
|
||
request<T>(reqProtocol: IRequestProtocol, rspObject: CallbackObject, showTips: boolean = true, force: boolean = false) {
|
||
var rspCmd = this._protocolHelper!.handlerRequestPackage(reqProtocol);
|
||
this.base_request(reqProtocol, rspCmd, rspObject, showTips, force);
|
||
}
|
||
|
||
/**
|
||
* 唯一request,确保没有同一响应的请求(避免一个请求重复发送,netTips界面的屏蔽也是一个好的方法)
|
||
* @param reqProtocol 请求协议
|
||
* @param rspObject 回调对象
|
||
* @param showTips 是否触发请求提示
|
||
* @param force 是否强制发送
|
||
*/
|
||
requestUnique(reqProtocol: IRequestProtocol, rspObject: CallbackObject, showTips: boolean = true, force: boolean = false): boolean {
|
||
var rspCmd = this._protocolHelper!.handlerRequestPackage(reqProtocol);
|
||
|
||
for (let i = 0; i < this._requests.length; ++i) {
|
||
if (this._requests[i].rspCmd == rspCmd) {
|
||
Logger.instance.logNet(`命令【${rspCmd}】重复请求`);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
this.base_request(reqProtocol, rspCmd, rspObject, showTips, force);
|
||
return true;
|
||
}
|
||
|
||
private base_request(reqProtocol: IRequestProtocol, rspCmd: string, rspObject: CallbackObject, showTips: boolean = true, force: boolean = false) {
|
||
var buf: NetData = JSON.stringify(reqProtocol); // 转为二进制流发送
|
||
|
||
if (this._state == NetNodeState.Working || force) {
|
||
this._socket!.send(buf);
|
||
}
|
||
|
||
Logger.instance.logNet(`队列命令为【${rspCmd}】的请求,等待请求数据的回调`);
|
||
|
||
// 进入发送缓存列表
|
||
this._requests.push({
|
||
buffer: buf, rspCmd, rspObject
|
||
});
|
||
// 启动网络请求层
|
||
if (showTips) {
|
||
this.updateNetTips(NetTipsType.Requesting, true);
|
||
}
|
||
}
|
||
|
||
/********************** 回调相关处理 *********************/
|
||
/**
|
||
* 设置一个唯一的服务器推送监听
|
||
* @param cmd 命令字串
|
||
* @param callback 回调方法
|
||
* @param target 目标对象
|
||
*/
|
||
setResponeHandler(cmd: string, callback: NetCallFunc, target?: any): boolean {
|
||
if (callback == null) {
|
||
error(`命令为【${cmd}】设置响应处理程序错误`);
|
||
return false;
|
||
}
|
||
this._listener[cmd] = [{ target, callback }];
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 可添加多个同类返回消息的监听
|
||
* @param cmd 命令字串
|
||
* @param callback 回调方法
|
||
* @param target 目标对象
|
||
* @returns
|
||
*/
|
||
addResponeHandler(cmd: string, callback: NetCallFunc, target?: any): boolean {
|
||
if (callback == null) {
|
||
error(`命令为【${cmd}】添加响应处理程序错误`);
|
||
return false;
|
||
}
|
||
let rspObject = { target, callback };
|
||
if (null == this._listener[cmd]) {
|
||
this._listener[cmd] = [rspObject];
|
||
}
|
||
else {
|
||
let index = this.getNetListenersIndex(cmd, rspObject);
|
||
if (-1 == index) {
|
||
this._listener[cmd]!.push(rspObject);
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 删除一个监听中指定子回调
|
||
* @param cmd 命令字串
|
||
* @param callback 回调方法
|
||
* @param target 目标对象
|
||
*/
|
||
removeResponeHandler(cmd: string, callback: NetCallFunc, target?: any) {
|
||
if (null != this._listener[cmd] && callback != null) {
|
||
let index = this.getNetListenersIndex(cmd, { target, callback });
|
||
if (-1 != index) {
|
||
this._listener[cmd]!.splice(index, 1);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 清除所有监听或指定命令的监听
|
||
* @param cmd 命令字串(默认不填为清除所有)
|
||
*/
|
||
cleanListeners(cmd: string = "") {
|
||
if (cmd == "") {
|
||
this._listener = {}
|
||
}
|
||
else {
|
||
delete this._listener[cmd];
|
||
}
|
||
}
|
||
|
||
protected getNetListenersIndex(cmd: string, rspObject: CallbackObject): number {
|
||
let index = -1;
|
||
for (let i = 0; i < this._listener[cmd]!.length; i++) {
|
||
let iterator = this._listener[cmd]![i];
|
||
if (iterator.callback == rspObject.callback
|
||
&& iterator.target == rspObject.target) {
|
||
index = i;
|
||
break;
|
||
}
|
||
}
|
||
return index;
|
||
}
|
||
|
||
/********************** 心跳、超时相关处理 *********************/
|
||
protected resetReceiveMsgTimer() {
|
||
if (this._receiveMsgTimer !== null) {
|
||
clearTimeout(this._receiveMsgTimer);
|
||
}
|
||
|
||
this._receiveMsgTimer = setTimeout(() => {
|
||
warn("接收消息定时器关闭网络连接");
|
||
this._socket!.close();
|
||
}, this._receiveTime);
|
||
}
|
||
|
||
protected resetHearbeatTimer() {
|
||
if (this._keepAliveTimer !== null) {
|
||
clearTimeout(this._keepAliveTimer);
|
||
}
|
||
|
||
this._keepAliveTimer = setTimeout(() => {
|
||
Logger.instance.logNet("网络节点保持活跃发送心跳信息");
|
||
this.send(this._protocolHelper!.getHearbeat());
|
||
}, this._heartTime);
|
||
}
|
||
|
||
protected clearTimer() {
|
||
if (this._receiveMsgTimer !== null) {
|
||
clearTimeout(this._receiveMsgTimer);
|
||
}
|
||
if (this._keepAliveTimer !== null) {
|
||
clearTimeout(this._keepAliveTimer);
|
||
}
|
||
if (this._reconnectTimer !== null) {
|
||
clearTimeout(this._reconnectTimer);
|
||
}
|
||
}
|
||
|
||
/** 是否自动重连接 */
|
||
isAutoReconnect() {
|
||
return this._autoReconnect != 0;
|
||
}
|
||
|
||
/** 拒绝重新连接 */
|
||
rejectReconnect() {
|
||
this._autoReconnect = 0;
|
||
this.clearTimer();
|
||
}
|
||
} |