Websocek笔记二 搭建egret序列化字节流传输环境

Websocek笔记二 搭建egret序列化字节流传输环境

此博客还未完善,需要加上很多解释文字

egret中的websocket有两种传输方式:

以二进制方式传输:

this.webSocket.type = egret.WebSocket.TYPE_BINARY;

以字符串形式传输:(this.webSocket.writeUTF(“abcde”);)

this.webSocket.type = egret.WebSocket.TYPE_STRING;

搭建字节流传输环境

源代码见我的上传资源

skynet服务端代码:

必须先搭建好skynet的websocket引用

字节流传输lua脚本代码:

skynet/test/testwebsocket.lua

local skynet = require "skynet"
local socket = require "skynet.socket"
local websocket = require "websocket"
local httpd = require "http.httpd"
local urllib = require "http.url"
local sockethelper = require "http.sockethelper"


local handler = {}
function handler.on_open(ws)
    print(string.format("%d::open", ws.id))
    --register Timer
    skynet.fork(function()
        while true do
            -- ws:send_text("heart" .. "from server")
            local string = "1212fdsafa";

            local x = string.pack("IHHibIIII",0,1150,0,0,"4",1000,1001,1002,1003);
            -- local y = {string.unpack('iii',x)};

            ws:send_binary(x)
            skynet.sleep(500)
        end
    end)
end

function handler.on_message(ws, message)
    print(string.format("%d receive:%s", ws.id, message))
    ws:send_binary("from server")
    -- ws:close()
end

function handler.on_close(ws, code, reason)
    print(string.format("%d close:%s  %s", ws.id, code, reason))
end

local function handle_socket(id)
    -- limit request body size to 8192 (you can pass nil to unlimit)
    local code, url, method, header, body = httpd.read_request(sockethelper.readfunc(id), 8192)
    if code then
        
        if header.upgrade == "websocket" then
            local ws = websocket.new(id, header, handler)
            ws:start()
        end
    end


end

skynet.start(function()
    local address = "0.0.0.0:8001"
    skynet.error("Listening "..address)
    local id = assert(socket.listen(address))
    socket.start(id , function(id, addr)
       socket.start(id)
       pcall(handle_socket, id)
    end)
end)

其中最关键的代码:

function handler.on_open(ws)中的:

local x = string.pack("IHHibIIII",0,1150,0,0,"4",1000,1001,1002,1003);//按照字符串中的顺序,把后面的n个参数序列化
ws:send_binary(x)

lua5.3中string.pack可以把数据序列化,格式:

#define OP_ZSTRING  'z'     //空字符串
    #define OP_BSTRING  'p'     //长度小于2^8的字符串
    #define OP_WSTRING  'P'     //长度小于2^16的字符串
    #define OP_SSTRING  'a'     //长度小于2^32/64的字符串*/
    #define OP_STRING   'A'     //指定长度字符串
    #define OP_FLOAT    'f'     /* float */
    #define OP_DOUBLE   'd'     /* double */
    #define OP_NUMBER   'n'     /* Lua number */
    #define OP_CHAR     'c'     /* char */
    #define OP_BYTE     'b'     /* byte = unsigned char */
    #define OP_SHORT    'h'     /* short */
    #define OP_USHORT   'H'     /* unsigned short */
    #define OP_INT      'i'     /* int */
    #define OP_UINT     'I'     /* unsigned int */
    #define OP_LONG     'l'     /* long */
    #define OP_ULONG    'L'     /* unsigned long */

灵活自定义"IHHibllll" 序列化方式fmt,以及后面参数的方法

序列化的时候,参数填成一个函数

-- hello.lua
-- the first program in every language

function fun()
  return 1,2,3,4,5,6,7
end

local x = string.pack('iii',fun());
local y = {string.unpack('iii',x)};
io.write("--",x,"--",y[1],y[2],y[3])

在线测试lua代码:

注意:本例中byte的fmt,egret项目Eprotocal中byte是“C”,服务端Lua代码byte中是“b”,拥有不同编码。所以本例中服务端的序列化码fmt("IHHibIIII"),和客户端的解序列化fmt码("IHHiCIIII")稍有不同。

本例的模拟数据,开头有四个信息是不属于数据内容

服务端模拟数据的格式
unsignedInt msgLen //这个的长度可以用断点,在CustomSocket.ts中的msgLen中查看
unsignedShort msg_type      //协议类型,这个东西是根据它获取相应的协议class
unsignedShort msg_sid
Int msg_id

客户端代码

