Paho MQTT 嵌入式c客户端研究笔记 (二)
来源:互联网 发布:日本社会的残酷知乎 编辑:程序博客网 时间:2024/05/16 14:18
paho.mqtt.embedded-c-master\MQTTPacket\samples,这个目录里面封装了发布消息、订阅消息的示例。运行pub0sub1,这个示例里面会去订阅主题消息、发布主题消息。并且订阅和发布的消息是同一个主题,所以在运行过程中会看到循环打印同一份消息。代码如下:
#include <stdio.h>#include <string.h>#include <stdlib.h>#include "MQTTPacket.h"#include "transport.h"/* This is in order to get an asynchronous signal to stop the sample,as the code loops waiting for msgs on the subscribed topic.Your actual code will depend on your hw and approach*/#include <signal.h>int toStop = 0;void cfinish(int sig){ signal(SIGINT, NULL); toStop = 1;}void stop_init(void){ signal(SIGINT, cfinish); signal(SIGTERM, cfinish);}/* */int main(int argc, char *argv[]){ MQTTPacket_connectData data = MQTTPacket_connectData_initializer; int rc = 0; int mysock = 0; unsigned char buf[200]; int buflen = sizeof(buf); int msgid = 1; MQTTString topicString = MQTTString_initializer; int req_qos = 0; char* payload = "mypayload"; int payloadlen = strlen(payload); int len = 0; char *host = "m2m.eclipse.org"; int port = 1883; stop_init(); if (argc > 1) host = argv[1]; if (argc > 2) port = atoi(argv[2]); mysock = transport_open(host, port); if(mysock < 0) return mysock; printf("Sending to hostname %s port %d\n", host, port); data.clientID.cstring = "me"; data.keepAliveInterval = 20; data.cleansession = 1; data.username.cstring = "testuser"; data.password.cstring = "testpassword"; //连接服务器 len = MQTTSerialize_connect(buf, buflen, &data); rc = transport_sendPacketBuffer(mysock, buf, len); /* wait for connack */ if (MQTTPacket_read(buf, buflen, transport_getdata) == CONNACK) { unsigned char sessionPresent, connack_rc; if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) != 1 || connack_rc != 0) { printf("Unable to connect, return code %d\n", connack_rc); goto exit; } } else goto exit; /* subscribe 订阅主题消息*/ topicString.cstring = "substopic"; len = MQTTSerialize_subscribe(buf, buflen, 0, msgid, 1, &topicString, &req_qos); rc = transport_sendPacketBuffer(mysock, buf, len); if (MQTTPacket_read(buf, buflen, transport_getdata) == SUBACK) /* wait for suback */ { unsigned short submsgid; int subcount; int granted_qos; rc = MQTTDeserialize_suback(&submsgid, 1, &subcount, &granted_qos, buf, buflen); if (granted_qos != 0) { printf("granted qos != 0, %d\n", granted_qos); goto exit; } } else goto exit; /* loop getting msgs on subscribed topic循环读取消息 */ topicString.cstring = "pubtopic"; while (!toStop) { /* transport_getdata() has a built-in 1 second timeout, your mileage will vary */ if (MQTTPacket_read(buf, buflen, transport_getdata) == PUBLISH) { unsigned char dup; int qos; unsigned char retained; unsigned short msgid; int payloadlen_in; unsigned char* payload_in; int rc; MQTTString receivedTopic; rc = MQTTDeserialize_publish(&dup, &qos, &retained, &msgid, &receivedTopic, &payload_in, &payloadlen_in, buf, buflen); printf("message arrived %.*s\n", payloadlen_in, payload_in); } printf("publishing reading\n"); //下面两行是用来发布消息。这里发布,上面订阅,就形成了一个循环。 len = MQTTSerialize_publish(buf, buflen, 0, 0, 0, 0, topicString, (unsigned char*)payload, payloadlen); rc = transport_sendPacketBuffer(mysock, buf, len); } printf("disconnecting\n"); len = MQTTSerialize_disconnect(buf, buflen); rc = transport_sendPacketBuffer(mysock, buf, len);exit: transport_close(mysock); return 0;}
在这个示例中,改造一下,把发布消息的代码给注释掉。我们发现,过了一会,循环读消息的地方就再也接收不到服务器发送过来的消息了。推测是此时客户端与服务器之间的长连接已经断了。回到代码,
//设置心跳包间隔时间data.keepAliveInterval = 20;
Keep Alive timer
The Keep Alive timer is present in the variable header of a MQTT CONNECT message.The Keep Alive timer, measured in seconds, defines the maximum time interval between >messages received from a client. It enables the server to detect that the network connection to a client has dropped, without having to wait for the long TCP/IP timeout. The client has a responsibility to send a message within each Keep Alive time period. In the absence of a data-related message during the time period, the client sends a PINGREQ message, which the server acknowledges with a PINGRESP message.
If the server does not receive a message from the client within one and a half times the Keep Alive time period (the client is allowed “grace” of half a time period), it disconnects the client as if the client had sent a DISCONNECT message. This action does not impact any of the client’s subscriptions. See DISCONNECT for more details.
If a client does not receive a PINGRESP message within a Keep Alive time period after sending a PINGREQ, it should close the TCP/IP socket connection.
The Keep Alive timer is a 16-bit value that represents the number of seconds for the time period. The actual value is application-specific, but a typical value is a few minutes. The maximum value is approximately 18 hours. A value of zero (0) means the client is not disconnected.
The format of the Keep Alive timer is shown in the table below. The ordering of the 2 bytes of the Keep Alive Timer is MSB, then LSB (big-endian).
pub0sub1示例代码中,删除发布消息代码块只保留订阅消息功能以后,客户端在20秒之内都可以接收到服务器推送过来的消息。如果20秒内客户端没有向服务器发送PINGREQ消息,那么服务器会关闭掉TCP/IP端口连接。
因此,如果希望开启一个可以永远保持订阅消息的客户端,需要在设置的心跳间隔时间内向服务器发送PINGREQ消息。具体做法可以这样,代码如下:
while (!toStop) { /****循环订阅消息*************/ topicString.cstring = "pubtopic"; len = MQTTSerialize_subscribe(buf, buflen, 0, msgid, 1, &topicString, &req_qos); rc = transport_sendPacketBuffer(mysock, buf, len); printf("publishing reading000\n"); if (MQTTPacket_read(buf, buflen, transport_getdata) == SUBACK) /* wait for suback */ { unsigned short submsgid; int subcount; int granted_qos; rc = MQTTDeserialize_suback(&submsgid, 1, &subcount, &granted_qos, buf, buflen); if (granted_qos != 0) { printf("granted qos != 0, %d\n", granted_qos); goto exit; } } else goto exit; /*****************/ /* transport_getdata() has a built-in 1 second timeout, your mileage will vary */ int state = MQTTPacket_read(buf, buflen, transport_getdata); //printf("state is = %d\n", state); //printf("PUBLISH is = %d\n", PUBLISH); if (state != -1){ printf("state2 is = %d\n", state); } if (state == PUBLISH) { unsigned char dup; int qos; unsigned char retained; unsigned short msgid; int payloadlen_in; unsigned char* payload_in; int rc; MQTTString receivedTopic; rc = MQTTDeserialize_publish(&dup, &qos, &retained, &msgid, &receivedTopic, &payload_in, &payloadlen_in, buf, buflen); printf("message arrived %.*s\n", payloadlen_in, payload_in); } //printf("publishing reading\n"); //len = MQTTSerialize_publish(buf, buflen, 0, 0, 0, 0, topicString, (unsigned char*)payload, payloadlen); //rc = transport_sendPacketBuffer(mysock, buf, len); }
我们在循环读取消息的代码块中加入了订阅消息,这样可以保持客户端与服务器之间长连接不会断开。当然,这么做效果并不好,因为循环发送订阅消息会对服务器产生比较多的负载。可以对代码做个优化,比如每间隔10秒钟发送一次订阅消息的请求。百度云官方提供的客户端代码就是这么实现的,下一次我们拿出来做对比。文档代码点这里
- Paho MQTT 嵌入式c客户端研究笔记 (二)
- Paho MQTT 嵌入式c客户端研究笔记
- MQTT客户端--基于paho实现(Java)
- MQTT paho客户端使用
- MQTT学习笔记——树莓派MQTT客户端 使用Mosquitto和paho-python
- MQTT学习笔记——树莓派MQTT客户端 使用Mosquitto和paho-python
- Eclipse paho实现的MQTT Java客户端
- 基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(七)交叉编译paho.mqtt.embedded-c库和嵌入式linux样例程序
- vc2015编译paho.mqtt.c-1.1.0
- cygwin编译paho.mqtt.c-1.1.0
- 交叉编译OpenSSL和paho.mqtt.c
- 使用eclipse paho在java端实现MQTT消息的收发(客户端与服务端实例)
- MQTT学习笔记——Yeelink MQTT服务 使用mqtt.js和paho-mqtt
- MQTT 嵌入式 C语言 客户端libemqtt源码解析
- MQTT协议实现Eclipse Paho学习总结二
- MQTT协议实现Eclipse Paho学习总结二
- Practical MQTT with Paho
- MQTT协议 Python Paho
- Spring Security笔记
- width:100%和height:auto区别
- 日期转换错误
- Android 权限配置一览
- 算法笔记之动态规划(DP)
- Paho MQTT 嵌入式c客户端研究笔记 (二)
- 冒泡排序算法总结
- linux下定时器setitimer的使用
- kubernetes helm API 客户端文件生成和远程调用
- (二)ROS中控制机器人运动(示例运行)
- 二分查找与快排算法
- gradle编译遇到的坑,持续更新
- chrome扩展开发示例之扩展页面与content_scripts建立长连接通信
- x86架构中断基础介绍