使用paho client c封装一个mqtt

1.paho.mqtt.c库编译

git clone https://github.com/eclipse/paho.mqtt.c.git
cd paho.mqtt.c
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=$PWD/../out ../
make && make install

这样就编译好了, 输出在out目录

├── bin
│   └── MQTTVersion
├── include
│   ├── MQTTAsync.h
│   ├── MQTTClient.h
│   ├── MQTTClientPersistence.h
│   ├── MQTTExportDeclarations.h
│   ├── MQTTProperties.h
│   ├── MQTTReasonCodes.h
│   └── MQTTSubscribeOpts.h
├── lib
│   ├── cmake
│   │   └── eclipse-paho-mqtt-c
│   │       ├── eclipse-paho-mqtt-cConfig.cmake
│   │       ├── eclipse-paho-mqtt-cConfig-noconfig.cmake
│   │       └── eclipse-paho-mqtt-cConfigVersion.cmake
│   ├── libpaho-mqtt3a.so -> libpaho-mqtt3a.so.1
│   ├── libpaho-mqtt3a.so.1 -> libpaho-mqtt3a.so.1.3.6
│   ├── libpaho-mqtt3a.so.1.3.6
│   ├── libpaho-mqtt3c.so -> libpaho-mqtt3c.so.1
│   ├── libpaho-mqtt3c.so.1 -> libpaho-mqtt3c.so.1.3.6
│   └── libpaho-mqtt3c.so.1.3.6

2.封装一个client同时支持pub和sub

paho-mqtt提供的例子分别pub和sub, 这里我们基于c++11 做个简单的封装, 使用hash map封装sub topic以及接收到topic的回调

// mqtt_client.h

#ifndef MQTT_CLIENT_H_
#define MQTT_CLIENT_H_

#ifdef __cplusplus
extern "C" {
#endif

typedef struct __mqtt_client * mqtt_client;

/**
 * @brief 创建mqtt client
 * 
 * @param path mqtt server地址, NULL为默认host:1883
 * @param id clientid
 * 
 * @return mqtt handle
 */
mqtt_client mqtt_client_create(const char* path, const char* id);

/**
 * @brief 释放mqtt client
 * 
 * @param client handle
 * @param id clientid
 * 
 * @return 0 成功 其他失败
 */
int mqtt_client_destroy(mqtt_client client);

/**
 * @brief 从mqtt的订阅列表中添加订阅处理
 * 
 * @param client mqtt handle
 * @param topic topic name
 * @param qos qos
 * @param func 处理该消息的函数
 * @param user_data 回传的数据
 * 
 * @return 0 成功 其他失败
 */
void mqtt_client_sub_list_push(mqtt_client client, const char* topic, int qos, int(*func)(mqtt_client client, const char* playload, size_t len, int qos, void* user_data), void* user_data);

/**
 * @brief 从mqtt的订阅列表中移除订阅处理
 * 
 * @param client mqtt handle
 * @param topic topic name
 * 
 * @return 0 成功 其他失败
 */
void mqtt_client_sub_list_pop(mqtt_client client, const char* topic);

/**
 * @brief 订阅消息
 * 
 * @param client mqtt handle
 * 
 * @return 0 成功 其他失败
 */
int mqtt_client_sub(mqtt_client client);

/**
 * @brief 取消订阅消息
 * 
 * @param client mqtt handle
 * @param topic topic name
 * 
 * @return 0 成功 其他失败
 */
int mqtt_client_unsub(mqtt_client client, const char* topic);

/**
 * @brief 发布消息
 * 
 * @param client mqtt handle
 * @param topic topic name
 * @param playload playload
 * @param playload_len playload len
 * 
 * @return 0 成功 其他失败
 */
int mqtt_client_pub(mqtt_client client, const char* topic, const char* playload, size_t playload_len);

#ifdef __cplusplus
}
#endif

#endif // MQTT_CLIENT_H_
#include "paho_mqtt/MQTTClient.h"

#include "mqtt_client/mqtt_client.h"
#include <unordered_map>
#include <string>
#include <functional>
#include <algorithm>
#include <thread>
#include <mutex>
#include <string.h>

using mqtt_msg_cb = std::function<int(mqtt_client client, const char* payload, size_t len, int qos, void* user_data)>;

struct __mqtt_client {
    __mqtt_client() {
        mqtt = NULL;
        conn_opts = MQTTClient_connectOptions_initializer;
        is_running = false;
    }

    MQTTClient mqtt;
    MQTTClient_connectOptions conn_opts;
    bool  is_running;
    std::unordered_map<std::string, std::tuple<int, mqtt_msg_cb, void*>> sub_list;
    std::thread thread;
};

static const char* DEFAULT_ADDR = "localhost:1883";

int messageArrived(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
    printf("delivered- %p, topic: %s\n", context, topicName);

    return 0;
}

void delivered(void *context, MQTTClient_deliveryToken dt)
{
    printf("delivered- %p\n", context);
}