目录:

src/bytesArray/CustomByteArray.ts:

class CustomByteArray extends egret.ByteArray{
	public constructor() {
		super();
		// this.endian = egret.Endian.BIG_ENDIAN;
		this.endian = egret.Endian.LITTLE_ENDIAN;
	}

	public writeUTFBytes(str:string, len:number = 0):void{
		if(len > 0){
			for(let i:number = 0; i < len; i++){
				this.writeByte(0);
			}
		}
		let temp:number = this.position;
		this.position -= len;
		super.writeUTFBytes(str);
		this.position = temp;
	}
	
	public readUTFBytes(len:number):string
	{
		let ba:egret.ByteArray = new egret.ByteArray()
		ba.endian = egret.Endian.LITTLE_ENDIAN;
		this.readBytes(ba, 0, len);

		let res:string = ba.readUTFBytes(len);
		ba.length = 0
		ba = null
		return res;
	}
}

src/bytesArray/CustomSocket.ts:

class CustomSocket extends egret.EventDispatcher {
	public static ERROR: string = "socket_error";
	public static CONNECT: string = "socket_connect";
	private _cmdArray: Array<Array<any>>;
	private _thisArray: Array<Array<any>>;
	private _bufferDataArray: Array<any>;
	private _cashBytes: CustomByteArray;
	private _contentLen: number = 0;
	/** 协议头长度 (协议长度4 + 消息号2 + msg_sid2 + msg_id 4) */
	private HEADLENGTH: number = 12;
	private _readDataFlag: boolean = false;
	public ip: string;
	public port: number;
	// public isClosed: boolean = true;
	public get isClosed():boolean{
		if(this._socket){
			return !this._socket.connected;
		}
		return true;
	}

	private static _instance: CustomSocket;
	public stopReceiveMsg:boolean = false;

	public static getInstance(): CustomSocket {
		if (CustomSocket._instance == null) {
			CustomSocket._instance = new CustomSocket();
		}
		return CustomSocket._instance;
	}

	private _socket:MySocket;
	public constructor() {
		super();
		this.init();
	}

	private init(): void {
		this._cmdArray = new Array();
		this._thisArray = new Array();
	}
	private reset():void{
		if(this._bufferDataArray)
			this._bufferDataArray.length = 0;
		this._readDataFlag = false;
		this._bufferDataArray = new Array();
	}

	public get id():number{
		if(this._socket == null){
			return 0;
		}
		return this._socket.id;
	}

	//开始连接
	private _connFailed:boolean = false;
	public connecting:boolean = false;
	public start(ip: string = null, port: number = 0): void {
		this.ip = ip;
		this.port = port;

		if(this._socket == null){
			this._socket = new MySocket();		
			this.configureListeners();
		}

		this.reset();
		this._connFailed = false;		
		this.connecting = true;
		send_msg_num = 0;
		rec_msg_num = 0;

		if (useSSL) {
			let url:string = `wss://${ip}`;
			trace(`[CustomSocket:start][${this.id}] url=${url}`);
			this._socket.connectByUrl(url);
		}
		else {
			trace(`[CustomSocket:start][${this.id}] [host=${this.ip} port=${this.port} id=${this._socket.id} time=${NOWTIMER}]`);
			
			if(this.ip.indexOf("s-") == 0){
				let tarr:string[] = this.ip.split("/");
				if(tarr.length == 2){
					this.ip = tarr[0].replace("s-", tarr[1]+"-");
				}
			}else{
				let reg:RegExp = new RegExp("x\d+\-", "i");
				let strarr = this.ip.match(reg);
				if(strarr && strarr.length > 0){
					let tarr:string[] = this.ip.split("/");
					if(tarr.length == 2){
						this.ip = tarr[0].replace(reg, tarr[1]+"-");
					}
				}
			}
			this._socket.connect(this.ip, this.port);
		}
	}
	public close():void{
		trace(`[CustomSocket:close][${this.id}][主动]关闭连接!!${this.isClosed} time=${NOWTIMER}`);
		// this.removeListeners();
		// this.isClosed = true;
		// this._socket = null;
		this.connecting = false;
		this._socket.close();
	}

	// private removeListeners():void{
	// 	if(this._socket){
	// 		this._socket.removeEventListener(egret.Event.CLOSE, this.closeHandler, this);
	// 		this._socket.removeEventListener(egret.Event.CONNECT, this.connectHandler, this);
	// 		this._socket.removeEventListener(egret.IOErrorEvent.IO_ERROR, this.ioErrorHandler, this);
	// 		this._socket.removeEventListener(egret.ProgressEvent.SOCKET_DATA, this.socketDataHandler, this);
	// 		// this._socket.close();
	// 	}
	// }

