Online服务器的第三部分就是数据层,send_request_to_db开始了数据层的处理逻辑:

int send_request_to_db(int cmd, sprite_t* p, int body_len, const void* body_buf, uint32_t id);

在该函数里首先以懒惰的方式连接数据库服务器,获取一个网络连接,注意参数p,如果该参数为空,那么就说明不关心数据库代理服务器返回的数据:

if (!p) pkg->seq = 0;

else pkg->seq = (sprite_fd (p) << 16) | p->waitcmd;

注意以上的代码,如果不关心返回数据,那么直接将pkg的seq字段设置为0即可,如果关心返回结果,就需要用这个seq字段保存一些信息了,比如当前处理的业务协议是什么,还有就是这个客户端实体p的对应的父进程的套结字描述符是多少,然后将这个pkg连同信息体一起发送给数据库代理服务器,等到代理服务器返回的时候会进入worker_handle_net中处理,注意handle_process函数里关于子进程的执行路线:

int worker_handle_net(int fd, void* buf, int len, sprite_t** u)

{

assert( len >= sizeof(server_proto_t) );

server_proto_t* dbpkg = buf;

} else if (fd == proxysvr_fd) {

return handle_db_return(fd, dbpkg, len, u);

}

return 0;

}

执行流进入handle_db_return:

static int handle_db_return(int fd, server_proto_t* dbpkg, int len, sprite_t** sp)

