本文由逍遥子撰写,转发请标注原址:算法
http://write.blog.csdn.NET/postedit/21462255数据结构
1、 Mosquito的核心功能分析函数
3.一、订阅树post
Mosquitto经过订阅树的方式来管理全部的topic以及客户端的订阅关系,它首先将全部的topic按照/分割并组织成一棵树结构,从根节点到树中的每一个节点即组成该节点所对应的一个topic,每一个topic都保存一个订阅列表,该订阅列表中保存了全部订阅当前topic的客户端信息。例若有以下订阅关系:ui
客户端a1,a2,a3订阅了topic:A1/B1/C1.net
客户端b1,b2订阅了topic:A2/B2/C2blog
客户端c1,c2订阅了topic:A1/B1/C3递归
客户端d1订阅了topic:A2/B3队列
则在mosquitto程序中须要先将topic按照/进行分割,而后将分割后的topic片断组织成订阅树,上述订阅树的示意图为图3-1:字符串
图3-1 订阅树示意图
Mosquitto程序在实现中根据topic消息的性质将订阅树分为两颗子树:业务子树和系统子树;mosquitto程序中将topic分为两种类型来处理:系统topic和业务topic,前者主要用于发布和维护mosquitto内部的系统消息,后者的topic是用户订阅的业务topic,作这种区分的缘由是由于这两种的类型的topic性质和实现方式上有许多差异,这种差异主要体如今如下4点:
1)生存周期不一样,系统topic不管是否有用户订阅都会存在与订阅树中,而业务topic必须有客户端订阅才能存在(除非其消息字段retain设置为1);
2)建立方式不一样,系统topic在消息发布时进行建立,业务topic便可以在订阅时建立也能够在消息发布时建立(此时须要该消息retain字段设置为1);
3)消息保存方式不一样,凡是发布到系统topic的消息都会被保存下来,业务消息将直接挂到订阅列表的各context的消息队列中,若是没有链接订阅或未设置其retain字段,消息将不会被保存下来;
消息的retain字段是否被设置在函数mqtt3_handle_publish进行检查,在该函数中有以下代码:
retain = (header & 0x01);
该代码可获取消息头部的第一个bit位,在mqtt3.1协议中,该为用于表示消息的类型是否为retain。
订阅树在程序中的采用孩子—兄弟链表法来表示。其主要涉及的数据结构是:
struct _mosquitto_subhier
struct _mosquitto_subleaf
3.1.一、订阅树的搭建
一、建立订阅树
mosquitto程序启动时将建立订阅树,该过程将建立三个节点:订阅树总根节点、业务子树根节点和系统子树根节点,这两个子树根节点做为订阅树总根节点的两个子节点,其中订阅树总根节点和业务子树的根节点中topic成员的值为空字符串,而系统子树根节点中保存的值为“$SYS”,如图3-2所示。
图3-2 订阅树的建立
订阅树的建立主要在文件database.c中mqtt3_db_open函数里实现。订阅树中节点的数据结构为struct _mosquitto_subhier,订阅树采用“孩子—兄弟”链表法保存。
二、搭建订阅树
在订阅树中,系统子树与业务子树的搭建过程不同,系统子树是在系统消息发布时建立,而业务子树建立过程便可以在消息发布时建立也能够在客户端订阅时才建立。
1)系统子树搭建过程
Mosquitto中,系统子树在发布系统消息时,自动检测topic片断是否存在,若是不存在则在系统topic上建立节点以搭建订阅树。例如,mosquitto程序启动时,将首先向系统topic:$SYS/broker/version发送一条版本消息“mosquittoversion 1.2”,此时订阅树的系统子树只有一个根节点,如图3.2所示,其搭建过程以下:
(1)将topic按照”/”分红topic片断,系统Topic:$SYS/broker/version将被分割为$SYS、broker、version三部分。
(2)根据第一个topic片断“$SYS”遍历订阅树的子节点找到系统子树的根节点。
(3)根据topic下一个片断“broker”查找系统子树;此时系统子树中不存在topic片断“broker”的节点,则为订阅树产生一个节点,其数据结构为:struct_mosquitto_subhier。此时订阅树由图3-2变为图3-3所示:
图3-3添加broker节点以后的订阅树
上述过程在函数mqtt3_db_messages_queue中调用函数_sub_add来完成。
(4)依此处理topic剩下的片断,在系统子树中添加topic片断“version”,该过程经过递归调用函数_sub_add来完成。添加完“version”片断以后的订阅树如图3-4所示。
图3-4添加version以后的订阅树
系统子树搭建过程当中,所用到的函数调用关系以下图3-5所示
图3-5 系统子树搭建的函数调用关系
2) 业务子树搭建过程
业务子树的搭建分为两种类型:一种在订阅时建立;一种是在消息发布时建立,这种方式与系统topic的建立过程同样,所以下面的内容将主要描述前一种方式。
在mosquitto程序运行期间,收到一条客户端的订阅请求后将调用函数mqtt3_sub_add将该客户端挂到对应的业务子树节点的订阅列表中,此时,若是所订阅树中不存在客户端所订阅的topic,则会自动为之添加相应的节点,此过程即为订阅树的业务子树搭建过程。例如,在mosquitto程序启动时(此时订阅树如图3-2所示)客户端订阅了topic:year/month/events,业务子树的搭建过程为:
(1)将topic按照”/”分红topic片断,topic:year/month/event将被分割成:year、month、events三个topic片断;此过程在函数mqtt3_sub_add中完成。
(2)遍历订阅树的第一级子节点,以查找业务子树的根节点,找到业务子树的根节点以后进行后续的处理。此过程在函数mqtt3_sub_add中完成。
(3)依此处理三级topic片断:year、month、events,此过程与系统topic的添加过程类似,若是订阅树中不某个存在topic片断,则为订阅树添加此节点,添加完成以后的订阅树如图3-6:本条功能在函数_sub_add中完成。
图3-6 添加业务topic以后的订阅树
业务子树搭建过程当中,所用到的函数调用关系以下图3-7所示
图3-7 业务子树搭建的函数调用关系