	//配置socket监听事件
	private configureListeners(): void {
		this._socket.addEventListener(egret.Event.CLOSE, this.closeHandler, this);
		this._socket.addEventListener(egret.Event.CONNECT, this.connectHandler, this);
		this._socket.addEventListener(egret.IOErrorEvent.IO_ERROR, this.ioErrorHandler, this);
		this._socket.addEventListener(egret.ProgressEvent.SOCKET_DATA, this.socketDataHandler, this);
	}

	private connectHandler(event: egret.Event): void {
		let sk:MySocket = event.currentTarget as MySocket;
		trace(`-----------------连接成功--------------${sk.id}/${this._socket.id}`);
		// if(this._socket.id != sk.id)
		// 	return;

		// this.isClosed = false;
		this.connecting = false;
		this.dispatchEvent(new ParamEvent(CustomSocket.CONNECT));		
	}

	//IO错误
	private ioErrorHandler(event: egret.Event): void {
		let sk:MySocket = event.currentTarget as MySocket;
		trace(`----------------err网络故障----------------${sk.id}/${this._socket.id}/${this._connFailed}::::::${this._socket.connected}`);
		// if(this._socket.id != sk.id)
		// 	return;
		
		this.connecting = false;
		if(!this._connFailed){
			this._connFailed = true;
		}else{
			return;
		}
		
		// if(!this.isClosed){
			// this.isClosed = true;
			if(this._socket.connected){
			// this.close();
				this._socket.close();
			}
			this.dispatchEvent(new ParamEvent(CustomSocket.ERROR, { code: 2 }));
		// }
	}

	//服务端断开连接
	private closeHandler(event: egret.Event): void {
		let sk:MySocket = event.currentTarget as MySocket;
		trace(`--------------socket断开了---------------${sk.id}/${this._socket.id}/${this._connFailed}::::::${this._socket.connected}`);
		// if(this._socket.id != sk.id)
		// 	return;
		
		this.connecting = false;
		if(!this._connFailed){
			this._connFailed = true;
		}else{
			return;
		}

		// if(!this.isClosed){
			// this.isClosed = true;
			if(this._socket.connected){
			// this.close();
				this._socket.close();
			}
			this.dispatchEvent(new ParamEvent(CustomSocket.ERROR, { code: 1 }));
		// }
	}

	//收到socket数据进行处理
	private socketDataHandler(event: ProgressEvent): void {
		LAST_SOCKET_TIME = NOWTIMER;
		var bytes: CustomByteArray = new CustomByteArray(); 	//开辟缓冲区
		this._socket.readBytes(bytes);		 //将数据读入内存缓冲区

		this._bufferDataArray.push(bytes);
		if (!this._readDataFlag) //如果当前没有在数据处理中 将开始处理数据,否则等待处理
		{
			this._readDataFlag = true; //设置状态标志为处理中
			this.handleBufferData(); //开始处理数据·
			this._readDataFlag = false;
		}
		event = null;
		bytes = null;
	}

	//处理缓冲区数据
	private handleBufferData(): void {
		if (this._bufferDataArray.length <= 0) {
			//当前数据缓冲区为空
			this._readDataFlag = false;
			return;
		}

		//如果不为空 将读取队列头数据
		var bytesArray: CustomByteArray = this._bufferDataArray.shift();
		bytesArray.position = 0; //将字节数组指针还原 
		//如果上一次缓存的字节数组里面有东西,将读取出来和这一次进行拼接
		if (this._cashBytes != null && this._cashBytes.bytesAvailable > 0) {
			var dataBytes: CustomByteArray = new CustomByteArray();
			this._cashBytes.readBytes(dataBytes, 0, this._cashBytes.bytesAvailable);
			bytesArray.readBytes(dataBytes, dataBytes.length, bytesArray.bytesAvailable);
			this._cashBytes = null;
			bytesArray = dataBytes;
			bytesArray.position = 0; 		//将字节数组指针还原 	
			dataBytes = null;
		}

		if (this._contentLen == 0 && bytesArray.bytesAvailable < this.HEADLENGTH) {
			//当前数据不够一条最短协议的长度,且还未读取过包长度  将缓存数据
			this.cacheRemainBytes(bytesArray);
			bytesArray.length = 0;
			bytesArray = null;
			this.handleBufferData(); //重新开始去队列数据				
		}
		else {
			//将字节数组转换成数据
			this.getBytes(bytesArray);
			dataBytes = null;
			bytesArray = null;
		}
	}

