为什么在连接回调之前调用 libmosquitto 发布回调?
Why libmosquitto publlish callback is called before connection callback?
我有这个程序使用 mosquitto MQTT library:
/*
compile using:
$ gcc -o libmosq libmosq.c -lmosquitto
*/
#include <stdio.h>
#include <mosquitto.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void connection_callback(struct mosquitto* mosq, void *obj, int rc)
{
if (rc) {
printf("connection error: %d (%s)\n", rc, mosquitto_connack_string(rc));
}
else {
printf("connection success\n");
}
}
void publish_callback(struct mosquitto* mosq, void *obj, int mid)
{
printf("this is the publish callback\n");
}
int main(int argc, char *argv[])
{
struct mosquitto *mosq = NULL;
mosquitto_lib_init();
mosq = mosquitto_new(NULL, true, NULL);
if(!mosq) {
fprintf(stderr, "Error: Out of memory.\n");
exit(1);
}
mosquitto_connect_callback_set(mosq, connection_callback);
mosquitto_publish_callback_set(mosq, publish_callback);
mosquitto_username_pw_set(mosq, "user1", "passwd1");
int resultCode = mosquitto_connect(mosq, "localhost", 1883, 60);
if (resultCode != MOSQ_ERR_SUCCESS) {
fprintf(stderr, "error calling mosquitto_connect\n");
exit(1);
}
int loop = mosquitto_loop_start(mosq);
if(loop != MOSQ_ERR_SUCCESS){
fprintf(stderr, "Unable to start loop: %i\n", loop);
exit(1);
}
char topic[] = "/testtopic";
char msg[] = "foobar";
int publish = mosquitto_publish(mosq, NULL, topic, (int) strlen(msg), msg, 0, false);
if(publish != MOSQ_ERR_SUCCESS){
fprintf(stderr, "Unable to publish: %i\n", publish);
exit(1);
}
// hang until control+C is done
sleep(1000000);
}
基本上,它连接到 MQTT 代理并在其上发布消息。
如果我运行程序我得到以下输出:
this is the publish callback
connection success
这意味着发布回调在连接回调之前被调用,从我的角度来看这是违反直觉的(因为事件的顺序是相反的:首先发生连接,然后发布消息从我的程序到 MQTT 代理)。
这怎么可能?为什么会发生这种违反直觉的行为?谢谢!
要意识到的重要一点是mosquitto_connect()
在阻塞时,不会阻塞整个连接过程,它只会阻塞直到发送了CONNECT数据包,它不会等待CONNACT数据包将被返回,因此 on_connect
回调。
因此,调用 mosquitto_connect()
之后的所有代码完全有可能执行到并包括在收到 CONNACT 数据包之前发布的调用。并且考虑到 QOS 0 消息的 on_publish
回调将在发送 PUBLISH 数据包后立即调用(或者甚至可能在它被添加到出站队列时)因为没有确认这也可能发生在 CONNACT 之前已收到。
如果您想正确执行此操作,则应将所有发布代码移至 on_connect
回调(或根据该回调中设置的标志进行门控)
P.S。您的主题真的不应该以前导 /
开头。虽然从技术上讲符合规范,但它只是为以后存储问题,因为它在主题树的开头添加了一个 null
条目,这将在您访问它们时破坏诸如共享订阅之类的东西。
我有这个程序使用 mosquitto MQTT library:
/*
compile using:
$ gcc -o libmosq libmosq.c -lmosquitto
*/
#include <stdio.h>
#include <mosquitto.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void connection_callback(struct mosquitto* mosq, void *obj, int rc)
{
if (rc) {
printf("connection error: %d (%s)\n", rc, mosquitto_connack_string(rc));
}
else {
printf("connection success\n");
}
}
void publish_callback(struct mosquitto* mosq, void *obj, int mid)
{
printf("this is the publish callback\n");
}
int main(int argc, char *argv[])
{
struct mosquitto *mosq = NULL;
mosquitto_lib_init();
mosq = mosquitto_new(NULL, true, NULL);
if(!mosq) {
fprintf(stderr, "Error: Out of memory.\n");
exit(1);
}
mosquitto_connect_callback_set(mosq, connection_callback);
mosquitto_publish_callback_set(mosq, publish_callback);
mosquitto_username_pw_set(mosq, "user1", "passwd1");
int resultCode = mosquitto_connect(mosq, "localhost", 1883, 60);
if (resultCode != MOSQ_ERR_SUCCESS) {
fprintf(stderr, "error calling mosquitto_connect\n");
exit(1);
}
int loop = mosquitto_loop_start(mosq);
if(loop != MOSQ_ERR_SUCCESS){
fprintf(stderr, "Unable to start loop: %i\n", loop);
exit(1);
}
char topic[] = "/testtopic";
char msg[] = "foobar";
int publish = mosquitto_publish(mosq, NULL, topic, (int) strlen(msg), msg, 0, false);
if(publish != MOSQ_ERR_SUCCESS){
fprintf(stderr, "Unable to publish: %i\n", publish);
exit(1);
}
// hang until control+C is done
sleep(1000000);
}
基本上,它连接到 MQTT 代理并在其上发布消息。
如果我运行程序我得到以下输出:
this is the publish callback
connection success
这意味着发布回调在连接回调之前被调用,从我的角度来看这是违反直觉的(因为事件的顺序是相反的:首先发生连接,然后发布消息从我的程序到 MQTT 代理)。
这怎么可能?为什么会发生这种违反直觉的行为?谢谢!
要意识到的重要一点是mosquitto_connect()
在阻塞时,不会阻塞整个连接过程,它只会阻塞直到发送了CONNECT数据包,它不会等待CONNACT数据包将被返回,因此 on_connect
回调。
因此,调用 mosquitto_connect()
之后的所有代码完全有可能执行到并包括在收到 CONNACT 数据包之前发布的调用。并且考虑到 QOS 0 消息的 on_publish
回调将在发送 PUBLISH 数据包后立即调用(或者甚至可能在它被添加到出站队列时)因为没有确认这也可能发生在 CONNACT 之前已收到。
如果您想正确执行此操作,则应将所有发布代码移至 on_connect
回调(或根据该回调中设置的标志进行门控)
P.S。您的主题真的不应该以前导 /
开头。虽然从技术上讲符合规范,但它只是为以后存储问题,因为它在主题树的开头添加了一个 null
条目,这将在您访问它们时破坏诸如共享订阅之类的东西。