0

我有这个程序使用mosquitto MQTT 库

/*
  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经纪人)。

这怎么可能?为什么会发生这种违反直觉的行为?谢谢!

4

1 回答 1

1

要意识到的重要一点是,mosquitto_connect()在阻塞时,不会阻塞整个连接过程,它只会阻塞直到 CONNECT 数据包发送完毕,它不会等待 CONNACT 数据包返回,因此on_connect回调。

因此,调用之后的所有代码完全有可能在mosquitto_connect()接收到 CONNACT 数据包之前执行并包括调用发布。并且on_publish,一旦发送 PUBLISH 数据包(或者甚至可能在它被添加到出站队列时),QOS 0 消息的回调将被调用,因为在接收到 CONNACT 之前也可能发生这种情况没有确认。

如果您想正确执行此操作,则应将所有发布代码移至on_connect回调(或对该回调中设置的标志进行门控)

PS您的主题真的不应该以领先的开头/。虽然在技术上符合规范,但它只是为以后存储问题,因为它null在主题树的开头添加了一个条目,当您访问它们时会破坏诸如共享订阅之类的东西。

于 2021-10-15T08:24:23.947 回答