	// 将 ba 读取之后剩余的数据放入缓冲区,等待下一次消息触发时拼接
	private cacheRemainBytes(ba:CustomByteArray):void{
		if(this._cashBytes == null)
			this._cashBytes = new CustomByteArray();
		ba.readBytes(this._cashBytes, this._cashBytes.length, ba.bytesAvailable);
		ba = null;
	}

	public getBytes(bytesArray: CustomByteArray): void {
		let msgLen:number = 0;	// 协议长度
		if(bytesArray.bytesAvailable < 4){	// 小于4字节,不够读取长度
			this.cacheRemainBytes(bytesArray);
			bytesArray.length = 0;
			bytesArray = null;
			this.handleBufferData();	// 继续读取消息队列
		}else{
			msgLen = bytesArray.readUnsignedInt();
			if(bytesArray.bytesAvailable < msgLen){		// 剩下的字节不够一个协议长度了
				// 因为已经读取了4字节的长度了,要将剩余的加入到缓冲区里面,这时指针要回退4个字节
				bytesArray.position -= 4;
				this.cacheRemainBytes(bytesArray);
				bytesArray.length = 0;
				bytesArray = null;
				this.handleBufferData();	// 继续读取消息队列
			}else{
				// 读取协议内容
				let msgBytes:CustomByteArray = new CustomByteArray();
				bytesArray.readBytes(msgBytes, 0, msgLen);
				// 读取到协议,进行处理
				this.getMsgBytes(msgBytes);
				msgBytes = null;
				
				// 如果还有数据继续读取
				if(bytesArray.bytesAvailable > 0){
					this.getBytes(bytesArray)
					bytesArray = null;
				}else{
					bytesArray.length = 0;
					bytesArray = null;
					this.handleBufferData();
				}
			}
		}
	}

	/** 在主角信息获取到之前,除了登录+心跳+角色基础+角色列表信息,其他的协议先缓存起来 */
	private _waitProtoList:any[] = [];
	private getMsgBytes(msgBytes:CustomByteArray):void{
		let proto:EProtocol = EProtocol.parseProtocol(msgBytes);
		if(proto == null || this.stopReceiveMsg){
			return;
		}
		let cmd:number = proto.msg_type;
		// console.log(`收到消息::${cmd}`);

		if(!MAINROLE_DATA_INITED){
			if(cmd > 1000 && cmd != 2700 && cmd != 2703){
				this._waitProtoList.push(proto);
			}else{
				this.dispatchProtocol(proto);

				if(cmd == 2703){
					MAINROLE_DATA_INITED = true;

					// 收到角色列表信息,表示角色数据初始化完成,清空等待队列里的协议
					for(let i:number = 0; i < this._waitProtoList.length; i++){
						let e:EProtocol = this._waitProtoList[i];
						this.dispatchProtocol(e);
					}
				}
			}
			return;
		}

		this.dispatchProtocol(proto);
		rec_msg_num++;
	}

	public dispatchProtocol(proto:EProtocol):void{
		let cmd:number = proto.msg_type;
		let hander: Array<any> = this._cmdArray[cmd];
		let thisarr: Array<any> = this._thisArray[cmd];
		if (hander == null || hander.length <= 0) {
			console.log(`协议 ${cmd} 没有被侦听`)
			return;
		}
		
		for (let i: number = 0; i < hander.length; i++) {
			// hander[i](proto)
			hander[i].apply(thisarr[i], [proto]);	// dxy 20180110 没有实现this很蛋疼
		}
		hander = null;
		thisarr = null;
	}

	/** 清理因为没有登录成功而等待的消息 */
	public clearWaitMsg():void{
		while(this._sendWaitMsg.length > 0){
			let proto:EProtocol = this._sendWaitMsg.shift();
			this.sendMessage(proto);
		}
	}