mqtt_client mqtt_client_create(const char* path, const char* id)
{
    __mqtt_client* client = new __mqtt_client();

    if (client) {
        if (client->mqtt == NULL) {
            const char* addr = path;
            if (addr == NULL || strlen(addr) == 0) {
                addr = DEFAULT_ADDR;
            }

            printf("create mqtt client, addr: %s\n", addr);
            int rc = MQTTClient_create(&client->mqtt, addr, id, MQTTCLIENT_PERSISTENCE_NONE, NULL);
            if (rc != MQTTCLIENT_SUCCESS) {
                delete client;
                client = nullptr;
                return nullptr;
            }
        }

        // 连接一次
        int rc = MQTTClient_connect(client->mqtt, &client->conn_opts);
        if (rc != MQTTCLIENT_SUCCESS) {
            printf("failed to connect to mqtt server\n");
        }
        
        client->is_running = true;
        client->thread = std::thread([client]()
        {
            while (client->is_running) {
                if (!MQTTClient_isConnected(client->mqtt)) {    // 断开后重新连接
                    printf("reconnect\n");
                    int rc = MQTTClient_connect(client->mqtt, &client->conn_opts);
                    if (rc != MQTTCLIENT_SUCCESS) {
                        printf("failed to connect to mqtt server\n");
                    }

                    // 重新sub
                    mqtt_client_sub(client);
                } else {
                    char* topic = NULL;
                    int topicLen;
                    MQTTClient_message* msg = NULL;

                    int rc = MQTTClient_receive(client->mqtt, &topic, &topicLen, &msg, 1000);
                    if (msg) {
                        printf("tocc: %s, qos: %d, payload: %s\n", topic, msg->qos, msg->payload);

                        auto it = client->sub_list.find(std::string(topic));
                        if (it != client->sub_list.end()) {
                            std::get<1>(it->second)(client, (const char*)msg->payload, msg->payloadlen, msg->qos, std::get<2>(it->second));
                        }

                        MQTTClient_freeMessage(&msg);
                        MQTTClient_free(topic);
                    }
                    if (rc != MQTTCLIENT_SUCCESS) {
                        printf("err: %d\n", rc);
                    }
                }
            }
        });
    }

    return client;
}

int mqtt_client_destroy(mqtt_client client)
{
    client->is_running = false;

    if (client) {
        if (client->thread.joinable())
            client->thread.join();

        if (client->mqtt) {
            MQTTClient_disconnect(client->mqtt, 500);
            MQTTClient_destroy(&client->mqtt);
        }
        delete client;
        client = nullptr;
    }

    return MQTTCLIENT_SUCCESS;
}

void mqtt_client_sub_list_push(mqtt_client client, const char* topic, int qos, int(*func)(mqtt_client client, const char* payload, size_t len, int qos, void* user_data), void* user_data)
{
    if (client && topic) {
        client->sub_list[std::string(topic)] = std::make_tuple(qos, func, user_data);
    }
}

void mqtt_client_sub_list_pop(mqtt_client client, const char* topic)
{
    if (client && topic) {
        client->sub_list.erase(std::string(topic));
    }
}

int mqtt_client_sub(mqtt_client client)
{
    if (client == NULL || !client->mqtt) {
        return MQTTCLIENT_FAILURE;
    }

    if (!MQTTClient_isConnected(client->mqtt)) {
        return MQTTCLIENT_DISCONNECTED;
    }

    std::for_each(client->sub_list.begin(), client->sub_list.end(),
        [client](const std::unordered_map<std::string, std::tuple<int, mqtt_msg_cb, void*>>::value_type& element) {

        printf("MQTTClient_subscribe, topic :%s, qos: %d\n", element.first.c_str(), std::get<0>(element.second));
        MQTTClient_subscribe(client->mqtt, element.first.c_str(), std::get<0>(element.second));
    });

    return MQTTCLIENT_SUCCESS;
}

int mqtt_client_unsub(mqtt_client client, const char* topic)
{
    if (client == NULL || !client->mqtt) {
        return MQTTCLIENT_FAILURE;
    }

    if (!MQTTClient_isConnected(client->mqtt)) {
        return MQTTCLIENT_DISCONNECTED;
    }

    auto it = client->sub_list.find(std::string(topic));
    if (it != client->sub_list.end()) {
        MQTTClient_unsubscribe(client->mqtt, topic);
        client->sub_list.erase(it);
    }

    return MQTTCLIENT_SUCCESS;
}

int mqtt_client_pub(mqtt_client client, const char* topic, const char* payload, size_t playload_len)
{
    if (client == NULL || !client->mqtt) {
        return MQTTCLIENT_FAILURE;
    }

    if (!MQTTClient_isConnected(client->mqtt)) {
        return MQTTCLIENT_DISCONNECTED;
    }

    printf("MQTTClient_publish, topic :%s, playlad: %.*s\n", topic, playload_len, payload);
    MQTTClient_publish(client->mqtt, topic, playload_len, payload, 1, 0, NULL);

    return MQTTCLIENT_SUCCESS;
}

测试


#include <unistd.h>
#ifndef _WIN32
#include <sys/prctl.h>
#endif

#include "mqtt_client/mqtt_client.h"

#include <string>
#include <thread>

const static float TARGET_X = 0.0f;
const static float TARGET_Y = 0.0f;

void testThread(int id, mqtt_client client)
{
    size_t count = 0;
    while (1) {
        if (count >= id) {
            break;
        }

        std::string str = "{\"x\":" + std::to_string(TARGET_X) + ",\"y\":" + std::to_string(TARGET_Y) + "}";

        mqtt_client_pub(client, "/pibot/body/move", str.c_str(), str.length());
        usleep(100*1000);
    }
}

int pos_update_cb(mqtt_client client, const char* playload, size_t len, int qos, void* user_data)
{
    printf("pos_update_cb, playload: %.*s, qos: %d/n", len, playload, qos);

    return 0;
}

int main()
{
    mqtt_client client = mqtt_client_create(NULL, "pibot");
    if (client == NULL) {
        return 0;
    }

    mqtt_client_sub_list_push(client, "/pibot/pos", 2, pos_update_cb, NULL);

    if (mqtt_client_sub(client) != 0) {
        printf("err\n");
    }

    std::thread t1 = std::thread(testThread, 10000, client);
    std::thread t2 = std::thread(testThread, 20000, client);
    t1.join();
    t2.join();
    
    mqtt_client_destroy(client);

    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270