Paho - implementation and explanation of MQTT C Cient of Internet of things

Original link: https://blog.csdn.net/qq_39436605/article/details/80893954?ops_request_misc=&request_id=&biz_id=102&utm_term=mqtt%20c&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-6-80893954.pc_search_result_control_group&spm=1018.2226.3001.4187

1 Overview

In the article implementation of Paho - MQTT C Cient, I introduced how to create mqttclient using Paho open source project_ Pulish client. However, it only briefly introduces the use method, and the results of the client are not consistent with those introduced before. Today, I will explain the main process of Paho using MQTT client in combination with new examples.
  as described earlier, MQTT clients are divided into synchronous clients and asynchronous clients. Today, we will mainly explain the structure of the synchronization client, as described in the synchronization client:

1. Create a client object;
   2. Set the option to connect to the MQTT server;
   3. If multithreading (asynchronous mode) operation is used, set the callback function (see asynchronous > vs synchronous client applications for details);
  4. Subscribe to any topic that the client needs to receive;
   5. Repeat the following operations until the end:
     a. publish any information required by the client;
     b. process all received information;
   6. Disconnect the client;
  7. Release all memory used by the client.

2 implementation

MQTT is a simple synchronization client.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MQTTClient.h"
#if !defined(WIN32)
#include <unistd.h>
#else
#include <windows.h>
#endif
 
#define NUM_THREADS 2
#define ADDRESS     "tcp://localhost:1883 "/ / change the address here
#Define ClientID "aaabbbccc_pub" / / change the client ID here
#define SUB_ ClientID "aaabbbccc_sub" / / change the client ID here
#Define topic "topic01" / / change the sent topic
#define PAYLOAD     "Hello Man, Can you see me ?!" //
#define QOS         1
#define TIMEOUT     10000L
#define USERNAME    "test_user"
#define PASSWORD    "jim777"
#define DISCONNECT  "out"
 
int CONNECT = 1;
volatile MQTTClient_deliveryToken deliveredtoken;
 
void delivered(void *context, MQTTClient_deliveryToken dt)
{
    printf("Message with token value %d delivery confirmed\n", dt);
    deliveredtoken = dt;
}
 
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
    int i;
    char* payloadptr;
 
    printf("Message arrived\n");
    printf("     topic: %s\n", topicName);
    printf("   message: ");
 
    payloadptr = message->payload;
    if(strcmp(payloadptr, DISCONNECT) == 0){
        printf(" \n out!!");
        CONNECT = 0;
    }
    
    for(i=0; i<message->payloadlen; i++)
    {
        putchar(*payloadptr++);
    }
    printf("\n");
    
    MQTTClient_freeMessage(&message);
    MQTTClient_free(topicName);
    return 1;
}
 
void connlost(void *context, char *cause)
{
    printf("\nConnection lost\n");
    printf("     cause: %s\n", cause);
}
 
void *subClient(void *threadid){
   long tid;
   tid = (long)threadid;
   printf("Hello World! It's me, thread #%ld!\n", tid);
   
    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    int rc;
    int ch;
 
    MQTTClient_create(&client, ADDRESS, SUB_CLIENTID,
        MQTTCLIENT_PERSISTENCE_NONE, NULL);
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    conn_opts.username = USERNAME;
    conn_opts.password = PASSWORD;
    
    MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered);
 
    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_unsubscribe(client, TOPIC);
    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);
   
   pthread_exit(NULL);
}
void *pubClient(void *threadid){
   long tid;
   tid = (long)threadid;
   int count = 0;
   printf("Hello World! It's me, thread #%ld!\n", tid);
//Declare an MQTTClient
    MQTTClient client;
    //Initialize MQTT Client options
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    //#define MQTTClient_message_initializer { {'M', 'Q', 'T', 'M'}, 0, 0, NULL, 0, 0, 0, 0 }
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    //Declaration message token
    MQTTClient_deliveryToken token;
    int rc;
    //Create a client with parameters and assign it to the previously declared client
    MQTTClient_create(&client, ADDRESS, CLIENTID,
        MQTTCLIENT_PERSISTENCE_NONE, NULL);
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    conn_opts.username = USERNAME;
    conn_opts.password = PASSWORD;
     //Using MQTTClient_connect connects the client to the server using the specified connection options. Mqttclient if successful_ SUCCESS
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
    {
        printf("Failed to connect, return code %d\n", rc);
        exit(EXIT_FAILURE);
    }
    pubmsg.payload = PAYLOAD;
    pubmsg.payloadlen = strlen(PAYLOAD);
    pubmsg.qos = QOS;
    pubmsg.retained = 0;
    while(CONNECT){
    MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token);
    printf("Waiting for up to %d seconds for publication of %s\n"
            "on topic %s for client with ClientID: %s\n",
            (int)(TIMEOUT/1000), PAYLOAD, TOPIC, CLIENTID);
    rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
    printf("Message with delivery token %d delivered\n", token);
    usleep(3000000L);
    }
    
    
    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);
}
int main(int argc, char* argv[])
{
    pthread_t threads[NUM_THREADS];
    long t;
    pthread_create(&threads[0], NULL, subClient, (void *)0);
    pthread_create(&threads[1], NULL, pubClient, (void *)1);
    pthread_exit(NULL);
}