	private _sendWaitMsg:EProtocol[] = [];
	public sendMessage(proto:EProtocol):void{
		if(this.isClosed){
			// if(!this.connecting && !GameCtrl.ins.connPoped && proto.msg_type > 109){
			// 	GameCtrl.ins.connTimeout("网络连接断开,请点击确定重试!");
			// }
			return;
		}

		if(proto.msg_type > 103 && !hadLogined){
			console.log(`协议[${proto.msg_type}]需要在登录成功后才能发送!!`)
			this._sendWaitMsg[this._sendWaitMsg.length] = proto;
			return;
		}

		LAST_SEND_MSG_TIME = NOWTIMER;

		let ba:CustomByteArray = EProtocol.packProtocol(proto);
		// console.log(`[sendMessage]::${proto.msg_type},len=${ba.length}`)
		if(ba){
			this._socket.writeBytes(ba);
			this._socket.flush();

			ba.length = 0;
			ba = null;
			send_msg_num++;
		}
		
	}

	/**
	 *添加某个消息号的监听
	 * @param cmd	消息号
	 * @param args	传两个参数,0为处理函数  1为需要填充的数据对象
	 *
	 */
	addCmdListener(cmd: number, hander: Function, thisObj: any): void {
		if (this._cmdArray[cmd] == null) {
			this._cmdArray[cmd] = [];
			this._thisArray[cmd] = [];
		}
		if (this._cmdArray[cmd].indexOf(hander) == -1) {
			this._cmdArray[cmd].push(hander);
			this._thisArray[cmd].push(thisObj);
		}
	}

	/**
		 *移除 消息号监听
		 * @param cmd
		 *
		 */
	removeCmdListener(cmd: number, listener: Function): void {
		var handers: any[] = this._cmdArray[cmd];
		var thisarr: any[] = this._thisArray[cmd];
		if (handers != null && handers.length > 0) {
			for (var i: number = (handers.length - 1); i >= 0; i--) {
				if (listener == handers[i]) {
					handers.splice(i, 1);
					thisarr.splice(i, 1);
				}
			}
		}
	}
}

src/bytesArray/EProtocol.ts

/**
 * 协议基类
 * 
 */
class EProtocol {
	/**
	 * 根据协议号,创建对应协议的实例
	 */
	public static createProtocol(msg_type:number):any{
		let cls = ProtocolMap[msg_type];
		if(cls)
			return new cls(msg_type);
		else{
			console.log(`未注册的协议号${msg_type}`)
			return null;
		}
	}

	/**
	 * 解析协议
	 */
	public static parseProtocol(bytes:CustomByteArray):EProtocol{
		let msg_type:number = bytes.readUnsignedShort();
		let msg_sid:number = bytes.readUnsignedShort();
		let msg_id:number = bytes.readInt();
		let proto:EProtocol = EProtocol.createProtocol(msg_type) as EProtocol;
		if(proto){
			proto.msg_sid = msg_sid;
			proto.msg_id = msg_id;
			proto.protoBytes = bytes;
			proto.decode();
		}
		bytes.length = 0;
		bytes = null;
		return proto;
	}

	/**
	 * 封装协议
	 */
	public static packProtocol(proto:EProtocol):CustomByteArray{		
		proto.encode();
		if(proto.protoBytes == null)
		{
			console.log("协议内容为空")
			return null;
		}

		let len:number = proto.protoBytes.length;	// 协议长度
		let bytes = new CustomByteArray();
		bytes.writeUnsignedInt(len);
		// proto.protoBytes.position = 0;
		bytes.writeBytes(proto.protoBytes, 0, len);
		return bytes;
	}

	public msg_type:number;
	public msg_sid:number;
	public msg_id:number;
	private _bytes:CustomByteArray;
	public constructor(msg_type:number, msg_sid?:number) {
		this.msg_type = msg_type;
		this.msg_sid = msg_sid == null ? -1 : msg_sid;
		this.msg_id = 0;
	}

	public set protoBytes(bytes:CustomByteArray){
		this._bytes = bytes;
	}
	public get protoBytes():CustomByteArray{
		return this._bytes;
	}

	private dispose():void{
		this._bytes.position = 0;
		this._bytes = null;
	}

