Почему это сообщение отправляется с использованием paho.mqtt.c, что вызывает ошибку сегментации?

Я пытаюсь отправить сообщение определенного типа, используя протокол MQTT. Я использую библиотеку paho.mqtt.c, и мой брокер - RabbitMQ 3.6.12, работает под управлением Erlang 20.0. Я работаю на виртуальной машине под управлением CentOS 6.9. Сначала я попытался сделать это, создав структуру для моего конкретного типа сообщения, затем, прежде чем задать этот вопрос, я также попытался использовать JSON для создания своего конкретного типа сообщения. Я установил cJSON ( отсюда).

Вот весь мой код с использованием cJSON:

pubframe.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MQTTClient.h"
#include "../tools.c"
#include <cjson/cJSON.h>

#define ADDRESS      "tcp://localhost:1883"
#define CLIENTID     "MY_PUB"
#define TOPIC        "MQTT/Test"

int main(int argc, char* argv[])
{   

        frame1 test = {42,"test"};

        cJSON* frm = NULL;
        frm = cJSON_CreateObject();

        cJSON_AddNumberToObject(frm,"entier",test.E);
        cJSON_AddStringToObject(frm,"string",test.S);

        print_frame1(frm);

        int i = cJSON_GetArraySize(frm);
        printf("number of items in frame : %d\n",i);
        cJSON* entier = NULL;
        entier = cJSON_GetObjectItem(frm,"entier");
        char* pt = cJSON_Print(entier);
        printf("entier : %s\n",pt);
        cJSON* str = NULL;
        str = cJSON_GetObjectItem(frm,"string");
        char* st = cJSON_Print(str);
        printf("string : %s\n",st);

        printf("size of message : %d\n",sizeof(cJSON));

        MQTTClient publisher;
        MQTTClient_connectOptions connexion = MQTTClient_connectOptions_initializer;

        MQTTClient_message msg = msg_creation(frm,sizeof(cJSON),0,0);
    MQTTClient_deliveryToken token;
        int rc;
        MQTTClient_create(&publisher, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL);
    connexion.cleansession = 1;
        MQTTClient_setCallbacks(publisher, NULL, connlost, frame_json_arrvd, NULL);
        if ((rc = MQTTClient_connect(publisher,&connexion)) != MQTTCLIENT_SUCCESS)
        {
            printf("Failed to connect, return code %d\n", rc);
        }
        MQTTClient_publishMessage(publisher, TOPIC,&msg,NULL);
        printf("Message sent!\n");
        cJSON_Delete(frm);
        MQTTClient_disconnect(publisher,10000);
        MQTTClient_destroy(&publisher);
        return rc;
}

subframe.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MQTTClient.h"
#include "../tools.c"
#include <cjson/cJSON.h>


#define ADDRESS     "tcp://localhost:1883"
#define CLIENTID    "ClientID"
#define TOPIC       "MQTT/Test"
#define QOS         0
#define TIMEOUT     10000L

int main(int argc, char* argv[])
{
    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    int rc;
    int ch;
    MQTTClient_create(&client, ADDRESS, CLIENTID,
        MQTTCLIENT_PERSISTENCE_NONE, NULL);
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    MQTTClient_setCallbacks(client, NULL, connlost, frame_json_arrvd, NULL);
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
    {
        printf("Failed to connect, return code %d\n", rc);
        exit(EXIT_FAILURE);
    }
    printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n"
           "Press Q<Enter> to quit\n\n", TOPIC, CLIENTID, QOS);
    MQTTClient_subscribe(client, TOPIC, QOS);
    do
    {
        ch = getchar();
    } while(ch!='Q' && ch != 'q');
    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);
    return rc;
}

tools.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <cjson/cJSON.h>
#include "MQTTClient.h"

int compteur;

// message frames

typedef struct frame1
{
    int E;
    char* S;
} frame1;

void print_frame1bis(frame1* F)
{
        printf("     %s\n",F->S);
        printf("     %d\n",F->E);
}

void print_frame1(cJSON* frame1)
{
    char * str = cJSON_Print(frame1);
    printf("%s\n",str);
}

int frame_json_arrvd(void* context, char* topicName, int topicLen, MQTTClient_message* msg)
{

        cJSON* payload_ptr = NULL;

        printf("size of message : %d\n",sizeof(msg->payload));

        int j = cJSON_GetArraySize(msg->payload);
        printf("ok\n");
        printf("number of items in frame : %d\n",j);

        payload_ptr = cJSON_CreateObject();
        payload_ptr = msg->payload;

        int i = cJSON_GetArraySize(payload_ptr);
        printf("number of items in frame : %d\n",i);

        print_frame1(payload_ptr);

        cJSON_Delete(payload_ptr);
        MQTTClient_freeMessage(&msg);
        MQTTClient_free(topicName);
        return 1;
}

// in case connexion is lost

void connlost(void *context, char *cause)
{
    printf("\nConnection lost\n");
    printf("     cause: %s\n", cause);
}


// create message

MQTTClient_message msg_creation(void* payload, int length, int qos, int retained)
{
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    pubmsg.payload = payload;
    pubmsg.payloadlen = length;
    pubmsg.qos = qos;
    pubmsg.retained = retained;
    return pubmsg;
}

Выход из абонента

Subscribing to topic MQTT/Test
for client ClientID using QoS0

Press Q<Enter> to quit

size of message : 8
Segmentation fault (core dumped)

Таким образом, кажется, что сообщение получено неправильно, так как подписчик аварийно завершает работу, как только он пытается получить количество элементов.

Это то, что я получаю, когда запускаю gdb, не уверен, поможет ли это.

Program terminated with signal 11, Segmentation fault.
#0  0x00007f4e49a8b63e in cJSON_GetArraySize () from /usr/lib64/libcjson.so.1
(gdb) bt
#0  0x00007f4e49a8b63e in cJSON_GetArraySize () from /usr/lib64/libcjson.so.1
#1  0x00000000004010c9 in frame_json_arrvd (context=0x0,
    topicName=0x7f4e440009e4 "MQTT/Test", topicLen=0, msg=0x7f4e44000bb4)
    at ../tools.c:110
#2  0x00007f4e49c962a5 in MQTTClient_run (n=<value optimized out>)
    at src/MQTTClient.c:604
#3  0x000000378b807aa1 in start_thread (arg=0x7f4e49a83700) at pthread_create.c:301
#4  0x000000378b4e8bcd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:115

Выход от издателя

{
        "entier":       42,
        "string":       "test"
}
number of items in frame : 2
entier : 42
string : "test"
size of message : 64
Message sent!

Похоже, сообщение создано правильно.

Я только что посмотрел на cJSON недавно, поэтому я буду продолжать расследование на случай, если я использую его неправильно, но любая помощь будет признательна. Я попытался сузить его, как это было предложено в комментариях, но я не вижу, что я могу сделать, поскольку он падает, как только пытается получить доступ к полезной нагрузке сообщения.

Пожалуйста, имейте в виду, что я всего лишь студент с опытом работы не более года в области компьютерных наук. Кроме того, английский не является моим родным языком, я надеюсь, что объясню достаточно ясно.

1 ответ

Решение

Проблема решена (вроде)

Я просто отправляю весь фрейм сообщения в виде строки и анализирую его в функции msgarrvd:

pubframe.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MQTTClient.h"
#include "../tools.c"
#include <cjson/cJSON.h>

#define ADDRESS      "tcp://localhost:1883"
#define CLIENTID     "MY_PUB"
#define TOPIC        "MQTT/Test"

volatile MQTTClient_deliveryToken deliveredtoken;
void delivered(void *context, MQTTClient_deliveryToken dt)
{
    printf("Message with token value %d delivery confirmed\n", dt);
    deliveredtoken = dt;
}

int main(int argc, char* argv[])
{   

    char* test = "{\"string\" : \"whatever\", \"entier\" : 42}";

    MQTTClient publisher;
    MQTTClient_connectOptions connexion = MQTTClient_connectOptions_initializer;

    MQTTClient_message msg = msg_creation(test,strlen(test),0,0);

    int rc;
    MQTTClient_create(&publisher, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL);
    connexion.cleansession = 1;
    MQTTClient_setCallbacks(publisher, NULL, connlost, frame_test_arrvd, NULL);
    if ((rc = MQTTClient_connect(publisher,&connexion)) != MQTTCLIENT_SUCCESS)
    {
        printf("Failed to connect, return code %d\n", rc);
    }
    MQTTClient_publishMessage(publisher, TOPIC,&msg,&token);
    printf("Message sent!\n");
    cJSON_Delete(frm);
    MQTTClient_disconnect(publisher,10000);
    MQTTClient_destroy(&publisher);
    return rc;
}

tools.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <cjson/cJSON.h>
#include "MQTTClient.h"

// message frames

void print_frame1(cJSON* frame1)
{
    char * str = cJSON_Print(frame1);
    printf("%s\n",str);
}

int frame_test_arrvd(void* context, char* topicName, int topicLen, MQTTClient_message* msg)
{
    printf("message original : %s\n",msg->payload);
    cJSON* payload_ptr = cJSON_Parse(msg->payload);

    print_frame1(payload_ptr);
    int i = cJSON_GetArraySize(payload_ptr);
    printf("number of items in frame : %d\n",i);
    cJSON* entier = NULL;
    entier = cJSON_GetObjectItem(payload_ptr,"entier");
    char* pt = cJSON_Print(entier);
    printf("entier : %s\n",pt);
    cJSON* str = NULL;
    str = cJSON_GetObjectItem(payload_ptr,"string");
    char* st = cJSON_Print(str);
    printf("string : %s\n",st);

    cJSON_Delete(payload_ptr);

    MQTTClient_freeMessage(&msg);
    MQTTClient_free(topicName);
    return 1;
}

// in case connexion is lost

void connlost(void *context, char *cause)
{
    printf("\nConnection lost\n");
    printf("     cause: %s\n", cause);
}


// create message

MQTTClient_message msg_creation(void* payload, int length, int qos, int retained)
{
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    pubmsg.payload = payload;
    pubmsg.payloadlen = length;
    pubmsg.qos = qos;
    pubmsg.retained = retained;
    return pubmsg;
}

subframe.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MQTTClient.h"
#include "../tools.c"
#include <cjson/cJSON.h>


#define ADDRESS     "tcp://localhost:1883"
#define CLIENTID    "ClientID"
#define TOPIC       "MQTT/Test"
#define QOS         0
#define TIMEOUT     10000L

int main(int argc, char* argv[])
{
    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    int rc;
    int ch;
    MQTTClient_create(&client, ADDRESS, CLIENTID,
        MQTTCLIENT_PERSISTENCE_NONE, NULL);
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    MQTTClient_setCallbacks(client, NULL, connlost, frame_test_arrvd, NULL);
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
    {
        printf("Failed to connect, return code %d\n", rc);
        exit(EXIT_FAILURE);
    }
    printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n"
           "Press Q<Enter> to quit\n\n", TOPIC, CLIENTID, QOS);
    MQTTClient_subscribe(client, TOPIC, QOS);
    do
    {
        ch = getchar();
    } while(ch!='Q' && ch != 'q');
    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);
    return rc;
}

Это не идеально, так как я все еще хотел бы создавать определенные типы сообщений, и я не выяснил, что стало причиной ошибки сегментации, но это прекрасно работает таким образом.

ОБНОВИТЬ

Я нашел способ создания определенных типов сообщений и по-прежнему отправлять их в виде строки для анализа их в функции msgarrvd. Я просто создал функцию cJSON_ToString для преобразования объекта cJSON* в строку, чтобы я мог создать cJOSN из любой структуры Я хочу, а затем преобразовать его в строку, чтобы отправить его.

Другие вопросы по тегам