应用——MQTT客户端开发

MQTT客户端开发

一、项目概述

基于Paho MQTT C客户端库实现的设备与MQTT服务器通信程序,主要用于设备属性上报和数据订阅。

二、头文件解析 (head.h)

cpp 复制代码
#ifndef HEAD_H
#define HEAD_H

#include <MQTTAsync.h>
#include <MQTTClient.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>

// MQTT服务器地址(OneNET平台)
#define NEW_ADDRESS     "tcp://183.230.40.96:端口号"

// 设备信息
#define DEV_NAME        "设备名字"
#define CLIENTID        DEV_NAME
#define PRODUCT_ID      "ip"

// 认证密码(包含版本、资源、过期时间、签名等信息)
#define PASSWD          "认证密码"

// MQTT配置参数
#define QOS             0           // 服务质量等级
#define TIMEOUT         10000L     // 超时时间(ms)

#endif // HEAD_H

三、主程序实现 (main.c)

cpp 复制代码
#include <stdio.h>
#include "head.h"

// 全局变量定义
static char topic[2][200] = {0};                    // 主题数组
static MQTTClient client;                          // MQTT客户端实例
static int id = 10000;                             // 消息ID计数器
volatile static MQTTClient_deliveryToken deliveredtoken; // 消息传递令牌

/**
 * @brief 构建MQTT主题
 * @param dev_name 设备名称
 * @param pro_id 产品ID
 */
void pack_topic(char *dev_name, char *pro_id)
{
    // 主题格式说明:
    // topic[0]: 订阅主题 - 用于接收服务器响应
    // topic[1]: 发布主题 - 用于发送设备数据
    sprintf(topic[0], "$sys/%s/%s/thing/property/post/reply", pro_id, dev_name);
    sprintf(topic[1], "$sys/%s/%s/thing/property/post", pro_id, dev_name);
}

/**
 * @brief 消息发送确认回调函数
 * @param context 上下文
 * @param dt 传递令牌
 */
void delivered(void *context, MQTTClient_deliveryToken dt)
{
    printf("消息发送成功,令牌值: %d\n", dt);
    deliveredtoken = dt;
}

/**
 * @brief 消息接收回调函数
 * @param context 上下文
 * @param topicName 主题名称
 * @param topicLen 主题长度
 * @param message 消息内容
 * @return 处理结果
 */
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
    int i;
    char *payloadptr;

    printf("\n=== 收到新消息 ===\n");
    printf("主题: %s\n", topicName);
    printf("内容: ");

    // 逐字符打印消息内容
    payloadptr = (char *)message->payload;
    for (i = 0; i < message->payloadlen; i++)
    {
        putchar(*payloadptr++);
    }
    putchar('\n');
    printf("==================\n");

    // 释放资源
    MQTTClient_freeMessage(&message);
    MQTTClient_free(topicName);
    return 1;
}

/**
 * @brief 连接断开回调函数
 * @param context 上下文
 * @param cause 断开原因
 */
void connlost(void *context, char *cause)
{
    printf("\n!!! 连接已断开 !!!\n");
    printf("原因: %s\n", cause);
}

/**
 * @brief MQTT客户端初始化
 * @return 初始化结果
 */
int mqtt_init()
{
    int rc;
    
    // 1. 构建主题
    pack_topic(DEV_NAME, PRODUCT_ID);
    
    // 2. 创建MQTT客户端
    rc = MQTTClient_create(&client, NEW_ADDRESS, CLIENTID,
                           MQTTCLIENT_PERSISTENCE_NONE, NULL);
    if (MQTTCLIENT_SUCCESS != rc)
    {
        printf("MQTT客户端创建失败...\n");
        exit(1);
    }
    
    // 3. 配置连接选项
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    conn_opts.keepAliveInterval = 20;    // 心跳间隔(秒)
    conn_opts.cleansession = 1;          // 清除会话
    conn_opts.username = PRODUCT_ID;     // 用户名(产品ID)
    conn_opts.password = PASSWD;         // 密码
    
    // 4. 设置回调函数
    rc = MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered);
    if (MQTTCLIENT_SUCCESS != rc)
    {
        printf("回调函数设置失败,错误码: %d\n", rc);
        exit(EXIT_FAILURE);
    }
    
    // 5. 连接到服务器
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
    {
        printf("连接服务器失败,错误码: %d\n", rc);
        exit(EXIT_FAILURE);
    }
    
    // 6. 订阅主题
    MQTTClient_subscribe(client, topic[0], QOS);
    printf("订阅信息:\n");
    printf("  主题: %s\n", topic[0]);
    printf("  客户端ID: %s\n", CLIENTID);
    printf("  QoS等级: %d\n\n", QOS);
    
    return rc;
}

/**
 * @brief 发送MQTT消息
 * @param key 数据键名
 * @param value 数据值
 * @return 发送结果
 */
int mqtt_send(char *key, int value)
{
    MQTTClient_deliveryToken deliveryToken;
    MQTTClient_message test2_pubmsg = MQTTClient_message_initializer;
    char message[1024] = {0};
    
    // 配置消息参数
    test2_pubmsg.qos = QOS;
    test2_pubmsg.retained = 0;
    test2_pubmsg.payload = message;
    
    // 构建JSON格式消息体
    // 格式: {"id":"消息ID","version":"1.0","params":{"键名":{"value":值}}}
    sprintf(message, "{\"id\":\"%d\",\"version\":\"1.0\",\"params\":{\"%s\":{\"value\":%d}}}",
            id++, key, value);
    test2_pubmsg.payloadlen = strlen(message);
    
    printf("发送消息: %s\n", message);
    
    // 发布消息
    int rc = MQTTClient_publishMessage(client, topic[1], &test2_pubmsg, &deliveryToken);
    if (MQTTCLIENT_SUCCESS != rc)
    {
        printf("消息发布失败... 线程ID: %lu\n", pthread_self());
        exit(1);
    }
    
    printf("等待消息发布完成... (超时: %d秒)\n", (int)(TIMEOUT / 1000));
    MQTTClient_waitForCompletion(client, deliveryToken, TIMEOUT);
    
    return rc;
}

/**
 * @brief MQTT客户端清理
 */
void mqtt_deinit()
{
    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);
    printf("MQTT客户端已断开连接并销毁\n");
}

/**
 * @brief 主函数
 * @return 程序退出码
 */
int main(void)
{
    printf("=== MQTT客户端程序启动 ===\n");
    
    // 1. 初始化MQTT客户端
    mqtt_init();
    printf("MQTT客户端初始化成功,开始发送数据...\n\n");
    
    // 2. 主循环:定时发送数据
    while (1)
    {
        // 生成随机温度值(1-100)
        int value = rand() % 100 + 1;
        
        // 发送温度数据
        mqtt_send("tmp", value);
        
        // 等待25秒
        sleep(25);
    }
    
    // 3. 清理资源(实际上不会执行到这里)
    mqtt_deinit();
    
    return 0;
}

四、关键技术点

1. MQTT主题设计

  • 发布主题 : $sys/{产品ID}/{设备名}/thing/property/post

  • 订阅主题 : $sys/{产品ID}/{设备名}/thing/property/post/reply

  • 采用OneNET平台的标准主题格式

2. 消息格式

复制代码
{
    "id": "消息ID",
    "version": "1.0",
    "params": {
        "数据键名": {
            "value": 数据值
        }
    }
}

3. 认证机制

  • 使用OneNET平台的Token认证方式

  • 密码包含版本、资源路径、过期时间、签名方法等信息

4. 回调机制

  • connlost: 处理连接断开事件

  • msgarrvd: 处理接收到的消息

  • delivered: 处理消息发送确认

5. 线程安全

  • 使用volatile关键字确保令牌变量的可见性

  • 适合在多线程环境中使用

五、编译和运行

1. 编译命令

复制代码
gcc -o mqtt_client main.c -lpaho-mqtt3c -lpthread

2. 运行程序

复制代码
./mqtt_client

3. 依赖库

  • Paho MQTT C Client Library

  • pthread 线程库

六、注意事项

  1. 密码有效期 : 当前密码中的et=1837255523表示过期时间(时间戳),需要定期更新

  2. QoS级别: 当前设置为0(最多一次),可根据可靠性需求调整

  3. 资源管理: 确保及时释放MQTT消息资源

  4. 错误处理: 生产环境需要更完善的错误处理和重连机制

  5. 心跳间隔: 当前设置为20秒,根据网络状况可适当调整

七、扩展建议

  1. 配置文件: 将设备信息、服务器地址等配置外置

  2. 日志系统: 添加更完善的日志记录

  3. 重连机制: 实现自动重连和连接状态监控

  4. 多线程: 将消息发送和接收分离到不同线程

  5. 数据持久化: 在网络异常时缓存未发送的数据

这个MQTT客户端程序实现了基本的设备数据上报功能,可以作为物联网设备接入OneNET平台的基础框架。

相关推荐
蓝天下的守望者1 天前
由continue引发的一个debug灾难
算法·systemverilog
乾元1 天前
企业无线的 AI 频谱与功率自动优化——从人工勘测到“可学习的无线网络”(含真实室内工程案例)
服务器·网络·人工智能·网络协议·安全·信息与通信
明洞日记1 天前
【VTK手册034】 vtkGeometryFilter 深度解析:高性能几何提取与转换专家
c++·图像处理·算法·ai·vtk·图形渲染
暴风鱼划水1 天前
三维重建【4-C】3D Gaussian Splatting:代码调试方法
c语言·开发语言
额呃呃1 天前
operator new/delete
开发语言·c++·算法
superman超哥1 天前
Rust `‘static` 生命周期:从字面意义到深层语义
开发语言·后端·rust·生命周期·编程语言·rust static·深层语义
平生不喜凡桃李1 天前
Google C++ Style Guide : 变量与函数名
开发语言·c++·google c++
互联网哪些事情1 天前
海外服务器安装宝塔面板的步骤
运维·服务器·海外服务器安装宝塔面板