mosquitto源码分析(五)

  本文由逍遥子撰写,转发请标注原址:

http://write.blog.csdn.net/postedit/21464519

3.2.1、poll机制简介

Poll机制是一种I/O多路转接(I/O multiplexing)技术,这种技术先构造一个有关描述符的表,然后调用一个函数,知道这些描述符中的一个已准备就绪好进行I/O时,该函数才返回,该函数返回之后它告诉进程那些描述符已经准备好进行I/O)。其工作过程为:

(1)首先构造一个pollfd结构数组,每个数组元素指定一个描述符编号以及对其所关心的状态。

(2)为每一个需要监听状态的设备描述符创建一个pollfd结构体,并将之放入pollfd结构数组中。

(3)调用函数poll监控pollfd结构数组中每个结构体的状态。Poll函数的原型如下:

int poll(struct pollfdfdarray[], nfds_t nfds, int timeout)

其中参数fdarray[]就是pollfd结构体数组,nfds是数组的元素数,timeout是等待超时限定,如果超过此时间还没有描述符准备好I/O,就返回;pollfd结构体的定义为:

struct pollfd{

int fd;

short event;

short revent;

};

其中,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连接断开了。此值需根据实际情况分析后确定。

发布了207 篇原创文章 · 获赞 196 · 访问量 108万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览