宏宏宏

由于 C++ 是静态语言,不能像 js 一样通过函数名字符串来直接执行函数,所以将 messageId 映射到可执行函数的复杂性大大提升;KBEngine 使用了一系列精巧的「宏」来解决这个问题。

为了叙述方便,我把需要通过 messageId 来映射执行的函数称为「协议函数」,以区分普通的函数。

loginapp 的宏,统一在 loginapp_interface_macros.h 中声明,在 loginapp_interface.h 中使用。我把 loginapp_interface.h 进行简化,只保留两个函数:importClientMessage,login。

可以看到,上面截图分为 3 个部分

  • 第 5 、14 行是一个完整的宏
  • 第 8 行是将 importClientMessages 转化为「协议函数」的宏
  • 第 11、12 行是将 login 转化为「协议函数」的宏

namespace LoginappInterface

上图是将本文第一张图 5、14 行宏展开后的代码,这对 BEGIN/END 宏,不光是定义了一个 namespace,更重要的是,声明了 messageHandlers,这里面就存放了所有映射好的「协议函数」

importClientMessages

上图是将本文第一张图中第 8 行展开后的代码,由几个重要的逻辑组成:

  • 30~38 行,声明了一个 MessageHandler 的派生类,这个类的 handle 函数,是个 virtual 函数,执行的就是 Loginapp::importClientMessages。(KBEngine 架构的 loginapp 是单例,所以这里才能这么玩,否则没法对应到想要执行的函数所在对象)
  • 40~45 行,将调用 messangeHandlers.add 函数,完成 messangeId 到 importClientMessagesLoginappMessagehandler0 类对象的映射;
    此后,就可以通过 messageId 找到一个 MessangeHandler 对象指针,然后执行他的 handle 函数,由于这是 virtual 函数,真正执行的就是 importClientMessagesLoginappMessagehandler0 中的 handle 函数,也就是 Loginapp::importClientMessages 函数。
  • 47 行,保存添加过映射关系的 MessangeHandlers;这个主要是为了其他组件调用,如 baseapp 可以通过(*pBundle).newMessage( Loginapp::importClientMessages);  这样的形式调用到这个函数。
  • 49~69 行 importClientMessages 函数的参数对象,ARGS0 表示 importClientMessages 函数不需要参数。

login

上图是将本文第一张图中第 11、12 行展开后的代码,由几个重要的逻辑组成:

  • 28 行,使用 "Loginapp::login" 作为 messageHandlers.pushExposedMessage 的参数,即这个函数将会被导出到客户端,以便客户端调用。
  • 30~68 行,与前面逻辑基本一样,这里不再解释。
  • 主要看两个图的第 37 行的区别,可以看到,LOGINAPP_MESSAGE_DECLARE_ARGS0 宏展开后,这行除了 Channel,没有其他参数;LOGINAPP_MESSAGE_DECLARE_STREAM 宏展开后,除了 Channel,还有一个 MemoryStream 参数,表示这个函数的参数需要从流中读取。

MessageHandlers::add

add 函数,将"Loginapp::login" 形式的函数名转成 messageId,并与 MessageHandler* 建立映射关系。

上图,150~171 行,先通过 FixedMessage 判断("Loginapp::login")函数名是否在 res/server/messages_fixed.xml 中声明过,如果声明过,就取声明的 id 作为 messageId,否则就循环找到一个未被使用的 id。

上图,180 行建立 messageId 到 MessageHandler* 的映射。msgHandlers_ 是一个 map。

上图,50 行,可以看到 FixMessages 加载的配置文件地址,就是我们一直提到的 messages_fixed.xml。

协议执行

通过上面的宏,我们可以看到 messageId -> 「协议函数」的映射关系已经建立起来,那么这个在什么时候回被调用到呢?

上图是 login 函数的调用堆栈,我们来看一下 Loginapp::handleMainTick 函数

可以看到,112 行的参数是 &LoginappInterface::messageHandlers,也就是在宏里已经填好 messageId -> 「协议函数」映射关系的全局变量。

真正的执行逻辑,在 PacketReader::processMessages 函数里。

如上图,先在 81 行取到 messageId,然后在 85 行,在 messageHandlers 里 find (简单的 map 的查找即可)到对应的 MessageHandler,再调用 handle 函数,按我们前面的分析,handle 函数里面会调用到 messageId 对应的 「协议函数」。

至此,整个 loginapp 服务器端协议从构建到解析执行的整个过程解释完成。

后记

KBEngine 支持的几种宏说明

    • LOGINAPP_MESSAGE_DECLARE_ARGS0,  「协议函数」有 0 个参数
    • LOGINAPP_MESSAGE_DECLARE_ARGS1,  「协议函数」有 1 个参数
    • LOGINAPP_MESSAGE_DECLARE_ARGS2,  「协议函数」有 2 个参数
    • LOGINAPP_MESSAGE_DECLARE_STREAM,「协议函数」参数为 MemoryStream
    • LOGINAPP_MESSAGE_EXPOSED,「协议函数」将被暴露给客户端,即会在 importClientMessages 中发送给客户端

