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所示,我们可以从连接对象的角度对设备类型机制进行分析。当某个客户与服务器系统建立连接后,将会被默认分配为临时设备类型,当然也可以在登录操作中给连接设置具体设备类型。然后该连接进入等待新消息的状态。当该连接收到一条新的消息后,系统将会通过第一级哈希表根据该连接对象的设备类型分配具体的设备类型对象,比如临时设备类型对象或登录设备类型对象。然后系统再在具体的设备类型对象中根据收到的消息事件类型,获得具体该消息的处理函数。最后系统将新消息和该连接对象作为参数传送给该消息的处理函数,执行回调处理。

  但是很有可能出现客户发送过来的消息事件并未在该设备类型中注册的问题。如果系统在连接对应的设备类型中找不到该消息事件,将会继续在临时设备类型中继续寻找该消息事件。如果临时设备类型中找到了该消息事件,则同样对该处理函数进行传参和回调。这也是之前登录设备类型并未继承临时设备类型却也能请求临时设备类型的消息事件的原因。如果在临时设备类型中依旧找不到对应的消息事件,则系统将会向连接客户返回请求事件类型错误的提示。

原文地址:https://www.cnblogs.com/moyangvip/p/5586033.html