	protected writeFMT(format, ...params):boolean{
		let ba:CustomByteArray = this._bytes;
		if(ba == null){
			console.log("writeFMT 没有设置协议数据");
			return false;
		}

		let len = format.length;
		let i:number;
		let idx:number = 0
		for(i = 0; i < len; i++){
			let t = format.charAt(i);
			let v = params[idx++];
			if(v == null){
				this.dispose();
				console.log("format格式与对应参数不匹配")
				return false;
			}
			let charnum = 0;
			let isstr:boolean = false;
			switch(t){
				case 'i': { ba.writeInt(v); break; }
				case 'I': { ba.writeUnsignedInt(v); break; }
				case 'd': { ba.writeDouble(v); break; }
				case 'h': { ba.writeShort(v); break; }
				case 'H': { ba.writeUnsignedShort(v); break; }
				case 'c': { ba.writeByte(v); break; }
				case 'C': { ba.writeByte(v); break; }
				case 'f': { ba.writeFloat(v); break; }
				case 's': {
					isstr = true;
					let j = i + 1;
					let numstr:string = "";
					while(j < len){
						let chr:number = format.charCodeAt(j);
						if(chr >= 48 && chr <= 58){
							numstr += format.charAt(j);
							i++;
							j++;
						}else{
							break;
						}
					}
					if(numstr != "")
						charnum = Number(numstr);

					break;
				}					
				default:{
					this.dispose()
					console.log("不是正确的格式:" + t);
					return false;
				}
			}

			if(isstr){
				if(charnum > 0){
					ba.writeUTFBytes(v, charnum);
				}else{
					this.dispose();
					console.log("format中s没有设定长度!");
					return false;
				}
			}			
		}

		if(idx != params.length){
			this.dispose();
			console.log(`format定义与参数个数不匹配${idx}/${params.length}`);
		}
		
		return true;
	}

	protected readFMT(format:string):any[]{
		let ba:CustomByteArray = this._bytes;
		if(ba == null){
			console.log("readFMT 没有设置协议数据");
			return null;
		}

		let res:any[] = [];
		let len = format.length;
		let i:number;
		for(i = 0; i < len; i++){
			let t = format.charAt(i);

			let charnum = 0;
			let val;
			let isstr:boolean = false;
			switch(t){
				case 'i': { val = ba.readInt(); break; }
				case 'I': { val = ba.readUnsignedInt(); break; }
				case 'd': { val = ba.readDouble(); break; }
				case 'h': { val = ba.readShort(); break; }
				case 'H': { val = ba.readUnsignedShort(); break; }
				case 'c': { val = ba.readByte(); break; }
				case 'C': { val = ba.readUnsignedByte(); break; }
				case 'f': { val = ba.readFloat(); break; }
				case 's': {
					isstr = true;
					let j = i + 1;
					let numstr:string = "";
					while(j < len){
						let chr:number = format.charCodeAt(j);
						if(chr >= 48 && chr <= 58){
							numstr += format.charAt(j);
							i++;
							j++;
						}else{
							break;
						}
					}
					if(numstr != "")
						charnum = Number(numstr);

					break;
				}					
				default:{
					this.dispose()
					console.log("不是正确的格式:" + t);
				}
			}

			if(isstr){
				if(charnum > 0){
					val = ba.readUTFBytes(charnum);
				}else{
					this.dispose();
					console.log("format中s没有设定长度!");
				}
			}

			res.push(val);		
		}
		
		return res;
	}

	public encode():void{
		// 添加协议头
		this._bytes = new CustomByteArray();
		this._bytes.writeUnsignedShort(this.msg_type);
		this._bytes.writeUnsignedShort(this.msg_sid);
		this._bytes.writeInt(this.msg_id);
	}

	public decode():void{

	}
}

src/bytesArray/Globals.ts

/** 协议字典 */
var ProtocolMap:{[msg_type:number]:any} = {};

var send_msg_num = 0;

var rec_msg_num = 0;

var useSSL:boolean = false;

/** 已经登录(服务器返回登录成功,在这之前,只有 登录/创角 协议可以发送) */
var hadLogined:boolean = false;

function trace(...args):void{
    let dt:Date = new Date();
    let str:string = `[${dt.getHours()}:${dt.getMinutes()}:${dt.getSeconds()}]`;
    // args.unshift(str);
    // console.log.apply(null, args);
    // console.log.call(null, str, ...args);
    for(let i=0; i<arguments.length; i++){
        str += `${arguments[i]}   `;
    }
    console.log(str);
}

/** 启动到当前的时间(ms) */
var NOWTIMER:number = 0;

/** 最后收到服务器消息的时间(用来做心跳超时) */
var LAST_SOCKET_TIME:number = 0;
/** 最后发送消息时间 */
var LAST_SEND_MSG_TIME:number = 0;

var MAINROLE_DATA_INITED:boolean = true;

class EMsgCode {
    public static SC_GuildWarTime = 1150;
}

src/bytesArray/msg_test.ts

class SC_GuildWarTime extends EProtocol {
	public time_arr: Array<number> = [];
	public constructor(msg_sid: number) {
		super(EMsgCode.SC_GuildWarTime, msg_sid);
	}
	public decode(): void {
		let count = this.readFMT("C")[0];
		for (var i = 0; i < count; i++) {
			let arr = this.readFMT("I")
			this.time_arr.push(arr[0])
		}
	}
}
ProtocolMap[EMsgCode.SC_GuildWarTime] = SC_GuildWarTime;