{

int waitcmd = dbpkg->seq & 0xFFFF;

int conn = dbpkg->seq >> 16;

if (!dbpkg->seq || waitcmd == PROTO_LOGOUT) //如果不关心返回数据,则在send_request_to_db就已经将seq设置成了0,于是直接返回,否则取出保存的fd信息

return 0;

if (!(*sp = get_sprite_by_fd(conn)) || (*sp)->waitcmd != waitcmd) {

//出错

}

int err = -1;

switch (dbpkg->ret) {

case 0:

break; //成功

…//处理各种错误码

在处理各种错误码的时候可以根据不同的协议进行不同的动作,协议保存在sprite_t的waitcmd字段中。在没有错误的情况下就会进入数据层的回调处理:

#define DO_MESSAGE(n, func) /

case n: err = func(*sp, dbpkg->id, dbpkg->body, len - sizeof (server_proto_t)); break

和协议层的处理十分类似,也是回调函数的形式,只不过这里没有提前注册,只是简单的封装了一下switch-case开关。对于前面的例子就是:

DO_MESSAGE(SVR_PROTO_RACE_SIGN, race_sign_callback);

int race_sign_callback(sprite_t* p, uint32_t id, char* buf, int len)

{

uint32_t itms[2] = {12999, 12998};

CHECK_BODY_LEN(len, 4);

p->teaminfo.team = *(uint32_t*)buf;

if (p->teaminfo.team != 1 && p->teaminfo.team != 2) {

ERROR_RETURN(("race failed/t[%u %u]", p->id, p->teaminfo.team), -1);

}

db_single_item_op(0, p->id, itms[p->teaminfo.team - 1], 1, 1);

response_proto_uint32(p, p->waitcmd, p->teaminfo.team, 0); //一定要向客户端回应,否则客户端将挂起

return 0;

}

一定要返回给客户端一个数据,因为客户端和服务器是一问多答式的,服务器的应答可以分好几部分来返回给客户端,比如一共需要返回5次,那么在这5次全部返回之间,服务器是不接受同一个客户端的别的请求的,必然是一问多答,而不是多问多答。注意send_to_self的最后一个参数的意义:

int send_to_self(sprite_t *p, uint8_t *buffer, int len, int completed)

如果completed为1,那么在该函数中就会将p的waitcmd设置为0,代表当前的这个协议已经处理完毕,online可以处理下一个协议请求了,否则就意味着当前的协议还没有处理完毕,online不接收新的协议请求,这个在dispatch_protocol中体现:

if (p->waitcmd != 0) {

send_to_self_error(p, cmd, -ERR_system_busy, 0);

WARN_RETURN(("wait for cmd=%d, id=%u, new cmd=%d", p->waitcmd, p->id, cmd), 0);

}

Onlien服务器通过这种方式解决了一些同步问题,一条协议没有处理完是不接受另外的协议的。关于数据同步,其实online服务器使用了另外的方案,并没有使用传统的锁之类的,而是使用了一个全局变量,并且onlien中不存在线程的概念,因此基本不存在处理数据时的数据共享访问,因此一个子进程同时只能处理一个客户的请求,因此全局变量msg被定义出来,用来保存需要返回给客户端的消息,注意包含协议头部。最后的问题就是请求和回应时的数据组织了,对于请求包,用UNPKG_UINT32来解析包的内容,j是游标号,需要在外部定义然后在外部使用,初始值就是需要开始解析的位置距离包(也就是b)开始的以字节为单位的大小,比如一个buffer,协议头为8个字节,我们需要解析协议体,也就是有效载荷,那么我们需要如下代码:

Int j = 8, v = count;

UNPKG_UINT32(buffer, count, j);

只要看看下面的定义就一目了然了:

#define UNPKG_UINT32(b, v, j) /

do { /

(v) = ntohl( *(uint32_t*)((b)+(j)) ); (j) += 4; /

} while (0)

对于封包同样的方式,只是将流程反过来了:

#define PKG_UINT32(b, v, j) /

do { /

*(uint32_t*)((b)+(j)) = htonl(v); (j) += 4; /

} while (0)

在往客户端返回包的时候,封包的过程就是用的PKG_UINT32,如果连包头一起封装,那么就是下面的流程:

int j = sizeof(protocol_t); //空余了包头的空间

PKG_UINT32(msg, intbuf1, j); //从包头的下一个字节开始打包

PKG_UINT32(msg, intbuf2, j); //继续打包

关于事件处理器是和数据库相关的处理器并列的逻辑处理器,这个处理器主要处理系统事件的,由于事件分为好多种,如果写进一个协议处理回调函数会使得这个函数的职责太多,不明确,如果每个事件作为一个协议封装,那么又会使整个协议处理器的架构主次不分,很含糊,因此就专门为事件处理单独列一个更低级的层次进行处理,也就是和协议处理不在一个层次,而专门为所有事件单独封装一个协议处理回调函数,然后为了协议处理的清晰,在这个协议处理钩子中将事件分发到不同的事件处理器中,如此一来,事件处理就单独成了一个子层次

online游戏服务器架构--数据库及事件相关 .相关推荐

  1. 游戏服务器架构与性能优化相关的一些问题

    问题一:游戏在线人数过多出现卡顿,是怎么造成的?服务器硬件还是网络或是其他原因? 答:原因有很多种,要根据不同情况进行分析: 1.手机或者电脑的性能不好,客户端自己卡顿,表现在fps很低: 2.网络本 ...

  2. 百万用户同时在线游戏服务器架构实现.doc 基于epoll 通信模型

    http://wenku.baidu.com/view/02033d0af78a6529647d53fc.html 百万用户在线网络游戏服务器架构实现 一.            前言 事实上100万 ...

  3. 游戏服务器架构设计的一些整理

    一.前言 没有最好的架构,只有最适合自身业务的架构. 首先我们应该确定的是大的架构方向:分布式 / 单应用+负载均衡,这两种架构设计直接影响后续的网络层.缓存层.数据层.业务层的设计.笔者这两种架构的 ...

  4. 百万用户级游戏服务器架构设计

    百万用户级游戏服务器架构设计 服务器结构探讨 -- 最简单的结构 所谓服务器结构,也就是如何将服务器各部分合理地安排,以实现最初的功能需求.所以,结构本无所谓正确与错误:当然,优秀的结构更有助于系统的 ...

  5. [转贴] 游戏服务器架构

    这里讨论的游戏服务器架构大概是目前国内乃至世界上的网游通用的一种架构了: 有段时间没有研究技术了,这次正好看到了新版的mangos,较之以前我看的版本有了比较大的完善,于是再次浏览了下他的代码,也借此 ...

  6. 【转】游戏服务器架构探讨

    这里讨论的游戏服务器架构大概是目前国内乃至世界上的网游通用的一种架构了: http://bbs.gameres.com/showthread.asp?threadid=93775 作者:qinglan ...

  7. c++分布式游戏服务器架构设计

    文章目录 一.游戏服务器架构介绍 1.1 游戏服务器介绍 1.2 服务的划分 1.3 服务与服务之间的通讯 二.单个服务的并发 2.1 多线程并发问题 2.2 使用单线程协程并发 三.数据库的选择与使 ...

  8. 如何设计大型游戏服务器架构?

    一.游戏服务器特征 游戏服务器,是一个会长期运行程序,并且它还要服务于多个不定时,不定点的网络请求.所以这类服务的特点是要特别关注稳定性和性能.这类程序如果需要多个协作来提高承载能力,则还要关注部署和 ...

  9. 游戏服务器架构演进(完整版)

    http://gad.qq.com/article/detail/32876 ​这是王者荣耀技术分析系列第三篇,有兴趣请持续关注我的blog和公众号. 1.像<王者荣耀>一样红过 2.从& ...

最新文章

  1. 新手理解的JS原型链
  2. NSTableView使用笔记(一)
  3. php判断base64编码,检测PHP中的base64编码?
  4. 下列不是c语言浮点常量的是,C语言二级题库.doc
  5. 阿里巴巴开源通用机器学习算法平台Alink
  6. 【clickhouse】clickhouse 表引擎之 null
  7. jsp校园二手交易平台的设计答辩PPT模板
  8. VMwar配置静态ip
  9. Random生成伪随机数
  10. 谁分配谁释放HEAP
  11. Java开发五年,java面试未来职业规划
  12. 偏最小二乘回归(PLSR)算法原理
  13. Tuxera NTFS for Mac(mac读写NTFS磁盘工具)
  14. mysql 免安装 密码_mysql免安装版配置与修改密码
  15. 计算机产生背景和编程语言分类
  16. 2022年中国国债收益率走势(附一年期、二年期、三年期、七年期、十年期、二十年期、三十年期)[图
  17. 四通一达归于阿里后就涨价,证明资本的目标就是以垄断攫取利润
  18. SCAU高级语言程序设计OJ
  19. java web 实践项目(搭建web留言系统)
  20. 你需要掌握的 Koa 洋葱模型和中间件

热门文章

  1. mysql数据库开启远程连接_安装MySQL数据库并开启远程访问
  2. 利用envi对landsat8数据进行处理
  3. 在Sharepoint 2010中使用ReportViewer控件展示RDLC报表
  4. 一步步编写操作系统 07 开机启动bios
  5. 正三角形旋转一周得到的图形是_七年级数学上册_第一章《基本的几何图形》_知识点...
  6. LNK2019:无法解析的外部符号
  7. 计算机组成与系统原理中的节拍是什么意思?
  8. 我的第一个Python程序(简单的用户名密码登录程序)
  9. 永远年轻,永远热泪盈眶----致所有奋斗的ACMer
  10. 【HDU - 1031 】Design T-Shirt(水题 排序)