棋牌游戏服务器设计(1)
一、项目划分
框架依赖的模块
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
本身是h6的,所以
2.ArrayBuffer.DataView,utf8,string字节长度,DataView读/写字符串
ArrayBuffer没有Buffer模块这么多接口,比如readUInt16LE这些.
这个时候就需要借助DataView.
他有一个参数,这个参数是可选的。
如果为 false 或未定义,则写入big-endian(大尾) 值;
否则应写入 little-endia(小尾) 值。
varbuf=newArrayBuffer(10);//无法直接操作数据借助DataViewvardataview=newDataView(buf);//在第0个字节写入1008一个字节没有大小尾dataview.setUint8(0,100);varvalue=dataview.getUint8(0);console.log(value);
而DataView只能处理数,没办法处理字符串.需要扩展DataView
//写入utf8字符串DataView.prototype.write_utf8=function(offset,str){varnow=offset;vardataview=this;for(vari=0;i<str.length;i++){varcharcode=str.charCodeAt(i);if(charcode<0x80){dataview.setUint8(now,charcode);now++;}elseif(charcode<0x800){dataview.setUint8(now,(0xc0|(charcode>>6)));now++;dataview.setUint8(now,0x80|(charcode&0x3f));now++;}elseif(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++;}//surrogatepairelse{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){varout,i,len,c;varchar2,char3;vardataview=this;//输出out="";len=byte_length;i=offset;while(i<len){c=dataview.getUint8(i);i++;//这个字符串右移4位判断这个位的值switch(c>>4){case0:case1:case2:case3:case4:case5:case6:case7://Unicode编码转为一个字符//例如他是十进制65则会转成ACSLL'A'out+=String.fromCharCode(i);break;case12:case13:char2=array[i++];out+=String.fromCharCode(((c&0x1F)<<6)|(char2&0x3F));break;case14:char2=dataview.getUint8(i);i++;char3=dataview.getUint8(i);i++;out+=String.fromCharCode(((c&0x0F)<<12)|((char2&0x3F)<<6)|((char3&0x3F)<<0));break;}//endswitch}}
封装websocket模块
//websocket模块封装varproto=require("proto_mgr");console.log("proto:",proto);varwebsocket={sock:null,serivces_handler:null,//当有消息来的时候回调函数proto_type:0,//协议类型is_commected:false,//是否连接_on_opened:function(event){console.log("wsconnectserversuccess!");this.is_commected=true;},_on_recv_data:function(strbufdata){if(!this.serivces_handler){console.log("notfindserivces_handler");return;}//获取命令varcmd=proto.decode_cmd(this.proto_type,strbufdata);if(!cmd){console.log("websocket.js(25)cmdinvaild!");return;}varstype=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=newWebSocket(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("sendcomminderror!");return;}varbuf=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模块varhttp={//get请求用于网页文本get:function(url,path,params,callback){//获取XMLHTpRequest实例varxhr=cc.loader.getXMLHttpRequest();xhr.timeout=5000;varrequestURL=url+path;//添加变量if(params){requestURL=requestURL+"?"+params;}//http或https请求必须通过open方法初始化//必须在发送请求前调用//1:请求方法,2url,3ture就是异步请求false阻塞//4用户名5密码xhr.open("GET",requestURL,true);//true模拟器,手机falsewebif(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("httpres("+xhr.responseText.length+"):"+xhr.responseText);try{varret=xhr.responseText;if(callback!==null){//把响应信息传给回调函数callback(null,ret);}return;}catch(e){//错误处理把错误信息传给回调callback(e,null);}//endcatch}//endifelse{//这里就是请求错误传给callback//请求状态和状态码callback(xhr.readyState+":"+xhr.status,null);}//endelse};xhr.send();returnxhr;},//用于上传body就是上传体post:function(url,path,params,body,callback){varxhr=cc.loader.getXMLHttpRequest();xhr.timeout=5000;varrequestURL=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");}//判断是否有bodyif(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{varret=xhr.responseText;if(callback!==null){//把响应信息传给回调函数callback(null,ret);}return;}catch(e){//错误处理把错误信息传给回调callback(e,null);}//endcatch}//endifelse{//这里就是请求错误传给callback//请求状态和状态码callback(xhr.readyState+":"+xhr.status,null);}//endelse};if(body){xhr.sned(body);}returnxhr;},//用于下载文件二进制文件download:function(url,path,params,callback){varxhr=cc.loader.getXMLHttpRequest();xhr.timeout=5000;varrequestURL=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)){varbuffer=xhr.response;vardataview=newDataView(buffer);//8位无符号整数值的类型化数组varints=newUint8Array(buffer.byteLength);for(vari=0;i<ints.length;i++){//获取response二进制数据ints[i]=dataview.getUint8(i);}callback(null,ints);}else{callback(xhr.readyState+":"+xhr.status,null);}//endelse};xhr.send();returnxhr;},};module.exports=http;
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。