src/bytesArray/MySocket.ts

class MySocket extends egret.WebSocket {
	public ip: string;
	public port: number;
	public isClosed: boolean = true;
	private _id:number;
	private static NET_INDEX:number = 0;
	public get id():number{
		return this._id;
	}
	public constructor() {
		super();
		this._id = ++MySocket.NET_INDEX;
		this.type = egret.WebSocket.TYPE_BINARY;
	}

	// private removeEvent():void{
	// 	this.addEventListener(egret.Event.CLOSE, this.closeHandler, this);
	// 	this.addEventListener(egret.Event.CONNECT, this.connectHandler, this);
	// 	this.addEventListener(egret.IOErrorEvent.IO_ERROR, this.ioErrorHandler, this);
	// 	this.addEventListener(egret.ProgressEvent.SOCKET_DATA, this.socketDataHandler, this);
	// }

	// private addEvent():void{
	// 	this.removeEvent();
	// 	this.removeEventListener(egret.Event.CLOSE, this.closeHandler, this);
	// 	this.removeEventListener(egret.Event.CONNECT, this.connectHandler, this);
	// 	this.removeEventListener(egret.IOErrorEvent.IO_ERROR, this.ioErrorHandler, this);
	// 	this.removeEventListener(egret.ProgressEvent.SOCKET_DATA, this.socketDataHandler, this);
	// }

	// //IO错误
	// private ioErrorHandler(event: egret.Event): void {
	// 	if(!this.isClosed){
	// 		this.isClosed = true;
	// 		console.log("----------------------服务器重启了-------------------");
	// 		this.dispatchEvent(new ParamEvent(CustomSocket.ERROR, { code: 2 }));
	// 	}
	// }

	// //判断是否在连接中
	// private connectHandler(event: egret.Event): void {
	// 	console.log("---------------------连接成功------------------");
	// 	this.isClosed = false;
	// 	this.dispatchEvent(new ParamEvent(CustomSocket.CONNECT));		
	// }

	// //服务端断开连接
	// private closeHandler(event: egret.Event): void {
	// 	if(!this.isClosed){
	// 		this.isClosed = true;
	// 		console.log("----------------------socket断开了-------------------");
	// 		this.dispatchEvent(new ParamEvent(CustomSocket.ERROR, { code: 1 }));
	// 	}
	// }

	// //收到socket数据进行处理
	// private socketDataHandler(event: ProgressEvent): void {
	// 	LAST_SOCKET_TIME = NOWTIMER;
	// 	var bytes: CustomByteArray = new CustomByteArray(); 	//开辟缓冲区
	// 	this.readBytes(bytes);		 //将数据读入内存缓冲区

	// 	this._bufferDataArray.push(bytes);
	// 	if (!this._readDataFlag) //如果当前没有在数据处理中 将开始处理数据,否则等待处理
	// 	{
	// 		this._readDataFlag = true; //设置状态标志为处理中
	// 		this.handleBufferData(); //开始处理数据·
	// 		this._readDataFlag = false;
	// 	}
	// 	event = null;
	// 	bytes = null;
	// }
}

src/bytesArray/ParamEvent.ts

class ParamEvent extends egret.Event{
	public data:any;
	public constructor(type:string, data:any=null) {
		super(type);
		this.data = data;
	}
}

src/Controller/BaseContol.ts

class BaseContol {
	protected _dispatcher: GameEvent;
	protected _socket: CustomSocket;
	/** -1:立即执行  0:500-1500ms随机执行 >0:指定延迟时间执行 */
	protected _callBackDelay: number = 0;
	public constructor() {
		this.init();
		this.addEventHandler();
		this.addCmdHandler();
	}

	protected init(): void {
		this._socket = CustomSocket.getInstance();
		this._dispatcher = GameEvent.ins;
		// this.addEventListener(EventName.GAME_START, this.whenGameStart, this);
		// this.addEventListener(EventName.GAME_RE_START, this.onRestart, this);
	}

