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

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); //继续打包

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/444981.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leecode5 最长回文子串

给定一个字符串 s&#xff0c;找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。 示例 1&#xff1a; 输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。 示例 2&#xff1a; 输入: "cbbd" 输出: "bb"…

libxml解析xml文件的一些总结

libxml -- 解析 XML 文档XML 介绍&#xff1a;XML 和 DOMlibxml 介绍 数据类型 — xmlChar数据结构 创建 XML 文档解析 XML 文档修改 xml 文档Xpath — 处理大型 XML 文档libxml2 库函数要注意的函数读取 xml 文件xml 操作基本结构及其指针类型根节点相关函数 创建子节点相关函…

Linux(7)-正则表达式

正则表达式demo1:在某个文件中寻找命令seddemo2:寻找8位电话号码正则表达式&#xff1a;用来描述或者匹配某一系列符合某个句法队则的字符串或者单个字符串。最初正则表达式&#xff0c;出现在自动控制理论和形式化语言理论中。 Linux 中 find grep sed ls命令都支持正则表达式…

服务器端开发的一些建议

摘要: 本文作为游戏服务器端开发的基本大纲&#xff0c;是游戏实践开发中的总结。第一部分专业基础&#xff0c;用于指导招聘和实习考核&#xff0c; 第二部分游戏入门&#xff0c;讲述游戏服务器端开发的基本要点&#xff0c;第三部分服务端架构&#xff0c;介绍架构设计中的一…

leetcode63 不同路径II

一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为“Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为“Finish”&#xff09;。 现在考虑网格中有障碍物。那么从左上角到右下角将…

小谈Online-game服务器端设计(1、2)

谈这个话题之前&#xff0c;首先要让大家知道&#xff0c;什么是服务器。在网络游戏中&#xff0c;服务器所扮演的角色是同步&#xff0c;广播和服务器主动的一些行为&#xff0c;比如说天气&#xff0c;NPC AI之类的&#xff0c;之所以现在的很多网络游戏服务器都需要负担一些…

Linux(8)-Linux下的编程开发-C/C++、PHP、JAVA概述

Linux下的编程开发1.C/C语言开发环境的搭建2.PHP开发环境搭建3.JAVA开发环境搭建1.C/C语言开发环境的搭建 方式1:文本编辑器编译器&#xff08;gcc/g&#xff09; Ubuntu 下常用的文本编辑器&#xff1a; Gedit–语法高亮Vim–vi(无比强大无比难用)的改进。字符界面/图形界面…

leetcode55 跳跃游戏 秒杀所有答案

给定一个非负整数数组&#xff0c;你最初位于数组的第一个位置。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个位置。 示例 1: 输入: [2,3,1,1,4] 输出: true 解释: 我们可以先跳 1 步&#xff0c;从位置 0 到达 位置 1, 然后再从位置 …

小谈Online-game服务器端设计(3)

下面我想来谈谈关于服务器上NPC的设计以及NPC智能等一些方面涉及到的问题。首先&#xff0c;我们需要知道什么是NPC&#xff0c;NPC需要做什么。NPC的全称是&#xff08;Non-Player Character&#xff09;&#xff0c;很显然&#xff0c;他是一个character&#xff0c;但不是玩…

小谈Online-game服务器端设计(4)

在这一章节&#xff0c;我想谈谈关于服务器端的脚本的相关设计。因为在上一章节里面&#xff0c;谈NPC智能相关的时候已经接触到一些脚本相关的东东了。还是先来谈谈脚本的作用吧。   在基于编译的服务器端程序中&#xff0c;是无法在程序的运行过程中构建一些东西的&#xf…

leetcode45 跳跃游戏II 秒杀所有答案

给定一个非负整数数组&#xff0c;你最初位于数组的第一个位置。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 示例: 输入: [2,3,1,1,4] 输出: 2 解释: 跳到最后一个位置的最小跳跃数是 2。 从下标为 …

MachineLearning(7)-决策树基础+sklearn.DecisionTreeClassifier简单实践

sklearn.DecisionTreeClassifier决策树简单使用1.决策树算法基础2.sklearn.DecisionTreeClassifier简单实践2.1 决策树类2.3 决策树构建2.3.1全数据集拟合&#xff0c;决策树可视化2.3.2交叉验证实验2.3.3超参数搜索2.3.4模型保存与导入2.3.5固定随机数种子参考资料1.决策树算法…

游戏服务器体系结构

本文描述了一个我所设计的游戏服务器体系结构,其目的是实现游戏服务器的动态负载平衡,将对象从繁忙的服务器转移到相对空闲的服务器中.设计并没有经过具体的测试与验证,仅仅是将自己目前的一些想法记录下来.随着新构思的出现,可能会有所变化. 以下是服务器的逻辑视图,其中忽略…

游戏服务器架构探讨

要描述一项技术或是一个行业&#xff0c;一般都会从其最古老的历史开始说起&#xff0c;我本也想按着这个套路走&#xff0c;无奈本人乃一八零后小辈&#xff0c;没有经历过那些苦涩的却令人羡慕的单机游戏开发&#xff0c;也没有响当当的拿的出手的优秀作品&#xff0c;所以也…

leetcode72 编辑距离

给定两个单词 word1 和 word2&#xff0c;计算出将 word1 转换成 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作&#xff1a; 插入一个字符 删除一个字符 替换一个字符 示例 1: 输入: word1 "horse", word2 "ros" 输出: 3 解释: ho…

即时通讯系统架构

有过几款IM系统开发经历&#xff0c;目前有一款还在线上跑着。准备简单地介绍一下大型商业应用的IM系统的架构。设计这种架构比较重要的一点是低耦合&#xff0c;把整个系统设计成多个相互分离的子系统。我把整个系统分成下面几个部分&#xff1a;&#xff08;1&#xff09;状态…

leetcode303 区域和检索

给定一个整数数组 nums&#xff0c;求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和&#xff0c;包含 i, j 两点。 示例&#xff1a; 给定 nums [-2, 0, 3, -5, 2, -1]&#xff0c;求和函数为 sumRange() sumRange(0, 2) -> 1 sumRange(2, 5) -> -1 sumRange(0,…

算法(24)-股票买卖

股票买卖1.动态规划框架LeetCode-121 一次买卖LeetCode-122 不限次数LeetCode-309 不限次数冷冻期LeetCode-714 不限次数手续费LeetCode-123 两次买卖LeetCode-188 k次买卖2.贪心特解LeetCode-121 一次买卖LeetCode-122 不限次数解题思路参考buladong解题&#xff0c;详细信息可…

网络游戏的客户端同步问题 .

有关位置同步的方案实际上已经比较成熟&#xff0c;网上也有比较多的资料可供参考。在《带宽限制下的视觉实体属性传播》一文中&#xff0c;作者也简单提到了位置同步方案的构造过程&#xff0c;但涉及到细节的地方没有深入&#xff0c;这里专门针对这一主题做些回顾。 最直接的…

leetcode319 灯泡的开关

初始时有 n 个灯泡关闭。 第 1 轮&#xff0c;你打开所有的灯泡。 第 2 轮&#xff0c;每两个灯泡你关闭一次。 第 3 轮&#xff0c;每三个灯泡切换一次开关&#xff08;如果关闭则开启&#xff0c;如果开启则关闭&#xff09;。第 i 轮&#xff0c;每 i 个灯泡切换一次开关。 …