From 181e6237a0d9f7f0b3720dc95de2f758f4c30548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=9D=E7=88=B7?= Date: Mon, 7 Oct 2019 16:29:45 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BD=91=E7=BB=9C=E6=8E=A5=E5=8F=A3=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 73 ++++- assets/Script/common.meta | 7 + assets/Script/common/EventManager.ts | 109 +++++++ assets/Script/common/EventManager.ts.meta | 9 + assets/Script/common/TaskQueue.ts | 166 ++++++++++ assets/Script/common/TaskQueue.ts.meta | 9 + assets/Script/network.meta | 7 + assets/Script/network/NetInterface.ts | 41 +++ assets/Script/network/NetInterface.ts.meta | 9 + assets/Script/network/NetManager.ts | 9 + assets/Script/network/NetManager.ts.meta | 9 + assets/Script/network/NetNode.ts | 347 +++++++++++++++++++++ assets/Script/network/NetNode.ts.meta | 9 + assets/Script/network/WebSock.ts | 61 ++++ assets/Script/network/WebSock.ts.meta | 9 + 15 files changed, 868 insertions(+), 6 deletions(-) create mode 100644 assets/Script/common.meta create mode 100644 assets/Script/common/EventManager.ts create mode 100644 assets/Script/common/EventManager.ts.meta create mode 100644 assets/Script/common/TaskQueue.ts create mode 100644 assets/Script/common/TaskQueue.ts.meta create mode 100644 assets/Script/network.meta create mode 100644 assets/Script/network/NetInterface.ts create mode 100644 assets/Script/network/NetInterface.ts.meta create mode 100644 assets/Script/network/NetManager.ts create mode 100644 assets/Script/network/NetManager.ts.meta create mode 100644 assets/Script/network/NetNode.ts create mode 100644 assets/Script/network/NetNode.ts.meta create mode 100644 assets/Script/network/WebSock.ts create mode 100644 assets/Script/network/WebSock.ts.meta diff --git a/.gitignore b/.gitignore index dff4d77..1f645db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,67 @@ -/local -/temp -/library -/.vscode -/packages -.DS_Store \ No newline at end of file +#///////////////////////////////////////////////////////////////////////////// +# Cocos Creator Projects +#///////////////////////////////////////////////////////////////////////////// + +build/ +library/ +temp/ +local/ +jsb-default/ +jsb-binary/ +jsb-link/ +web-desktop/ +# web-mobile/ 发布网页版本使用 +runtime/ +build/ +jsb/ +export/ + +#///////////////////////////////////////////////////////////////////////////// +# Logs and databases +#///////////////////////////////////////////////////////////////////////////// + +*.log +*.sql +*.sqlite + +#///////////////////////////////////////////////////////////////////////////// +# files for debugger +#///////////////////////////////////////////////////////////////////////////// + +*.sln +*.csproj +*.pidb +*.unityproj +*.suo + +#///////////////////////////////////////////////////////////////////////////// +# OS generated files +#///////////////////////////////////////////////////////////////////////////// + +.DS_Store +ehthumbs.db +Thumbs.db + +#///////////////////////////////////////////////////////////////////////////// +# exvim files +#///////////////////////////////////////////////////////////////////////////// + +*UnityVS.meta +*.err +*.err.meta +*.exvim +*.exvim.meta +*.vimentry +*.vimentry.meta +*.vimproject +*.vimproject.meta +.vimfiles.*/ +.exvim.*/ +quick_gen_project_*_autogen.bat +quick_gen_project_*_autogen.bat.meta +quick_gen_project_*_autogen.sh +quick_gen_project_*_autogen.sh.meta +.exvim.app + +.idea/ +.vscode/ diff --git a/assets/Script/common.meta b/assets/Script/common.meta new file mode 100644 index 0000000..059323d --- /dev/null +++ b/assets/Script/common.meta @@ -0,0 +1,7 @@ +{ + "ver": "1.0.1", + "uuid": "b8f13390-ade4-44a3-8658-6ebf77febb27", + "isSubpackage": false, + "subpackageName": "", + "subMetas": {} +} \ No newline at end of file diff --git a/assets/Script/common/EventManager.ts b/assets/Script/common/EventManager.ts new file mode 100644 index 0000000..cc550b1 --- /dev/null +++ b/assets/Script/common/EventManager.ts @@ -0,0 +1,109 @@ +/* +* 事件管理器,事件的监听、触发、移除 +* +* 2018-9-20 by 宝爷 +*/ +export type EventManagerCallFunc = (eventName: string, eventData: any) => void; + +interface CallBackTarget { + callBack: EventManagerCallFunc, + target: any, +} + +export class EventManager { + private static instance: EventManager = null; + public static getInstance(): EventManager { + if (!this.instance) { + this.instance = new EventManager(); + } + return this.instance; + } + + public static destroy(): void { + if (this.instance) { + this.instance = null; + } + } + + private constructor() { + } + + private _eventListeners: { [key: string]: CallBackTarget[] } = {}; + + private getEventListenersIndex(eventName: string, callBack: EventManagerCallFunc, target?: any): number { + let index = -1; + for (let i = 0; i < this._eventListeners[eventName].length; i++) { + let iterator = this._eventListeners[eventName][i]; + if (iterator.callBack == callBack && (!target || iterator.target == target)) { + index = i; + break; + } + } + return index; + } + + addEventListener(eventName: string, callBack: EventManagerCallFunc, target?: any): boolean { + if (!eventName) { + cc.warn("eventName is empty" + eventName); + return; + } + + if (null == callBack) { + cc.log('addEventListener callBack is nil'); + return false; + } + let callTarget: CallBackTarget = { callBack: callBack, target: target }; + if (null == this._eventListeners[eventName]) { + this._eventListeners[eventName] = [callTarget]; + + } else { + let index = this.getEventListenersIndex(eventName, callBack, target); + if (-1 == index) { + this._eventListeners[eventName].push(callTarget); + } + } + + return true; + } + + setEventListener(eventName: string, callBack: EventManagerCallFunc, target?: any): boolean { + if (!eventName) { + cc.warn("eventName is empty" + eventName); + return; + } + + if (null == callBack) { + cc.log('setEventListener callBack is nil'); + return false; + } + let callTarget: CallBackTarget = { callBack: callBack, target: target }; + this._eventListeners[eventName] = [callTarget]; + return true; + } + + removeEventListener(eventName: string, callBack: EventManagerCallFunc, target?: any) { + if (null != this._eventListeners[eventName]) { + let index = this.getEventListenersIndex(eventName, callBack, target); + if (-1 != index) { + this._eventListeners[eventName].splice(index, 1); + } + } + } + + raiseEvent(eventName: string, eventData?: any) { + console.log(`==================== raiseEvent ${eventName} begin | ${JSON.stringify(eventData)}`); + if (null != this._eventListeners[eventName]) { + // 将所有回调提取出来,再调用,避免调用回调的时候操作了事件的删除 + let callbackList: CallBackTarget[] = []; + for (const iterator of this._eventListeners[eventName]) { + callbackList.push({ callBack: iterator.callBack, target: iterator.target }); + } + for (const iterator of callbackList) { + iterator.callBack.call(iterator.target, eventName, eventData); + } + } + console.log(`==================== raiseEvent ${eventName} end`); + } +} + +export let EventMgr = EventManager.getInstance(); \ No newline at end of file diff --git a/assets/Script/common/EventManager.ts.meta b/assets/Script/common/EventManager.ts.meta new file mode 100644 index 0000000..8ec130d --- /dev/null +++ b/assets/Script/common/EventManager.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.5", + "uuid": "d23b8dc4-fd4c-43e4-87db-4c5bc3bfa818", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/Script/common/TaskQueue.ts b/assets/Script/common/TaskQueue.ts new file mode 100644 index 0000000..fe4157f --- /dev/null +++ b/assets/Script/common/TaskQueue.ts @@ -0,0 +1,166 @@ +/* +* 任务队列管理器 +* 1. 传入任务,进入队列顺序执行 +* 2. 支持优先级(从小到大排序,priority越小优先级越高) +* 3. 支持队列Tag,允许多个互不影响的队列执行 +* 4. 支持清理任务队列 +* 5. 一个任务只能完成一次,避免代码的原因多次调用完成,导致后续任务提前执行 +* +* 6. 调试模式下记录了每个Task添加时的堆栈,方便调试(可以快速查看哪个任务没有结束) +* +* 2018-5-7 by 宝爷 +*/ + + +// 任务结束回调 +export type TaskFinishCallback = () => void; +// 任务执行回调 +export type TaskCallback = (TaskFinishCallback) => void; + +class TaskInfo { + public task: TaskCallback; + public priority: number; + public constructor(task: TaskCallback, priority: number) { + this.task = task; + this.priority = priority; + } +} + +export class TaskQueue { + private _curTask: TaskInfo = null; + private _taskQueue: TaskInfo[] = Array(); + + // 添加一个任务,如果当前没有任务在执行,该任务会立即执行,否则进入队列等待 + public pushTask(task: TaskCallback, priority: number = 0): void { + let taskInfo = new TaskInfo(task, priority); + if (this._taskQueue.length > 0) { + for (var i: number = this._taskQueue.length - 1; i >= 0; --i) { + if (this._taskQueue[i].priority <= priority) { + this._taskQueue.splice(i + 1, 0, taskInfo); + return; + } + } + } + // 插到头部 + this._taskQueue.splice(0, 0, taskInfo); + if (this._curTask == null) { + this.executeNextTask(); + } + } + + public clearTask(): void { + this._curTask = null; + this._taskQueue.length = 0; + } + + private executeNextTask(): void { + let taskInfo = this._taskQueue.shift() || null; + this._curTask = taskInfo; + if (taskInfo) { + taskInfo.task(() => { + if (taskInfo === this._curTask) { + this.executeNextTask(); + } else { + console.warn("your task finish twice!"); + } + }); + } + } +} + +export class TaskManager { + private static _instance: TaskManager = null; + private _taskQueues: { [key: number]: TaskQueue } = {} + + public static getInstance(): TaskManager { + if (!this._instance) { + this._instance = new TaskManager(); + } + return this._instance; + } + + public static destory(): void { + this._instance = null; + } + + private constructor() { + + } + + public pushTask(task: TaskCallback, priority: number = 0): void { + return this.getTaskQueue().pushTask(task, priority); + } + + public pushTaskByTag(task: TaskCallback, tag: number, priority: number = 0): void { + return this.getTaskQueue(tag).pushTask(task, priority); + } + + public clearTaskQueue(tag: number = 0): void { + let taskQueue = this._taskQueues[tag]; + if (taskQueue) { + taskQueue.clearTask(); + } + } + + public clearAllTaskQueue(): void { + for (let queue in this._taskQueues) { + this._taskQueues[queue].clearTask(); + } + this._taskQueues = {} + } + + private getTaskQueue(tag: number = 0): TaskQueue { + let taskQueue = this._taskQueues[tag]; + if (taskQueue == null) { + taskQueue = new TaskQueue(); + this._taskQueues[tag] = taskQueue; + } + return taskQueue; + } +} + +/* 测试用例: +* 1. 测试多个任务的执行顺序 + 优先级 +* 2. 测试在执行任务的过程中动态添加新任务 + +export function testQueue() { + let creatTask = (idx, pri): TaskCallback => { + return (finish) => { + console.log(`execute task ${idx} priority ${pri}`); + finish(); + }; + }; + let tag = 0; + let begin = (finish) => { + for (var i = 0; i < 100; ++i) { + let priority = 0; + if (i % 10 == 0) { + priority = -1; + } else if (i == 88) { + priority = 1; + } else if (i == 22) { + let task = creatTask(1.1, priority); + TaskManager.getInstance().pushTaskByTag(task, tag, priority); + task = creatTask(1.2, priority); + TaskManager.getInstance().pushTaskByTag(task, tag, priority); + task = creatTask(1.3, priority); + TaskManager.getInstance().pushTaskByTag(task, tag, priority); + } else if (i == 51 && tag == 2) { + // 清理之后,添加的任务会立即执行... + TaskManager.getInstance().clearTaskQueue(tag); + } + let task = creatTask(i, priority); + Object.defineProperty(task, "idx", { value: i }); + TaskManager.getInstance().pushTaskByTag(task, tag, priority); + } + console.log("add task finish, start test"); + finish(); + // 测试重复调用结束 + finish(); + // tag为2时的finish两次都会报警告,因为begin已经被清理了 + } + TaskManager.getInstance().pushTaskByTag(begin, tag); + tag = 2; + TaskManager.getInstance().pushTaskByTag(begin, tag); + +}*/ diff --git a/assets/Script/common/TaskQueue.ts.meta b/assets/Script/common/TaskQueue.ts.meta new file mode 100644 index 0000000..7a52cda --- /dev/null +++ b/assets/Script/common/TaskQueue.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.5", + "uuid": "4ed3ba86-aa0d-4b93-864b-adc0f8d58177", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/Script/network.meta b/assets/Script/network.meta new file mode 100644 index 0000000..709a715 --- /dev/null +++ b/assets/Script/network.meta @@ -0,0 +1,7 @@ +{ + "ver": "1.0.1", + "uuid": "7f2dddd0-7938-4352-802a-fa3d6aac09ec", + "isSubpackage": false, + "subpackageName": "", + "subMetas": {} +} \ No newline at end of file diff --git a/assets/Script/network/NetInterface.ts b/assets/Script/network/NetInterface.ts new file mode 100644 index 0000000..78f7a50 --- /dev/null +++ b/assets/Script/network/NetInterface.ts @@ -0,0 +1,41 @@ + +export type NetData = (string | ArrayBufferLike | Blob | ArrayBufferView); +export type NetCallFunc = (mainCmd: number, subCmd: number, data: any) => void; + +// 回调对象 +export interface CallbackObject { + target: any, // 回调对象,不为null时调用target.callback(xxx) + callback: NetCallFunc, // 回调函数 +} + +// 请求对象 +export interface RequestObject { + buffer: NetData, // 请求的Buffer + rspCmd: number, // 等待响应指令 + rspObject: CallbackObject, // 等待响应的回调对象 +} + +// 协议对象 +export interface IProtocolHelper { + getHeadlen(): number; // 返回包头长度 + getHearbeat(): NetData; // 返回一个心跳包 + checkHead(msg: NetData): boolean; // 检查数据头部是否合法 + checkCmd(msg: NetData): number; // 获取协议类型或id +} + +// Socket对象 +export interface ISocket { + onConnected: (event) => void; + onMessage: (msg: NetData) => void; + onError: (event) => void; + onClosed: (event) => void; + connect(options: any) : boolean; + send(buffer: NetData) : boolean; + close(code?: number, reason?: string); +} + +// 网络提示对象 +export interface INetworkTips { + reconnectTips(isShow : boolean): void; + requestTips(isShow : boolean): void; +} diff --git a/assets/Script/network/NetInterface.ts.meta b/assets/Script/network/NetInterface.ts.meta new file mode 100644 index 0000000..55f5833 --- /dev/null +++ b/assets/Script/network/NetInterface.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.5", + "uuid": "92a57531-69f5-4125-b17e-4fb69dedf652", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/Script/network/NetManager.ts b/assets/Script/network/NetManager.ts new file mode 100644 index 0000000..3cd55d0 --- /dev/null +++ b/assets/Script/network/NetManager.ts @@ -0,0 +1,9 @@ + +/* +* 网络节点管理类 +* +*/ + +export class NetManager { + +} \ No newline at end of file diff --git a/assets/Script/network/NetManager.ts.meta b/assets/Script/network/NetManager.ts.meta new file mode 100644 index 0000000..9a0d0db --- /dev/null +++ b/assets/Script/network/NetManager.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.5", + "uuid": "a83ab7c0-5761-4aac-b367-a108f45b61ad", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/Script/network/NetNode.ts b/assets/Script/network/NetNode.ts new file mode 100644 index 0000000..3bec928 --- /dev/null +++ b/assets/Script/network/NetNode.ts @@ -0,0 +1,347 @@ +import { ISocket, INetworkTips, IProtocolHelper, RequestObject, CallbackObject, NetData, NetCallFunc } from "./NetInterface"; + +/* +* CocosCreator网络节点基类,以及网络相关接口定义 +* 1. 网络连接、断开、请求发送、数据接收等基础功能 +* 2. 心跳机制 +* 3. 断线重连 + 请求重发 +* 4. 调用网络屏蔽层 +* +* 2018-5-7 by 宝爷 +*/ + +type ExecuterFunc = (callback: CallbackObject, buffer: NetData) => void; +type VoidFunc = () => void; +type BoolFunc = () => boolean; + +export enum NetNodeState { + Closed, // 已关闭 + Connecting, // 连接中 + Checking, // 验证中 + Working, // 可传输数据 +} + +export enum NetNodeConnectType { + Normal, + ReConnect, +} + +export class NetNode { + protected _isAutoReconnect: boolean = false; // 是否在网络断开之后自动重连 + protected _isSocketInit: boolean = false; // Socket是否初始化过 + protected _isSocketOpen: boolean = false; // Socket是否连接成功过 + protected _connType: NetNodeConnectType = NetNodeConnectType.Normal; // 连接类型 + protected _state: NetNodeState = NetNodeState.Closed; // 节点当前状态 + protected _socket: ISocket = null; // Socket对象(可能是原生socket、websocket、wx.socket...) + protected _host: string; // IP + protected _port: number; // 端口 + + protected _networkTips: INetworkTips = null; // 网络提示ui对象(请求提示、断线重连提示等) + protected _protocolHelper: IProtocolHelper = null; // 包解析对象 + protected _connectedCallback: VoidFunc = null; // 连接完成回调 + protected _disconnectCallback: BoolFunc = null; // 断线回调 + protected _callbackExecuter: ExecuterFunc = 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(); // 请求列表 + protected _listener: { [key: number]: CallbackObject[] } = {} // 监听者列表 + + /********************** 网络相关处理 *********************/ + public init(socket: ISocket, networkTips: any = null) { + console.log(`NetNode init socket`); + this._socket = socket; + this._networkTips = networkTips; + } + + public connect(host: string, port: number, auotReconnect: boolean = true, connType: NetNodeConnectType = NetNodeConnectType.Normal) { + if (this._socket && this._state == NetNodeState.Closed) { + if (!this._isSocketInit) { + this.initSocket(); + } + this._state = NetNodeState.Connecting; + this._isAutoReconnect = auotReconnect; + this._socket.connect({ip : host, port}); + this._connType = connType; + if (connType == NetNodeConnectType.Normal) { + this._host = host; + this._port = port; + } + console.log(`NetNode connect to ${host}:${port}`); + } else { + console.error(`NetNode connect error! should init socket! state ${this._state}`); + } + } + + protected initSocket() { + 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 onConnected(event) { + console.log("NetNode onConnected!") + this._isSocketOpen = true; + // 如果设置了 + if (this._connectedCallback !== null) { + this._state = NetNodeState.Checking; + this._connectedCallback(); + } else { + this.onChecked(); + } + console.log("NetNode onConnected! state ="+this._state); + } + + // 连接验证成功,进入工作状态 + protected onChecked() { + console.log("NetNode onChecked!") + this._state = NetNodeState.Working; + // 关闭重连中的状态显示 + if (this._networkTips !== null) { + this._networkTips.reconnectTips(false); + } + // 重发待发送信息 + console.log(`NetNode flush ${this._requests.length} request`) + if (this._requests.length > 0) { + for (var i = 0; i < this._requests.length;) { + let req = this._requests[i]; + this._socket.send(req.buffer); + if (req.rspObject == null || req.rspCmd <= 0) { + this._requests.splice(i, 1); + } else { + ++i; + } + } + // 如果还有等待返回的请求,启动网络请求层 + if (this._networkTips != null) { + if (this._requests.length > 0) { + console.log(`NetNode startRequestTips`) + this._networkTips.requestTips(true); + } else { + this._networkTips.requestTips(false); + } + } + } + } + + // 接收到一个完整的消息包 + protected onMessage(msg): void { + // console.log(`NetNode onMessage status = ` + this._state); + // 进行头部的校验(实际包长与头部长度是否匹配) + if (!this._protocolHelper.checkHead(msg)) { + console.error(`NetNode checkHead Error`); + return; + } + // 接受到数据,重新定时收数据计时器 + this.resetReceiveMsgTimer(); + // 重置心跳包发送器 + this.resetHearbeatTimer(); + // 触发消息执行 + let rspCmd = this._protocolHelper.checkCmd(msg); + console.log(`NetNode onMessage rspCmd = ` + rspCmd); + // 优先触发request队列 + if (this._requests.length > 0) { + for (let reqIdx in this._requests) { + let req = this._requests[reqIdx]; + if (req.rspCmd == rspCmd) { + console.log(`NetNode execute request rspcmd ${rspCmd}`); + this._callbackExecuter(req.rspObject, msg); + this._requests.splice(parseInt(reqIdx), 1); + break; + } + } + console.log(`NetNode still has ${this._requests.length} request watting`); + if (this._requests.length == 0 && this._networkTips) { + this._networkTips.requestTips(false); + } + } + + let listeners = this._listener[rspCmd]; + if (null != listeners) { + for (const rsp of listeners) { + console.log(`NetNode execute listener cmd ${rspCmd}`); + this._callbackExecuter(rsp, msg); + } + } + } + + protected onError(event) { + console.error(event); + } + + protected onClosed(event) { + this.clearTimer(); + + // 执行断线回调 + if (this._disconnectCallback && !this._disconnectCallback()) { + console.log(`disconnect return!`) + return; + } + + // 自动重连 + if (this._isAutoReconnect) { + if (this._networkTips) { + this._networkTips.reconnectTips(true); + } + this._reconnectTimer = setTimeout(() => { + this._socket.close(); + this._state = NetNodeState.Closed; + this.connect(this._host, this._port, this._isAutoReconnect, NetNodeConnectType.ReConnect); + }, this._reconnetTimeOut); + } else { + this._state = NetNodeState.Closed; + } + } + + public close(code?: number, reason?: string) { + this.clearTimer(); + this._listener = {}; + this._requests.length = 0; + if (this._networkTips) { + this._networkTips.reconnectTips(false); + this._networkTips.requestTips(false); + } + if (this._socket) { + this._socket.close(code, reason); + } else { + this._state = NetNodeState.Closed; + } + } + + // 只是关闭Socket套接字(仍然重用缓存与当前状态) + public closeSocket(code?: number, reason?: string) { + if (this._socket) { + this._socket.close(code, reason); + } + } + + // 发起请求,如果当前处于重连中,进入缓存列表等待重连完成后发送 + public send(buf: NetData, force: boolean = false) { + if (this._state == NetNodeState.Working || force) { + console.log(`socket send ...`); + this._socket.send(buf); + } else if (this._state == NetNodeState.Checking || + this._state == NetNodeState.Connecting) { + this._requests.push({ + buffer: buf, + rspCmd: 0, + rspObject: null + }); + console.log("NetNode socket is busy, push to send buffer, current state is " + this._state); + } else { + console.error("NetNode request error! current state is " + this._state); + } + } + + // 发起请求,并进入缓存列表, + public sendWithTimeout(buf: NetData, rspCmd: number, rspObject: CallbackObject, showTips: boolean = true, force: boolean = false) { + if (this._state == NetNodeState.Working || force) { + this._socket.send(buf); + } + console.log(`NetNode request with timeout for ${rspCmd}`); + // 进入发送缓存列表 + this._requests.push({ + buffer: buf, rspCmd, rspObject + }); + // 启动网络请求层 + if (this._networkTips !== null && showTips) { + this._networkTips.requestTips(true); + } + } + + /********************** 回调相关处理 *********************/ + public setResponeHandler(cmd: number, callback: NetCallFunc, target?: any): boolean { + if (callback == null) { + console.error(`NetNode setResponeHandler error ${cmd}`); + return false; + } + this._listener[cmd] = [{ target, callback }]; + return true; + } + + public addResponeHandler(cmd: number, callback: NetCallFunc, target?: any): boolean { + if (callback == null) { + console.error(`NetNode addResponeHandler 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; + } + + public removeResponeHandler(cmd: number, 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); + } + } + } + + protected getNetListenersIndex(cmd: number, 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(() => { + console.warn("NetNode recvieMsgTimer close socket!"); + this._socket.close(); + }, this._receiveTime); + } + + protected resetHearbeatTimer() { + if (this._keepAliveTimer !== null) { + clearTimeout(this._keepAliveTimer); + } + + this._keepAliveTimer = setTimeout(() => { + console.log("NetNode keepAliveTimer send Hearbeat") + 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); + } + } + + public rejectReConnect(){ + this._isAutoReconnect = false; + this.clearTimer(); + } +} \ No newline at end of file diff --git a/assets/Script/network/NetNode.ts.meta b/assets/Script/network/NetNode.ts.meta new file mode 100644 index 0000000..97176be --- /dev/null +++ b/assets/Script/network/NetNode.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.5", + "uuid": "75b71ada-9878-434a-96fb-c03cfb4491c9", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/Script/network/WebSock.ts b/assets/Script/network/WebSock.ts new file mode 100644 index 0000000..65e4bb7 --- /dev/null +++ b/assets/Script/network/WebSock.ts @@ -0,0 +1,61 @@ +import { ISocket, NetData } from "./NetInterface"; + +/* +* WebSocket封装 +* 1. 连接/断开相关接口 +* 2. 网络异常回调 +* 3. 数据发送与接收 +* +* 2018-5-14 by 宝爷 +*/ + +export class WebSock implements ISocket { + private _ws: WebSocket = null; // websocket对象 + + onConnected: (event) => void = null; + onMessage: (msg) => void = null; + onError: (event) => void = null; + onClosed: (event) => void = null; + + connect(options: any) { + if (this._ws) { + if (this._ws.readyState === WebSocket.CONNECTING) { + console.log("websocket connecting, wait for a moment...") + return false; + } + } + + let url = null; + if(options.url) { + url = options.url; + } else { + let ip = options.ip; + let port = options.port; + let protocol = options.protocol; + url = `${protocol}://${ip}:${port}`; + } + + this._ws = new WebSocket(url); + this._ws.binaryType = options.binaryType ? options.binaryType : "arraybuffer"; + this._ws.onmessage = (event) => { + this.onMessage(event.data); + }; + this._ws.onopen = this.onConnected; + this._ws.onerror = this.onError; + this._ws.onclose = this.onClosed; + return true; + } + + send(buffer: NetData) { + if (this._ws.readyState == WebSocket.OPEN) + { + this._ws.send(buffer); + return true; + } + return false; + } + + close(code?: number, reason?: string) { + this._ws.close(); + } +} \ No newline at end of file diff --git a/assets/Script/network/WebSock.ts.meta b/assets/Script/network/WebSock.ts.meta new file mode 100644 index 0000000..816bfd8 --- /dev/null +++ b/assets/Script/network/WebSock.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.5", + "uuid": "7c5d5446-f565-45c1-9e5d-e9c46c91f3a3", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file