mosquitto源码分析(4)

2019-03-05 22:56

其中,int fd是文件描述符,在mosquitto的中主要指socket描述符(在linux一切I/O皆文件的工作模式下,socket描述符与普通文件描述被同等对待);events成员用于告诉内核,我们对描述符fd关心什么;revents用以说明该描述符fd发生了什么事情。

另外,使用poll是需要注意,每次调用poll函数之前都要重新设置一下各描述符的状态。

3.2.2、mosquitto的消息机制

Mosquito在工作过程中用poll机制检测监听所有socket以判断其是否有数据发送或接收,mosquitto所监听的socket按照其功能分为监听socket和业务socket,监听socket主要负责监听各客户端的连接;业务socket主要负责mosquitto服务器与各客户端之间的数据收发;一旦mosquitto服务器从监听socket中接收到一个客户端连接进来,将立即为该客户端创建一个业务socket,负责后续mosquitto服务器与该客户端的所有数据收发。

Mosquitto中对poll操作主要在/ mosquitto-1.2/src loop.c文件的mosquitto_main_loop函数中,在该函数中将按照以下步骤循环处理所有的socket:

1) 创建pollfd结构体数组pollfds

2) 将要监听的socket放入pollfds

3) 使用poll函数查询pollfds数组中各描述符的状态;

4) Poll函数返回之后,遍历pollfds数组对其中就绪的socket描述符进行处理。

mosquitto_main_loop函数不仅涉及对poll函数的相关处理,它也是整个消息处理架构的实现之处,该函数对消息处理的流程如下图3-11

图3-11 mosquitto函数的消息业务处理流程

在上述消息处理过程中,poll函数返回之后,pollfds数组中socket描述的pollfd结构的revents成员就会置为相应的就绪状态,因此只要循环扫描该数组,即可根据对应socket完成相应的读写操作。

在mosquitto程序中,poll函数所监听的socket有两种类型:监听socket和业务socket,其对应的结果处理函数就分两个过程来完成。

监听socket处理过程主要在mosquitto_main_loop函数中直接完成,它主要完成的工作是处理每个监听socket,如果有客户端连接进入时,为之创建一个业务socket和一个对应的context结构体,context结构体描述了该连接的所有信息,因此在mosquitto程序中,一个context就表示一个客户端连接。

业务socket的处理主要通过loop_handle_reads_writes函数完成,在该函数中将循环检查所有的context,如果该context对应的socket有数据写入则调用函数_mosquitto_packet_write进行写操作;如果对应的socket有数据要读取,则调用函数

_mosquitto_packet_read完成socket的读取和处理,其中读取消息的处理在函数mqtt3_packet_handle中完成,在该函数中将根据不同的消息类型,调用不同的处理函数,业务socket的消息读取及其处理的函数调用关系如图3-12所示。

图3-12 消息读取和处理函数调用关系

3.2.3、mosquitto的消息收发

本文中,消息的收发是相对mosquitto服务器而言,Mosquito的消息的收发主要包括以下两个过程:

1、 消息接收过程是mosquitto服务器接收到客户端向某个主体发布一条消息。

2、 消息发送过程是指Mosquito服务器将消息发送给订阅客户端。

在mosquitto的程序实现中,上述两个过程是分开实现和操作的:

消息接收过程,Mosquitto服务器端在收到客户端向某个主体发送的消息之后,会遍历订阅树,找到该主体的订阅列表,然后将消息挂到订阅列表中每个订阅者的消息队列中。需注意的是,此时消息并实际发送给订阅客户端,只是被挂在了mosquitto服务程序中该客户端对应的context结构中所定义的消息队列中;上述工作过程涉及到的主要函数及其调用过程可参照图3-9所示。

Mosquitto的消息的组装发送过程集中在函数mqtt3_db_message_write中完成,其函数调用关系如下图3-13所示。

图3-13 消息组装和发送函数调用关系

3.3、mosquitto的ping/pong功能

1、为什么mosquitto要引人ping/pong的操作

根据tcp/ip协议的描述,tcp连接建立之后,如果双方没有通信,连接可以一直保存下去,假如中间路由器崩溃或者中间的某条线路断开,只要两端的主机没有被重启,连接就一直被保持着。尽管tcp协议规范中未做次要求,但是在很多tcp协议的实际实现中,却提供了保活定时器的功能,保活定时器一般配置的时间是2小时。在实际的服务器程序开发过程中,2个小时的连接断开的时间太长。因此,很多服务器程序都在上层自己提供保活功能,也就是服务器程序开发过程中经常提到的:心跳连接或ping/pong消息等功能。

2、mosquitto的ping/pong功能描述

在mosquitto中,提供了ping/pong功能来判断连接异常断开的情况,并通过keepalive的参数来控制检查时间,一般客户端需要在keepalive时间内向服务器发送一条消息,表明自己还存在,服务器会周期检查与客户端建立起的每一个连接,如果某个连接在keepalive*1.5的时间内没有收到过消息,则认为这个连接就失效了,于是服务器将主动断开这个超时的连接。

