一、项目划分
框架依赖的模块
1.高性能webserver--express模块 热更新 语音对话
2.websocket模块--ws
3.mysql模块-->mysql
4.redis模块-->redis
框架划分
1.webserver作用就是用来上传下载,获取配置信息,更新等.
通过web接入第三方的sdk。
2.gateway网关服务器,
(1)用来接收所有用户的长连接,转发用户请求
(2)连接游戏服务器转发服务器回应
(3)安全防护,过滤非法的数据包隔离游戏服务器免受客户***.
3.用户中心服务器: 用来管理这个用户的账号信息.这个用户信息
管理号,可以在进行这个平台的其他游戏.
4.系统服务: 处理用户和系统进行交互:每日登录,邮件,兑换东西等等.
比如你要做一个活动,这个就是用户和服务器单独交互.不会涉及其他用户.
5.游戏服务器:他涉及到多个用户之间的交互,处理不同游戏的服务.
每个服务又是一个进程,
数据库划分
1.后台数据库,分为两个.第一个数据库用来存放用户数据,大家公用的,
第二个是每个游戏都有一个游戏数据库。
2.为了访问速度的提高,把常用的数据缓存到redis服务器,
redis缓冲中心:也是两个:用户redis,和游戏数据redis.
3.数据库是所有进程都公用的.
3rd/utils/netbus模块
1.3rd存放第三方的js代码库
2.utils存放所有的公共模块
3.netbus模块,为所有长连接服务器所公用,支持websocket
TCP socket 二进制和json协议。
二、日志 TCPsocket和websocket模块封装支持
Log日志代码
1.log日志是重要的服务器手段
2.log写日志必须是异步的
3.log日志能够方便的定向到对应的服务器里去
4.log日志分登记和颜色
5.调试时全部打印到标准的输出文件,上线时在输出在文件
6.创建一个output文件夹存放输出的日志.
7.日志分级,如一般信息,警告信息,错误信息.
8.把重要信息保存到log,比如你充值了10元.
验证数据的合法性
1.tcp socket收到的数据必须是Buffer类型
2.ws socket json数据协议下收到的类型必须是字符串
3.ws socket buf数据协议下收到的类型必须是buffer
三、协议管理模块
1.协议规定是: 服务号,命令号,数据部分
2.提供协议解码 cmd[0]服务号,cmd[1]命令号,cmd[2]body三个部分
3.提供协议编码函数转json字符 或者 buff二进制(2字节2字节+body);
4提供协议服务端buf×××注册函数
5.同时支持json和二进制,通过客户端连接自己选择
6.协议加密和解密也可以加入到这个模块
先编码在加密 —— 先解密在解码
一般只需要支持一种协议即可.
四、netbus服务管理模块
1.当netbus收到数据包的时候,需要把包分发给对应的服务来进行处理
2.service_manager(mg管理)
3.所有服务的管理模块,所有的服务都注册到这里
4.netbus收到数据,玩家掉线等,都进入它,通知对应的服务
5.提供服务模块注册函数,编写模板服务编写
6.转发到对应的服务后,使用decode_cmd 加密命令
7.告诉所有的service链接丢失
五、creator支持websocket_http支持buf和json协议
1.creator使用websocket和服务器进行联机,因为本身creator
本身是h5的,所以
2.ArrayBuffer.DataView,utf8,string字节长度,DataView读/写字符串
ArrayBuffer没有Buffer模块这么多接口,比如readUInt16LE这些.
这个时候就需要借助DataView.
他有一个参数,这个参数是可选的。
如果为 false 或未定义,则写入big-endian(大尾) 值;
否则应写入 little-endia(小尾) 值。
var buf = new ArrayBuffer(10); //无法直接操作数据 借助DataView var dataview = new DataView(buf); //在第0个字节写入100 8一个字节没有大小尾 dataview.setUint8(0,100); var value = dataview.getUint8(0); console.log(value);
而DataView只能处理数,没办法处理字符串.需要扩展DataView
//写入utf8字符串 DataView.prototype.write_utf8 = function(offset,str){ var now = offset; var dataview = this; for (var i = 0; i < str.length; i++) { var charcode = str.charCodeAt(i); if (charcode < 0x80) { dataview.setUint8(now, charcode); now ++; } else if (charcode < 0x800) { dataview.setUint8(now, (0xc0 | (charcode >> 6))); now ++; dataview.setUint8(now, 0x80 | (charcode & 0x3f)); now ++; } else if (charcode < 0xd800 || charcode >= 0xe000) { dataview.setUint8(now, 0xe0 | (charcode >> 12)); now ++; dataview.setUint8(now, 0x80 | ((charcode>>6) & 0x3f)); now ++; dataview.setUint8(now, 0x80 | (charcode & 0x3f)); now ++; } // surrogate pair else { i ++; charcode = 0x10000 + (((charcode & 0x3ff)<<10) | (str.charCodeAt(i) & 0x3ff)); dataview.setUint8(now, 0xf0 | (charcode >>18)); now ++; dataview.setUint8(now, 0x80 | ((charcode>>12) & 0x3f)); now ++; dataview.setUint8(now, 0x80 | ((charcode>>6) & 0x3f)); now ++; dataview.setUint8(now, 0x80 | (charcode & 0x3f)); now ++; } } } //读取utf8字符串 DataView.prototype.read_utf8 = function(offset,byte_length){ var out,i,len,c; var char2,char3; var dataview = this; //输出 out = ""; len = byte_length; i = offset; while(i < len){ c = dataview.getUint8(i); i++; //这个字符串右移4位 判断这个位的值 switch(c >> 4) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: //Unicode 编码转为一个字符 //例如他是十进制65 则会转成ACSLL 'A' out += String.fromCharCode(i); break; case 12: case 13: char2 = array[i++]; out += String.fromCharCode(((c & 0x1F)<<6) | (char2 & 0x3F)); break; case 14: char2 = dataview.getUint8(i); i++; char3 = dataview.getUint8(i); i++; out += String.fromCharCode(((c & 0x0F)<<12)| ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); break; }//end switch } }
封装websocket模块
//websocket模块封装 var proto = require("proto_mgr"); console.log("proto:",proto); var websocket = { sock: null, serivces_handler: null, //当有消息来的时候回调函数 proto_type: 0, //协议类型 is_commected: false,//是否连接 _on_opened: function(event){ console.log("ws connect server success!"); this.is_commected = true; }, _on_recv_data: function(strbufdata){ if(!this.serivces_handler){ console.log("not find serivces_handler"); return; } //获取命令 var cmd = proto.decode_cmd(this.proto_type,strbufdata); if(!cmd){ console.log("websocket.js(25) cmd invaild!"); return; } var stype = cmd[0]; if(this.serivces_handler[stype]){ this.serivces_handler[stype](cmd[0],cmd[1],cmd[2]); } }, _on_socket_close: function(event){ if(this.sock){ this.close(); } }, _on_socket_err: function(event){ this.close(); }, connect: function(url,proto_type){ this.sock = new WebSocket(url); this.sock.onopen = this._on_opened.bind(this); this.sock.onmessage = this._on_recv_data.bind(this); this.sock.onclose = this._on_socket_close.bind(this); this.sock.onerror = this._on_socket_err.bind(this); this.proto_type = proto_type; }, send_cmd: function(stype,ctype,body){ if(!this.sock || !this.is_commected){ console.log("send commind error!"); return; } var buf = proto.encode_cmd(this.proto_type,stype,ctype,body); this.sock.send(buf); }, close: function(){ this.is_commected = false; if(this.sock !== null){ this.sock.close(); this.sock = null; } }, regist_services_handler: function(serivces_handler){ this.serivces_handler = serivces_handler; }, } //选择启动协议 //使用json 连接到服务 //websocket.connect("ws://127.0.0.1:6081/ws",proto.PROTO_JSON); //使用二进制连接到服务 //websocket.connect("ws://127.0.0.1:6083/ws",proto.PROTO_BUF); module.exports = websocket;
封装http模块
//http 模块 var http = { //get请求 用于网页 文本 get: function(url, path, params,callback){ //获取XMLHTpRequest实例 var xhr = cc.loader.getXMLHttpRequest(); xhr.timeout = 5000; var requestURL = url + path; //添加变量 if(params){ requestURL = requestURL + "?" + params; } //http或https请求必须通过open方法初始化 //必须在发送请求前调用 //1:请求方法,2url,3ture就是异步请求 false阻塞 //4用户名 5密码 xhr.open("GET",requestURL,true); //true模拟器,手机 false web if(cc.sys.isNative){ //设置请求头 信息 xhr.setRequestHeader("Accept-Encoding","gzip,deflate","text/html;charset=UTF-8"); } //readystate 是http的请求状态 当XMLHttpRequest //初次创建时,这个属性是0,直到接收到完整的HTTP响应 //这个值增加到 4。 //1是open方法被调用,send方法未调用 //2是send方法被调用,HTTP请求到达web服务器 //3是所有响应头部都已经接收到。响应体开始接收但未完成。 //4是HTTP响应已经完成接收 xhr.onreadystatechange = function(){ if(xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)){ //输出响应长度 和 正文 console.log("http res("+xhr.responseText.length+"):"+xhr.responseText); try{ var ret = xhr.responseText; if(callback !== null){ //把响应信息 传给回调函数 callback(null,ret); } return; }catch(e){ //错误处理 把错误信息传给回调 callback(e,null); }//end catch }//end if else{ //这里就是请求错误 传给callback //请求状态 和 状态码 callback(xhr.readyState+":"+xhr.status,null); }//end else }; xhr.send(); return xhr; }, //用于上传 body就是上传体 post: function(url,path,params,body,callback){ var xhr = cc.loader.getXMLHttpRequest(); xhr.timeout = 5000; var requestURL = url + path; if(params){ requestURL = requestURL + "?" + params; } xhr.open("POST",requestURL,true); if(cc.sys.isNative){ xhr.setRequestHeader("Accept-Encoding","gzip,deflate","text/html;charset=UTF-8"); } //判断是否有body if(body){ //在发送到服务器之前,所有字符都会进行编码 xhr.setRequestHeader("Content-Type","application/x-www-form=urlencoded"); //长度 xhr.setRequestHeader("Content-Length",body.length); } xhr.onreadystatechange = function(){ if(xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)){ try{ var ret = xhr.responseText; if(callback !== null){ //把响应信息 传给回调函数 callback(null,ret); } return; }catch(e){ //错误处理 把错误信息传给回调 callback(e,null); }//end catch }//end if else{ //这里就是请求错误 传给callback //请求状态 和 状态码 callback(xhr.readyState+":"+xhr.status,null); }//end else }; if(body){ xhr.sned(body); } return xhr; }, //用于下载 文件 二进制文件 download: function(url,path,params,callback){ var xhr = cc.loader.getXMLHttpRequest(); xhr.timeout = 5000; var requestURL = url + path; if(params){ requestURL = requestURL + "?" + params; } //响应类型 xhr.responseType = "arraybuffer"; xhr.open("GET",requestURL,true); if(cc.sys.isNative){ xhr.setRequestHeader("Accept-Encoding","gzip,deflate","text/html;charset=UTF-8"); } xhr.onreadystatechange = function(){ if(xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)){ var buffer = xhr.response; var dataview = new DataView(buffer); //8 位无符号整数值的类型化数组 var ints = new Uint8Array(buffer.byteLength); for(var i = 0;i < ints.length; i++){ //获取response 二进制数据 ints[i] = dataview.getUint8(i); } callback(null,ints); }else{ callback(xhr.readyState+":"+xhr.status,null); }//end else }; xhr.send(); return xhr; }, }; module.exports = http;