MQTT 嵌入式 C语言 客户端libemqtt源码解析

2019-07-13 05:11发布

本文是MQTT 嵌入式 C语言 客户端libemqtt源码解析 MQTT协议连接
libemqtt源码下载 源码目录结构:

├── client:客户端应用代码(订阅与发布)
├── include:头文件包含
└── src:emqtt实现源码
└── python

注意:里面有python实现代码,本文直接跳过,全部讲解C代码。 需要具备的一点概念:
  1. 阻塞与非阻塞
  2. 同步与异步
  3. 看完上面的两个概念链接,你现在可以告诉我阻塞调用和同步调用的区别与联系吗?请在下面留言用最最最短的话描述。谢谢。
提出的待解决问题:
  1. 客户端规定在最大时间发送报文的时间间隔内,再次发送一份pingreq给服务器,否则将自动断开连接。在arm上可以使用signal信号处理函数,在单片机上可以使用定时器,间隔合理时间地发送pingreq报文就可保持双方连接状态。
  2. libemqtt源码(C语言客户端)使用[I/O多路转换]来实现并发(对于订阅者来说,则是并发接收),同一个socket_io则选择其中之一,这从mosquitto的测试里面提及到的20W的连接量是一致的说法。那么,libemqtt是运行在arm linux上,而单片机,则可能需要使用定时器来解决,甚至是需要操作系统开启多线程。这点可能需要更多的斟酌。
  3. 在socket初始化中,关闭了nagle算法:就是禁止发送合并。因为物联网数据包一般比较少,同时满足于实时性要求,将算法关闭,适应物联网开发条件。
    Nagle算法通过将未确认的数据存入缓冲区直到蓄足一个包一起发送的方法,来减少主机发送的零碎小数据包的数目。但对于某些应用来说,这种算法将降低系统性能。所以TCP_NODELAY可用来将此算法关闭。应用程序编写者只有在确切了解它的效果并确实需要的情况下,才设置TCP_NODELAY选项,因为设置后对网络性能有明显的负面影响。
  4. libemqtt源码中的read_packet函数使用select()函数来确定已经打开的socket_id状态,多路检测可用套接字,通知调用socket进程哪一个socket可以读。同时,select也都需要一组fs_set数组,与socket_id建立联系。源码中select之后马上调用recv(此时确知recv不会阻塞)。
    最后,第四点最终目的,其实是为了使得recv无阻赛地进行读取。
    以下提点优化:
    1. 使用select是需要耗费时间去对等待内核根据io状态返回(这时候就换个角度来讲,它也是一个阻塞的函数),就如同select函数设置timeval参数一样,等待的时间正是我们需要优化的地方。
    2. 如同上述,可以参考使用多线程来解决等待非阻塞select问题。使用多线程,使用同步模型来编写程序,并让这些线程以异步的方式运行,但多线程的线程间同步的开销又带来了新的问题。
    3. 又或者可以使用异步I/O,不过,又会带来无法根据信号来判断哪个描述符起作用,还有就是移植性问题(不同版本的的异步IO有区别)。
    4. 上面讨论的或许有矛盾地方,但并非死循环,我们一定可以找到最优解。
  5. 还是要继续回答第四点留下的一个疑问:通过查看libemqtt源码read_packet函数,发现仅加入一个文件描述符(socket_id)到select的套接口组的子集(readfds)里面,这意味着把一直都在监听一个socket_id,接收的数据一直来自一个服务器;如果,我要接收美国和中国两个服务器(他们肯定不同吧),那么就有两个socket_id,所以,这时候使用异步IO又意味着可以同时检测两路数据。
libemqtt源码阅读建议:
1. 参考书籍《UNIX环境高级编程》
1. 第14章:高级IO
2. 第16章:网络IPC:套接字
3. 查看高级IO的过程中发现,还需要阅读 第十章 信号。