Mosquitto中,每个客户端所对应的context中有两个变量last_msg_out和last_msg_in,分别用于记录该context上次发送和接收消息的时间,然后在mosquitto_main_loop(位于文件loop.c)中每次循环都对每个context的所记录的消息收发时间进行检查,如果超过设定的keepalive时间的1.5倍则断开此客户端的连接,因此,如果mosquitto客户端在keepalive时间内与mosquitto服务器之间存在任何通信(无论是普通消息还是ping/pong消息,都是如此),mosquitto就认为该客户端是连接状态良好的。

3、ping/pong功能对mosquitto性能产生的潜在影响

Mosquitto以keepalive*1.5时间作为判断客户端连接是否异常断开的时间界限,这里keepalive的值对mosquitto的性能会产生较大影响,此值过大,可能无法及时判断处异常的发生;此值过小,不仅浪费网络带宽,还可能造成误判,例如客户端与服务器之间tcp连接上的某个服务器异常重启,可能会被服务器误判为tcp连接断开了。此值需根据实际情况分析后确定。

四、 Mosquito的辅助功能介绍

Mosquitto代码的辅助功能主要包括:log输出功能、配置参数管理功能和内存封装的功能,这三个功能虽不是mosquitto的核心模块,但是却在其源码实现中经常遇到,它们的实现给mosquitto的代码开发带来了很大的方便。

4.1、log输出功能

Mosquitto日志输出功能的实现代码主要在文件/ mosquitto-1.2/src/logging.c中。Mosquitto的log模块具备以下功能:日志分等级输出、日志的多平台输出、日志对各种参数的格式化输出;

mosquitto的日志模块初始化由函数mqtt3_log_init完成,该函数主要完成对日志输出位置以及日志输出等级两个参数的设置。函数mqtt3_log_close完成日志模块的关闭功能。日志模块的输出由函数_mosquitto_log_printf完成,该函数的声明形式为:

int _mosquitto_log_printf(struct mosquitto *mosq, int priority,const char *fmt, ...)

其中第一个参数struct mosquitto *mosq表示某客户端连接所对应的上下文信息,该参数可以为空;第二个参数int priority表示日志的优先级,在mosquitto的配置文件mosquitto.conf中将会配置日志的输出等级,如果传给函数的日志等级低于配置文件中的等级配置,该条日志将不被输出。另外,该函为一可变参数的函数,在使用过程中可以根据需要将参数进行格式化输出。例如下一条日志输出语句:

_mosquitto_log_printf(NULL,MOSQ_LOG_NOTICE, \

在该条日志输出语句中,将保存在client_id中的客户端ID格式化到字符串\中作为本条日志输出的内容,本条日志输出的等级为MOSQ_LOG_NOTICE。

4.2、配置参数管理

Mosquitto参数配置功能的实现代码主要在文件mosquitto-1.2/src/conf.c中,配置文件保存路径为:mosquitto-1.2/ mosquitto.conf,在mosquitto程序中主要由结构体structmqtt3_config保存从配置文件中读取的各参数值。Mosquitto的配置参数管理功能主要包括对配置文件的读取和解析、对用户输入参数的解析以及对配置文件结构体的管理三部分

Mosquitto对外提供的操作接口均以“mqtt3_config_”开头,共有以下四个:

(1) mqtt3_config_init

该函数的声明为:

void mqtt3_config_init(struct mqtt3_config *config)

它主要完成对参数config的各结构体成员的初始化工作。

(2) mqtt3_config_cleanup

该函数的声明为:

void mqtt3_config_cleanup(struct mqtt3_config *config)

它主要完成对参数config的各成员的清除、释放等操作。

(3) mqtt3_config_read

该函数的声明为:

int mqtt3_config_read(struct mqtt3_config *config, bool reload)

它主要完成从配置文件中读取各配置参数到结构体config中,具体的配置文件读取工作由函数_config_read_file完成。

(4) mqtt3_config_parse_args

该函数的声明为:

int mqtt3_config_parse_args(struct mqtt3_config *config, intargc, char *argv[])

它主要完成对用户输出参数的解析工作。

Mosquitto的配置文件内部共提供对整形、布尔类型,字符串三种类型的解析函数,这三个解析函数只共内部调用,分别是:_conf_parse_bool、_conf_parse_int、_conf_parse_string。

4.3、内存操作的封装

Mosquitto对内存操作的封装代码主要在文件mosquitto-1.2/src/memory_mosq.c中实现,共实现了对常用的内存申请与释放相关的系统调用函数,共有:_mosquitto_calloc函数是对系统函数calloc的封装,_mosquitto_malloc函数对系统函数malloc的封装,_mosquitto_realloc函数对系统函数realloc的封装,_mosquitto_free函数对系统函数free的封装,_mosquitto_strdup函数对系统函数strdup的封装。在上述封装函数中如果文件中定义了宏REAL_WITH_MEMORY_TRACKING,则这些封装函数只是对系统函数进行封装,不做任何额外操作。如果定义了REAL_WITH_MEMORY_TRACKING宏,则会在内存申请和释放时分别记录所申请或释放内存的大小。


mosquitto源码分析(4).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:2017年三年级数学下册期末复习巩固与提高 位置与方向

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: