博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
棋牌游戏服务器设计(1)
阅读量:5889 次
发布时间:2019-06-19

本文共 9603 字,大约阅读时间需要 32 分钟。

一、项目划分

框架依赖的模块

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. 

图片.png


他有一个参数,这个参数是可选的。

如果为 false 或未定义,则写入big-endian(大尾) 值;

否则应写入 little-endia(小尾) 值。

1
2
3
4
5
6
7
        
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);

图片.png

而DataView只能处理数,没办法处理字符串.需要扩展DataView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//写入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模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//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模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//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;




 本文转自超级极客51CTO博客,原文链接:http://blog.51cto.com/12158490/2071208,如需转载请自行联系原作者

你可能感兴趣的文章
视频编解码学习之五:差错控制及传输
查看>>
Postman教程
查看>>
python模块--os模块
查看>>
HSSFRow获取单元格方法与区别
查看>>
洛谷 1365 WJMZBMR打osu! / Easy
查看>>
删除UINavigationItem上的BarButtonItem
查看>>
数据分析相关模块
查看>>
Python数据结构1-----基本数据结构和collections系列
查看>>
SQL Denali-FileTable
查看>>
C# 图像处理:复制屏幕到内存中,拷屏操作
查看>>
PHP微信支付流程
查看>>
linux下单节点oracle数据库间ogg搭建
查看>>
PLSQL Developer软件使用大全
查看>>
swift三方库
查看>>
杭州之行
查看>>
oracle ORA-00917: missing comma 是因为少逗号
查看>>
策略模式简介
查看>>
UIViewController中loadView的用法(应当注意的几点)
查看>>
POJ NOI0105-42 画矩形
查看>>
Java 数组在内存中的结构
查看>>