KBEngine 服务器端-loginapp-协议构建、解析执行详细介绍的更多相关文章

  1. HTML页面加载和解析流程详细介绍

    浏览器加载和渲染html的顺序 1. IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的. 2. 在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相关联的元 ...

  2. Http协议中Cookie使用详细介绍

    Cookie总是保存在客户端中,按在客户端中的存储位置,可分为内存Cookie和硬盘Cookie.内存Cookie由浏览器维护,保存在内存中,浏览器关闭后就消失了,其存在时间是短暂的.硬盘Cookie ...

  3. https协议了解,以及相关协议的解析

    HTTPS简介 HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版. ...

  4. TCP协议 状态解析和状态统计

    一.三次握手和四次挥手 1.建立连接(三次握手)   (1)服务器会处于listen状态,客户端发送一个带SYN标志的TCP报文到服务器.   (2)服务器端回应客户端的请求,这是三次握手中的第2个报 ...

  5. 【Win10 UWP】URI Scheme(一):Windows Store协议的解析和使用

    协议是Windows Phone和Windows Store应用的一个重要特点,可以做到在不同应用之间进行互相呼起调用.小小协议,学问大着呢.我打算写几篇关于协议在UWP中使用的文章. 这一讲的主要对 ...

  6. 实验六 TLS协议报文解析

    一.实验目的 1.访问一个https://....的网站,捕TLS包并分析报文序列. 2.分析连接建立的完整过程,如:TCP三次握手.SSL安全连接,使用TLS协议连接.协商过程,加密传送的状态.TC ...

  7. 使用 C# 实现 CJ-T188 水表协议和 DL-T645 电表协议的解析与编码

    一.协议的定义 要对某种协议进行编解码操作,就必须知道协议的基本定义,首先我们来看一下 CJ/T188 的数据帧定义(协议定义),了解请求数据与响应数据的基本结构. 1.1 CJ/T188 水表通讯协 ...

  8. 基于部标1078视频协议和苏标Adas协议构建主动安全平台

    苏标本身仍然是基于部标808协议的基础上递增起草的,苏标协议是包容808协议的, 不能脱离808协议而独立存在的, 主要基于<JT/T 796 道路运输车辆卫星定位系统平台技术要求>.&l ...

  9. 转:Http协议中Cookie详细介绍

    Http协议中Cookie详细介绍 Cookie总是保存在客户端中,按在客户端中的存储位置,可分为内存Cookie和硬盘Cookie.内存Cookie由浏览器维护,保存在内存中,浏览器关闭后就消失了, ...

随机推荐

  1. 修改jQuery.validate验证方法和提示信息

    1.添加验证方法 在jquery.validate.js文件中直接添加验证方法,例如: jQuery.validator.addMethod("Specialstring", fu ...

  2.  RabbitMQ3.6.3集群搭建+HAProxy1.6做负载均衡

    目录 目录 1.基本概念 1.1.RabbitMQ集群概述 1.2.软件负载均衡器HAProxy 2.RabbitMQ的配置步骤 2.1.安装 Erlang.RabbitMQ 2.2.修改 /etc/ ...

  3. 在QML应用中实现threading多任务

    在这个样例中,我们将介绍怎样在QML应用中使用QML语言提供的threading功能,实现多任务. 很多其它的阅读在:http://doc.qt.io/qt-5/qtquick-threading-e ...

  4. Java+Oracle实现事务——JDBC事务

    J2EE支持JDBC事务.JTA事务和容器事务事务,这里说一下怎样实现JDBC事务. JDBC事务是由Connection对象所控制的,它提供了两种事务模式:自己主动提交和手动提交,默认是自己主动提交 ...

  5. Android-SQLiteOpenHelper

    Android-SQLiteOpenHelper 一 概念 是对SOLiteDatabase的封装.主要用于建立和版本号控制,方便我们去建立库表结构 二 用法 又一次封装一个MySqliteOpenH ...

  6. [hihoCoder] #1044 : 状态压缩·一

    时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho在兑换到了喜欢的奖品之后,便继续起了他们的美国之行,思来想去,他们决定乘坐火车前往下一座城市——那座城市即将 ...

  7. Latch: Row Cache Objects (One bug?)

    During the performance test, observe the following condition in the database server from EM, And the ...

  8. Storm API,Tutorial,Trident,Windowing BOOKS

    http://storm.apache.org/releases/1.0.0/Trident-API-Overview.html https://stackoverflow.com/questions ...

  9. Mysql 中创建索引和索引的使用问题

    在数据库中合理的使用索引是提升mysql数据库的一种高效和快捷的方式,但是在索引的使用上在我的使用中发现有很多坑,因为自己之前没有认识到,所以来总结一下 索引的介绍 索引是一种特殊的文件,其中包含着对 ...

  10. Filter method example

    The Scala List class filter method implicitly loops over the List/Seq you supply, tests each element ...