C++服务器设计(五):多设备类型及消息事件管理
在传统的服务器系统中,服务器仅针对接收到的客户端消息进行解析,并处理后回复响应。在该过程中服务器并不会主动判断客户端类型。但在现实中,往往存在多种类型的客户端设备,比如物联网下的智能家居系统,就存在智能电视、智能灯具、智能空调等,甚至一类客户端也可区分为网页端设备和移动端设备。不同类型的设备的消息处理机制不同,同一类型的网页端和移动端的消息处理也可能存在些许差别。此时服务器就需要对多种类型的设备进行管理。
设备类型机制及消息事件设计
一般而言,服务器检测客户端类型,存在两种方法:
- l 为不同类型的设备分配不同的服务器处理,每种服务器只处理特定的设备消息。
- l 在网络应用层协议中添加额外字段,用于记录设备类型。每次收到消息后将设备类型作为解析协议的一部分进行判断处理。
在方案一中,通过主机或端口号对不同的设备进行了区分,并且不同设备间的消息处理并不重合。但是该方案将会对服务器本身性能和开销造成浪费,且不好协调不同设备间负载问题,同时不同设备间的交互只能通过后台数据库,不够灵巧。最重要的是,会产生大量重复编码问题。
而方案二中,虽然做到了在同一服务器中对不同设备进行处理,但是每次传输均需在网络协议中增加字段,会导致额外的网络带宽开销。同时每次收到消息后均需在业务逻辑中对设备类型再次进行判断,而且不同设备能够处理的消息类型也不尽相同,因此该方案可能使业务逻辑部分代码晦涩复杂,难以做到业务逻辑的分离。
在服务框架中,我们原生加入了对多种类型设备的管理,并引入了设备类型和消息事件的概念。每个设备类型维护不同的消息事件,不同设备类型间能够请求的消息事件又通过设备类型的权限进行管理。且这种类型设备是可以通过多级继承被复用的。
图4-3 多设备类型机制
如图4-3所示,系统默认提供了两种设备类型:临时设备类型(TempNodeType)和登录设备类型(NodeType),它们两者继承于设备类型这个抽象类(ObjectType)。我们可以根据具体的业务需求制定新的设备类型,但是这些新的设备类型必须是基于临时设备类型或登录设备类型的继承和扩展。同时所有创建的设备类型都具有设备类型这个公共父类。
这些系统设备类型和用户定制设备类型一起组成了系统的设备类型树。我们通过具体的设备类型实现,及其组成的设备类型树完成对多类型设备的消息管理。
在该树中,每种设备类型维护对应设备的消息事件和具体的事件处理,同时又能够继承使用属于该类型的父类类型的消息事件及事件处理。即我们创建一个新的设备类型MyNewNodeType,在该类型中维护了MyEvent消息事件,并且这个新设备类型是继承于登录设备类型,那么属于MyNewNodeType设备类型的客户不但能够请求MyEvent的消息事件,还能请求登录设备类型的logout及nodeId等消息事件。但是如果我们再创建一个新的设备类型MyNewNodeType2,同样继承于登录设备类型,作为MyNewNodeType的兄弟类型,此时两个兄弟类型间的消息事件并不能被共享。
可以通过一个很简单的例子来描述这种关系:我们创建一个名为灯的设备类型,并为灯设备类型创建相关消息事件。然后创建继承于灯设备类型的日光灯设备类型,再创建同样继承于灯设备类型的智能灯设备类型。此时如果有一个连接客户的设备类型为日光灯,显然它能够请求日光灯的消息事件。由于日光灯设备继承于灯设备,因此它也能够请求灯的消息事件。但它毕竟是日光灯不是智能灯,因此作为兄弟类型的智能灯的消息类型它就无权请求了。
设备类型作为一切新设备类型的抽象公共父类,为其它类型制定了错误类型处理和解析错误等公共接口,但是它最主要的作用是维护了一个哈希表结构,类似于unordered_map<string, RegisterMessageCallback>。对于每种具体的设备类型,均会在该表中注册该设备能够处理的消息事件与具体消息事件回调处理函数的映射。因此当我们确立了设备的类型,只需根据具体消息事件就可在该设备类型对象中查找到对应的消息处理函数,而具体消息处理函数的实现往往存在于该设备类型类,或在该类的父级类中。
临时设备类型由TempNodeType类实现,它是所有连接客户的默认设备类型。任何连接了服务器系统的客户,无需登录操作,即可请求在临时设备类型中注册的所有消息类型事件。
在临时设备类型的默认实现中,注册了login(登录事件)、devType(设备类型事件)、isLogin(登录状态事件)这三个消息事件。任何连接都可以请求这类消息事件。其中login消息事件涉及到登录生命周期的管理,为保留事件,系统用户无法对其事件处理函数进行重写操作。其他的消息事件都可以由用户根据实际业务需求来创建新的临时设备类型进行修改或重写,甚至添加新的临时消息事件。
我们可以创建一个MyTempNodeType设备类型,它继承于临时设备类型,因此它保留了临时设备类型所拥有的三个消息事件,并且还新添加了Time作为请求当前时间的消息事件。由于Time作为临时设备类型的消息事件,因此任何连接客户同样无需登录便可请求该Time事件。
图4-4 临时设备类型添加新消息事件
在图4-4中,我们通过临时设备类型的isLogin消息事件获得该连接的登录状态。此时该连接尚未登录,默认是临时设备类型,并且我们能够很顺利的获取新添加的Time消息事件的回复数据。
登录设备类型由NodeType类实现,当连接客户通过临时设备类型的login事件请求登录成功后,将默认成为登录设备类型。在登录设备类型及其子类中,所有属于该设备类型的消息事件都必须登录成功后才能获得请求的权限。
在登录设备类型的默认实现中,注册了logout(注销事件)、nodeId(设备ID事件)这两个消息事件。这两个消息事件必须连接客户成功登录后才能请求系统响应。其中logout涉及到登录生命周期管理,为保留事件,无法对其进行重写操作。其他消息事件都可以由用户根据实际业务需求通过创建继承于NodeType的新的设备类型进行修改或重写,或者添加新的消息事件。
NodeType并非继承于TempNodeType,这两种类型互为兄弟类型。当某个连接客户登录成功后由临时设备类型更改为登录设备类型时,根据上文结论,该客户连接应该无法再调用临时设备类型的消息事件。但是临时设备类型的定义就是任何连接都可以请求这类型的消息事件,因此系统通过某种机制,保证了登录设备类型依旧能够请求临时设备类型的消息事件。
我们创建一个MyNewNodeType的设备类型,它继承于登录设备类型,同时保留了临时设备类型及登录设备类型的消息事件请求权限。同时我们为其添加一个MyEvent的消息事件。此时由于并没有其他设备类型将MyNewNodeType作为父类,因此只有其本身的设备类型能够请求MyEvent的消息事件,其他设备类型均无请求的权限。
图4-5 MyNewNodeType设备类型的消息事件
在图4-5中,在客户连接刚建立的时候,我们请求myEvent事件。此时由于连接并没有登录操作,因此该客户设备类型默认属于临时设备类型,并没有请求myEvent消息事件的权限,因此服务器系统返回错误类型的请求。接着我们进行登录操作,并设置该客户类型为MyNewNodeType设备类型。我们再对myEvent消息事件进行请求,这时我们就能成功收到了服务器系统的正确回复。我们再请求临时事件类型的devType消息事件,服务器系统返回当前设备类型为my_node_type。这也验证了之前的论述,表明虽然当前设备类型继承于登录设备类型,但依旧能够请求临时事件类型的消息事件。
多设备类型及消息事件原理
如图4-6所示,设备类型机制及每个设备类型对应的消息事件机制是由两级映射的哈希表实现的。
图4-6 二级哈希表实现的消息事件
在第一层哈希表中维护了系统管理的不同设备类型对应对象,且为这些不同对象设置了不同字符串作为键进行映射。
在第一层哈希表中维护了系统管理的不同设备类型对应对象,且为这些不同对象设置了不同字符串作为键进行映射。不同连接客户对象会保存对应设备类型对象的字符串,表明当前连接客户具体的设备类型。连接对象默认保存的是临时设备类型的字符串,同时可以在登录操作中对该连接的具体设备类型进行更改。通过该字符串,连接对象可以很方便的获取对应设备类型的对象。
同时在每个设备类型对象中,同样维护了一个哈希表结构。在该表中维护了每个消息事件类型与对应事件处理函数,因此如果某个设备类型需要添加新的消息事件,就是将该消息事件及事件处理函数添加到该设备类型的该哈希表中即可。
在图4-6中,临时设备类型对象的哈希表中维护了devType、login和isLogin的消息事件处理函数;登录设备类型对象的哈希表中维护了nodeId和logout的消息事件处理函数;而在自己创建的MyNewNodeType类型对象的哈希表中,不但维护了myEvent的消息事件处理,还添加了登录设备类型对象的nodeId和logout的消息事件。因为每个设备类型在添加属于自己的消息事件的同时也会添加自己父类设备类型维护的消息事件。这也解释了为什么属于MyNewNodeType设备类型的连接对象能够请求登录设备类型维护的消息事件。
图4-7 消息事件的处理机制
如图4-7所示,我们可以从连接对象的角度对设备类型机制进行分析。当某个客户与服务器系统建立连接后,将会被默认分配为临时设备类型,当然也可以在登录操作中给连接设置具体设备类型。然后该连接进入等待新消息的状态。当该连接收到一条新的消息后,系统将会通过第一级哈希表根据该连接对象的设备类型分配具体的设备类型对象,比如临时设备类型对象或登录设备类型对象。然后系统再在具体的设备类型对象中根据收到的消息事件类型,获得具体该消息的处理函数。最后系统将新消息和该连接对象作为参数传送给该消息的处理函数,执行回调处理。
但是很有可能出现客户发送过来的消息事件并未在该设备类型中注册的问题。如果系统在连接对应的设备类型中找不到该消息事件,将会继续在临时设备类型中继续寻找该消息事件。如果临时设备类型中找到了该消息事件,则同样对该处理函数进行传参和回调。这也是之前登录设备类型并未继承临时设备类型却也能请求临时设备类型的消息事件的原因。如果在临时设备类型中依旧找不到对应的消息事件,则系统将会向连接客户返回请求事件类型错误的提示。
C++服务器设计(五):多设备类型及消息事件管理的更多相关文章
- C++服务器设计(七):聊天系统服务端实现
在之前的章节中,我们对服务端系统的设计实现原理进行了剖析,在这一章中,我们将对服务端框架进行实际运用,实现一款运行于内网环境的聊天系统.该聊天系统由客户端与服务器两部分组成,同时服务端通过数据库维护用 ...
- C++服务器设计(零):总体设计
这个系列把毕业论文的部分贴了出来,以作保存留念.整个系列分为三大部分,其中第一章到第三章是介绍服务器的系统层设计,设计思路参考了libevent和muduo等开源代码的实现:第四章到第六章是介绍服务器 ...
- C++服务器设计(六):设备连接的生命周期管理
生命周期介绍 每一个服务器系统的新连接从建立开始时,均会经历多个阶段.比如连接的建立,登录的验证,退出前的资源释放等.同时在具体的消息处理中,还会遇到不可识别的消息事件,或者消息处理时出现数据错误等. ...
- FPS游戏服务器设计的问题 【转】
一.追溯 去gameloft笔试,有一个题目是说: 叫你去设计一个FPS(第一人称射击游戏),你是要用TCP呢还是要用UDP,说明理由 . 二.学习 这是两篇网上找到的文章,写非常不错. 当时笔试的时 ...
- (转)FPS游戏服务器设计的问题
FPS游戏服务器设计的问题出处:http://www.byteedu.com/thread-20-1-1.html一.追溯 去gameloft笔试,有一个题目是说: 叫你去设计一个FPS(第一人称射击 ...
- 基于内存,redis,mysql的高速游戏数据服务器设计架构
转载请注明出处,欢迎大家批评指正 1.数据服务器详细设计 数据服务器在设计上采用三个层次的数据同步,实现玩家数据的高速获取和修改. 数据层次上分为:内存数据,redis数据,mysql数据 设计目的: ...
- HTML5 学习总结(五)——WebSocket与消息推送
B/S结构的软件项目中有时客户端需要实时的获得服务器消息,但默认HTTP协议只支持请求响应模式,这样做可以简化Web服务器,减少服务器的负担,加快响应速度,因为服务器不需要与客户端长时间建立一个通信链 ...
- HTML5 学习笔记(五)——WebSocket与消息推送
B/S结构的软件项目中有时客户端需要实时的获得服务器消息,但默认HTTP协议只支持请求响应模式,这样做可以简化Web服务器,减少服务器的负担,加快响应速度,因为服务器不需要与客户端长时间建立一个通信链 ...
- 游戏服务器设计之NPC系统
游戏服务器设计之NPC系统 简介 NPC系统是游戏中非常重要的系统,设计的好坏很大程度上影响游戏的体验.NPC在游戏中有如下作用: 引导玩家体验游戏内容,一般游戏内有很多主线.支线任务,而任务的介绍. ...
随机推荐
- CSS凹型导航按钮
一般需求,圆角看起来更加舒服,但是下面直角略显生硬 于是设计师有了下面的需求,下面加上小凹型: 凹型?凹型?凹型?有点变态,这怎么实现........... 图片肯定是最先考虑到的,CSS实现有貌似有 ...
- MySQL数据库恢复(使用mysqlbinlog命令)
binlog是通过记录二进制文件方式来备份数据,然后在从二进制文件将数据恢复到某一时段或某一操作点. 1:开启binlog日志记录 修改mysql配置文件mysql.ini,在[mysqld]节点下添 ...
- HTML5 video 事件
1.获取视频时间长度 当视频载入video后,使用 onloadedmetadata 事件获取视频的时间长度. video.onloadedmetadata = function () { var v ...
- php给一张图片加上水印效果
<?php /** * 功能:给一张图片加上水印效果 * $i 要加水印效果的图片 * $t 水印文字 * $size 文字大小 * $pos 水印的位置 * $color 文字的颜色 * $f ...
- 【转】linux之自建yum仓库
原链接:http://www.live-in.org/archives/1410.html 平时使用yum方式安装更新软件,可以自建一个yum源,同步官方更新源,这样如果本地有机器要升级的话就可以直接 ...
- 函数reduce,lambda,filter
#比较时间差,判断执行有时. import time def panduan(x): if x%5==0 and x%7==0: return True else: return False star ...
- 文件系统:介绍一个高大上的东西 - 零基础入门学习Python030
文件系统:介绍一个高大上的东西 让编程改变世界 Change the world by program 接下来我们会介绍跟Python的文件相关的一些十分有用的模块.模块是什么?不知大家对以下代码还有 ...
- HBase架构深度解析
原文出处: DLevin(@雪地脚印_) 前记 公司内部使用的是MapR版本的Hadoop生态系统,因而从MapR的官网看到了这篇文文章:An In-Depth Look at the HBase A ...
- html 作业1
<body bgcolor="#000000" topmargin="200px" leftmargin="200px" text=& ...
- 本地windows主机无法访问虚拟机里主机解决办法
一:设置虚拟机里IP,使其与本地计算机IP在同一网段 本地计算机网络IP设置如下: 虚拟机里ip为192.168.1.9 设置IP步骤请参考:Linux里如何设置IP(RED HAT) 二:将虚拟机网 ...