In the code, I created two threads to handle the subscription client and the publishing client respectively.

3 overall detailed explanation

Next, let me explain this simple client. The general process is as follows:

The general flow is shown in the figure. After the client starts, the thread will be started to create a subscription client, which will listen for the arrival of the message, and trigger the corresponding callback function to process the message after the message arrives; After that, start a thread and create a sending client to send messages. Before each message is sent, it will judge whether to drop the line. If CONNECT=0, it will drop the line. Otherwise, it will send a message to topic01.

3.1 subscription client details

The following function completes the subscription function.

void *subClient(void *threadid);

The process is as follows:
   step 1: declare the client and assign value to it through the function;

MQTTClient client;
MQTTClient_create(&client, ADDRESS, SUB_CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL);

Step 2: set the option to connect to the MQTT server;

MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;

Step 3: set callback function;

MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered);
//The corresponding callback functions connlost, msgarrvd and delivered are all in my code

Step 4: use the client and connection options to connect to the server;

MQTTClient_connect(client, &conn_opts));

Step 5 subscribe to topics;

MQTTClient_subscribe(client, TOPIC, QOS);

Step 6: wait until you enter 'Q' or 'Q';

    do 
    {
        ch = getchar();
    } while(ch!='Q' && ch != 'q');

Step 7 cancel subscription;

MQTTClient_unsubscribe(client, TOPIC);

Step 8. Disconnect the client;

 MQTTClient_disconnect(client, 10000);

At this point, the subscription client ends. Generally, the general structure of the subscription client is like this. The difference is the personalization of callback functions.

3.2 release client details

The following functions complete the function of sending.

void *pubClient(void *threadid);

The process is as follows:
   step 1: declare the client and assign value to it through the function;

MQTTClient client;
MQTTClient_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL);

Step 2: set the option to connect to the MQTT server;

MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;

Step 3: connect to the server using the client and connection options;

MQTTClient_connect(client, &conn_opts);

Step 4: set the attribute of sending message;

    pubmsg.payload = PAYLOAD;
    pubmsg.payloadlen = strlen(PAYLOAD);
    pubmsg.qos = QOS;
    pubmsg.retained = 0;

Step 5: send the message circularly;

   MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token);

Step 6: wait all the time and exit the client when CONNECT=0;

Step 7. Disconnect the client;

    MQTTClient_disconnect(client, 10000);

Step 8. Release all memory used by the client;

 MQTTClient_destroy(&client);

At this point, the sending client is over. The general structure of the sending client is the same, but the asynchronous client may be a little different. It is just to design the callback function, and then use the callback function to do some operations when connecting or disconnecting. You can study it yourself.

In order to let you have a deeper understanding, I have explained some functions and structures I have learned.

3.3 related structures

MQTTClient
Definition: typedef void MQTTClient*
Meaning: handle representing MQTT client. Mqttclient successfully called_ After create(), you can get a valid client handle.
MQTTClient_connectOptions
definition:

typedef struct
{
char struct_id[4];//The identification sequence of the structure must be MQTC
int struct_version;//Structure version
/**
Values in 0,1,2,3,4,5:
0-Indicates that there is no SSL option and no serverURIs;
1-Indicates that there is no serverURIs;
2-Indicates that there is no MQTTVersion
3-Indicates that there is no return value;
4-Indicates that there is no binary password option
*/
int keepAliveInterval;
/**
When there is no data related message during this period, the client sends a very small MQTT "ping" message, and the server will confirm this message
*/
int cleansession;
/**
When cleansession is true, session state information is discarded when connecting and disconnecting. Setting cleanup to false preserves session state information
*/
int reliable;
/*
Setting this value to true means that the published message must be completed (acknowledgement received) before another message can be sent
*/
MQTTClient_willOptions* will;
/*
If the program does not use the last will and will function, set this pointer to NULL.
*/
const char* username;//user name
const char* password;//password
int connectTimeout;//Stale time allowed to try to connect
int retryInterval;//Time to attempt reconnection
MQTTClient_SSLOptions* ssl;
/*
If the program does not use the last ssl, set this pointer to NULL.
*/
int serverURIcount;
 
char* const* serverURIs;
/*
url to connect to the server in the format of protocol: / / host: port
*/
int MQTTVersion;
/*
MQTT Version of, MQTTVERSION_3_1(3)´╝îMQTTVERSION_3_1_1 (4) 
*/
struct
{
const char* serverURI;   
int MQTTVersion;     
int sessionPresent;  
} returned;
  struct {
  int len;            
const void* data;  
} binarypwd;
} MQTTClient_connectOptions;

Meaning: structure used to set connection options of MQTTClient.

MQTTClient_message
definition:

typedef struct
{
    char struct_id[4];//The identification sequence of the structure must be MQTM
    int struct_version;//Structure version, must be 0
    int payloadlen;//Length of MQTT information
    void* payload;//Pointer to message payload
    int qos;//Service quality
    int retained;//Reserved flag
    int dup;dup//Flag indicates whether the message is a duplicate. Only meaningful when QoS1 message is received. If true, the client application should take appropriate measures to process duplicate messages.
    int msgid;//Message identifiers are typically reserved for internal use by MQTT clients and servers.
} MQTTClient_message;

Meaning: structure representing MQTT information.

Detailed explanation of correlation function
MQTTClient_create
definition:

DLLExport int MQTTClient_create(    
        MQTTClient *    handle,
        const char *    serverURI,
        const char *    clientId,
        int     persistence_type,
        void *      persistence_context 
    ) 

Function: this function creates an MQTT client for connecting to a specific server and using a specific persistent storage.

MQTTClient_setCallbacks
definition:

DLLExport int MQTTClient_setCallbacks   (   
        MQTTClient      handle,
        void *      context,
        MQTTClient_connectionLost *     cl,
        MQTTClient_messageArrived *     ma,
        MQTTClient_deliveryComplete *   dc 
    )   

Function: this function creates a callback function for a specific client. If your client application does not use a specific callback function, set the relevant parameters to NULL. Call MQTTClient_setCallbacks() puts the client into multi-threaded mode. Any necessary message acknowledgments and status communications are processed in the background without any intervention from the client application.

Note: the MQTT client must be disconnected when calling this function. (that is, first call this function to connect the client).

MQTTClient_connect
definition:

DLLExport int MQTTClient_connect    (   
        MQTTClient      handle,
        MQTTClient_connectOptions *     options 
    )       

Function: this function attempts to connect a previously created client to an MQTT server using the specified options.

MQTTClient_subscribe
definition:

DLLExport int MQTTClient_subscribe  (   
        MQTTClient      handle,
        const char *    topic,
        int     qos 
    )   

MQTTClient_publishMessage
definition:

DLLExport int MQTTClient_publishMessage     (   
        MQTTClient      handle,
        const char *    topicName,
        MQTTClient_message *    msg,
        MQTTClient_deliveryToken *      dt 
    ) 

Function: this function attempts to subscribe customers to a single topic, which may contain wildcards. This function also specifies the quality of service.

MQTTClient_waitForCompletion
definition:

DLLExport int MQTTClient_waitForCompletion  (   
        MQTTClient      handle,
        MQTTClient_deliveryToken    dt,
        unsigned long   timeout 
    )   

Function: the client application calls this function to synchronize the execution of the main thread with the completion and publication of the message. When called, mqttclient_ Waitforcompletement() blocks execution until the message is delivered successfully or the specified time has elapsed.
Return value:
Mqttclient is returned if the message is delivered successfully_ Success (0). If the time has expired or there is a problem detecting the token, the error code will be returned.

Tags: C IoT message queue ci

Posted on Sat, 06 Nov 2021 13:36:46 -0400 by beanman1