	// protected whenGameStart(e:ParamEvent):void{
	// 	this.removeEventListener(EventName.GAME_START, this.whenGameStart, this, false);
	// 	var delay: number = this._callBackDelay;
	// 	if (delay == -1) {
	// 		this.onGameStart();
	// 		return;
	// 	}
	// 	if (delay == 0) {
	// 		delay = 500 + Math.ceil(Math.random() * 1000) >> 1;
	// 	}
	// 	setTimeout2(() => {
	// 		// let tm = NOWTIMER;
	// 		this.onGameStart();
	// 	}, delay);
	// }

	// protected onRestart(event:ParamEvent):void{
		
	// }

	protected onGameStart(event?: ParamEvent): void {
		
	}

	protected addCmdHandler(): void { }
	protected addEventHandler(): void {

	}
	protected addCmdListener(cmd: number, fun: Function, thisObj: any): void {
		this._socket.addCmdListener(cmd, fun, thisObj);
	}

	protected removeCmdListener(cmd: number, fun: Function): void {

	}

	protected sendSocketData(cmd: number, data: Object = null): void {
		// this._socket.sendMessage(cmd, data);
	}

	protected addEventListener(eventName: string, func: Function, thisObj: any, useCapture: boolean = false, priority: number = 1): void {
		this._dispatcher.addEventListener(eventName, func, thisObj, useCapture, priority);
	}

	protected removeEventListener(eventName: string, func: Function, thisObj: any, useCapture: boolean = false): void {
		this._dispatcher.removeEventListener(eventName, func, thisObj, useCapture);
	}

	protected hasEventListener(eventName: string): boolean {
		return this._dispatcher.hasEventListener(eventName);
	}

	protected once(eventName: string, func: Function, thisObj: any, useCapture: boolean = false): void {
		this._dispatcher.once(eventName, func, thisObj, useCapture)
	}

	protected dispatchEvent(event: egret.Event): boolean {
		return this._dispatcher.dispatchEvent(event);
	}

	protected willTrigger(eventName: string): boolean {
		return this._dispatcher.willTrigger(eventName);
	}
}

src/Controller/GameCtrl.ts

注意这个地方,要在虚拟机查看本机Ip,中找到skynet的服务器地址,还有skynet/examples/config找到监听端口

class GameCtrl extends BaseContol {
	private _host: string = "192.168.137.151";//这里填入虚拟机服务器的ip地址
	private _port: number = 8001; //这里填skynet的网络端口

	public constructor() {
		super();
		this._socket.start(this._host, this._port);
	}

	private static _instance: GameCtrl;
	public static get ins(): GameCtrl {
		if (GameCtrl._instance == null)
			GameCtrl._instance = new GameCtrl();
		return GameCtrl._instance;
	}

	protected addCmdHandler(): void {
		this._socket.addCmdListener(EMsgCode.SC_GuildWarTime, this.SSC_GuildWarTime, this);
	}

	/**
     * 返回协议内容
     * @param e 返回的协议内容
     */
    private SSC_GuildWarTime(e: EProtocol):void{
        let message: SC_GuildWarTime = e as SC_GuildWarTime;
		console.log("SC_GuildWarTime 			= 1147 时间列表变更返回:", message);
    }

}

src/Controller/GameEvent.ts

class GameEvent extends egret.EventDispatcher {

	private static _instance: GameEvent;
	public constructor() {
		super();
	}

	public static get ins(): GameEvent {
		if (GameEvent._instance == null) {
			GameEvent._instance = new GameEvent();
		}
		return GameEvent._instance;
	}

	public addEvent(eventName: string, func: Function, thisObj: any, useCapture: boolean = false, priority: number = 1): void {
		super.addEventListener(eventName, func, thisObj, useCapture, priority);
	}

	public removeEvent(eventName: string, func: Function, thisObj: any, useCapture: boolean = false): void {
		super.removeEventListener(eventName, func, thisObj, useCapture);
	}

	public hasEvent(eventName: string): boolean {
		return super.hasEventListener(eventName);
	}

	public once(eventName: string, func: Function, thisObj: any, useCapture: boolean = false): void {
		super.once(eventName, func, thisObj, useCapture)
	}

	public dispatchEvent(event: egret.Event): boolean {
		var t1: number = egret.getTimer();
		var result: boolean = super.dispatchEvent(event);
		var gap:number = egret.getTimer() - t1;
		if (gap > 5) {
			//console.log(StringUtil.substitute("send Evt gap = {1} name = {0}", event.type, gap));
		}
		return result;
	}

	public willTrigger(eventName: string): boolean {
		return super.willTrigger(eventName);
	}
}

在:Main.ts中:

    private createGameScene() {
        GameCtrl.ins;
    }

运行测试:

每隔5s钟就会收到协议并解析