基于 C/C++ 的 MQTT 物联网通信协议实现

一、架构设计

1.1 整体架构

复制代码
┌─────────────────────────────────────────────────┐
│                  应用层                          │
├─────────────────────────────────────────────────┤
│                MQTT 客户端库                     │
│  ┌─────────┬──────────┬──────────┬──────────┐  │
│  │连接管理 │ 消息处理 │ QoS控制  │ 主题管理 │  │
│  └─────────┴──────────┴──────────┴──────────┘  │
├─────────────────────────────────────────────────┤
│                协议解析层                        │
│          ┌────────────┬────────────┐           │
│          │ 报文编码   │ 报文解码   │           │
│          └────────────┴────────────┘           │
├─────────────────────────────────────────────────┤
│                 传输层                           │
│          TCP / SSL / WebSocket                 │
├─────────────────────────────────────────────────┤
│                 网络层                           │
│             Socket 接口                          │
└─────────────────────────────────────────────────┘

1.2 特性支持

  • MQTT 3.1.1 & 5.0
  • QoS 0/1/2
  • 遗嘱消息
  • 保留消息
  • 主题通配符
  • 会话持久化
  • SSL/TLS 加密
  • WebSocket 支持
  • 断线重连
  • 多线程安全

二、核心数据结构

2.1 基础定义

c 复制代码
// mqtt_defs.h
#ifndef __MQTT_DEFS_H
#define __MQTT_DEFS_H

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

// 版本定义
#define MQTT_PROTOCOL_V3_1     3
#define MQTT_PROTOCOL_V3_1_1   4
#define MQTT_PROTOCOL_V5       5

// 消息类型
typedef enum {
    MQTT_MSG_CONNECT        = 1,
    MQTT_MSG_CONNACK        = 2,
    MQTT_MSG_PUBLISH        = 3,
    MQTT_MSG_PUBACK         = 4,
    MQTT_MSG_PUBREC         = 5,
    MQTT_MSG_PUBREL         = 6,
    MQTT_MSG_PUBCOMP        = 7,
    MQTT_MSG_SUBSCRIBE      = 8,
    MQTT_MSG_SUBACK         = 9,
    MQTT_MSG_UNSUBSCRIBE    = 10,
    MQTT_MSG_UNSUBACK       = 11,
    MQTT_MSG_PINGREQ        = 12,
    MQTT_MSG_PINGRESP       = 13,
    MQTT_MSG_DISCONNECT     = 14,
    MQTT_MSG_AUTH           = 15
} mqtt_msg_type_t;

// QoS 级别
typedef enum {
    MQTT_QOS_0 = 0,  // 最多一次
    MQTT_QOS_1 = 1,  // 至少一次
    MQTT_QOS_2 = 2   // 恰好一次
} mqtt_qos_t;

// 连接返回码 (3.1.1)
typedef enum {
    CONNACK_ACCEPTED                = 0x00,
    CONNACK_REFUSED_PROTOCOL        = 0x01,
    CONNACK_REFUSED_IDENTIFIER      = 0x02,
    CONNACK_REFUSED_SERVER          = 0x03,
    CONNACK_REFUSED_CREDENTIALS     = 0x04,
    CONNACK_REFUSED_AUTHORIZATION   = 0x05
} mqtt_connack_t;

// 订阅返回码
typedef enum {
    SUBACK_SUCCESS_QOS_0    = 0x00,
    SUBACK_SUCCESS_QOS_1    = 0x01,
    SUBACK_SUCCESS_QOS_2    = 0x02,
    SUBACK_FAILURE          = 0x80
} mqtt_suback_t;

// 错误码
typedef enum {
    MQTT_SUCCESS               = 0,
    MQTT_ERROR_INIT            = -1,
    MQTT_ERROR_CONNECT         = -2,
    MQTT_ERROR_SEND            = -3,
    MQTT_ERROR_RECV            = -4,
    MQTT_ERROR_TIMEOUT         = -5,
    MQTT_ERROR_MEMORY          = -6,
    MQTT_ERROR_PROTOCOL        = -7,
    MQTT_ERROR_SSL             = -8,
    MQTT_ERROR_SOCKET          = -9,
    MQTT_ERROR_DISCONNECTED    = -10
} mqtt_error_t;

// 连接状态
typedef enum {
    MQTT_STATE_DISCONNECTED   = 0,
    MQTT_STATE_CONNECTING     = 1,
    MQTT_STATE_CONNECTED      = 2,
    MQTT_STATE_DISCONNECTING  = 3
} mqtt_state_t;

// 消息结构
typedef struct {
    uint8_t     *payload;       // 消息内容
    uint32_t    payload_len;    // 消息长度
    uint16_t    packet_id;      // 报文ID
    uint8_t     qos;           // QoS级别
    uint8_t     retain;        // 保留标志
    uint8_t     dup;           // 重复标志
    char        *topic;        // 主题
} mqtt_message_t;

// 遗嘱消息
typedef struct {
    char        *topic;        // 遗嘱主题
    uint8_t     *message;      // 遗嘱消息
    uint32_t    msg_len;       // 消息长度
    uint8_t     qos;          // QoS级别
    uint8_t     retain;       // 保留标志
} mqtt_will_t;

// 回调函数类型
typedef void (*mqtt_message_cb)(void *client, mqtt_message_t *msg);
typedef void (*mqtt_connect_cb)(void *client, int result);
typedef void (*mqtt_disconnect_cb)(void *client, int reason);
typedef void (*mqtt_log_cb)(void *client, int level, const char *format, ...);

#ifdef __cplusplus
}
#endif

#endif

2.2 客户端结构

c 复制代码
// mqtt_client.h
#ifndef __MQTT_CLIENT_H
#define __MQTT_CLIENT_H

#include "mqtt_defs.h"

#ifdef __cplusplus
extern "C" {
#endif

// 客户端配置
typedef struct {
    // 服务器信息
    const char  *host;          // 服务器地址
    int         port;          // 端口
    const char  *client_id;     // 客户端ID
    
    // 认证信息
    const char  *username;      // 用户名
    const char  *password;      // 密码
    
    // 连接参数
    uint16_t    keep_alive;     // 保活时间(秒)
    uint8_t     clean_session;  // 清理会话
    uint8_t     protocol_version; // 协议版本
    
    // 遗嘱消息
    mqtt_will_t *will;         // 遗嘱消息
    
    // SSL配置
    const char  *ca_cert;       // CA证书
    const char  *client_cert;   // 客户端证书
    const char  *client_key;    // 客户端私钥
    
    // 网络参数
    int         timeout;        // 超时时间(ms)
    int         reconnect_delay; // 重连延迟(ms)
    int         reconnect_max;  // 最大重连次数
    
    // 回调函数
    mqtt_message_cb     message_cb;     // 消息回调
    mqtt_connect_cb     connect_cb;     // 连接回调
    mqtt_disconnect_cb  disconnect_cb;  // 断开回调
    mqtt_log_cb         log_cb;         // 日志回调
    
    // 用户数据
    void        *user_data;     // 用户数据
} mqtt_config_t;

// 客户端句柄
typedef struct mqtt_client_s mqtt_client_t;

// API函数
mqtt_client_t* mqtt_client_new(void);
int mqtt_client_init(mqtt_client_t *client, mqtt_config_t *config);
int mqtt_client_connect(mqtt_client_t *client);
int mqtt_client_disconnect(mqtt_client_t *client);
int mqtt_client_publish(mqtt_client_t *client, 
                        const char *topic, 
                        const void *payload, 
                        int payload_len, 
                        int qos, 
                        int retain);
int mqtt_client_subscribe(mqtt_client_t *client, 
                          const char *topic, 
                          int qos);
int mqtt_client_unsubscribe(mqtt_client_t *client, 
                            const char *topic);
int mqtt_client_ping(mqtt_client_t *client);
int mqtt_client_loop(mqtt_client_t *client, int timeout_ms);
int mqtt_client_is_connected(mqtt_client_t *client);
void mqtt_client_destroy(mqtt_client_t *client);

#ifdef __cplusplus
}
#endif

#endif

三、协议实现

3.1 报文编码/解码

c 复制代码
// mqtt_packet.c
#include "mqtt_packet.h"
#include <string.h>
#include <stdlib.h>

// 编码固定头
static int encode_fixed_header(uint8_t *buf, 
                               uint8_t msg_type, 
                               uint8_t dup, 
                               uint8_t qos, 
                               uint8_t retain, 
                               uint32_t remaining_len) {
    int pos = 0;
    
    // 第一个字节
    buf[pos++] = (msg_type << 4) | (dup << 3) | (qos << 1) | retain;
    
    // 编码剩余长度
    do {
        uint8_t encoded = remaining_len % 128;
        remaining_len /= 128;
        if (remaining_len > 0) {
            encoded |= 0x80;
        }
        buf[pos++] = encoded;
    } while (remaining_len > 0 && pos < 5);
    
    return pos;
}

// 解码固定头
static int decode_fixed_header(const uint8_t *buf, 
                               int buf_len,
                               uint8_t *msg_type, 
                               uint8_t *dup, 
                               uint8_t *qos, 
                               uint8_t *retain, 
                               uint32_t *remaining_len) {
    if (buf_len < 2) return -1;
    
    int pos = 0;
    uint8_t first_byte = buf[pos++];
    
    *msg_type = (first_byte >> 4) & 0x0F;
    *dup = (first_byte >> 3) & 0x01;
    *qos = (first_byte >> 1) & 0x03;
    *retain = first_byte & 0x01;
    
    // 解码剩余长度
    uint32_t multiplier = 1;
    uint8_t encoded;
    *remaining_len = 0;
    
    do {
        if (pos >= buf_len) return -1;
        encoded = buf[pos++];
        *remaining_len += (encoded & 0x7F) * multiplier;
        multiplier *= 128;
    } while ((encoded & 0x80) != 0 && pos < 5);
    
    return pos;
}

// 编码字符串
static int encode_string(uint8_t *buf, const char *str) {
    uint16_t len = strlen(str);
    buf[0] = (len >> 8) & 0xFF;
    buf[1] = len & 0xFF;
    memcpy(buf + 2, str, len);
    return len + 2;
}

// 解码字符串
static int decode_string(const uint8_t *buf, char **str) {
    uint16_t len = (buf[0] << 8) | buf[1];
    *str = malloc(len + 1);
    if (!*str) return -1;
    memcpy(*str, buf + 2, len);
    (*str)[len] = '\0';
    return len + 2;
}

// 编码CONNECT报文
int mqtt_encode_connect(uint8_t *buf, 
                        const char *client_id,
                        const char *username,
                        const char *password,
                        uint16_t keep_alive,
                        uint8_t clean_session,
                        mqtt_will_t *will) {
    int pos = 0;
    
    // 固定头
    pos += encode_fixed_header(buf, MQTT_MSG_CONNECT, 0, 0, 0, 0);
    
    // 可变头
    pos += encode_string(buf + pos, "MQTT");
    buf[pos++] = 0x04;  // 协议版本 3.1.1
    
    // 连接标志
    uint8_t flags = 0;
    if (clean_session) flags |= 0x02;
    if (will) {
        flags |= 0x04;
        flags |= (will->qos & 0x03) << 3;
        if (will->retain) flags |= 0x20;
    }
    if (username) flags |= 0x80;
    if (password) flags |= 0x40;
    buf[pos++] = flags;
    
    // 保活时间
    buf[pos++] = (keep_alive >> 8) & 0xFF;
    buf[pos++] = keep_alive & 0xFF;
    
    // 有效载荷
    pos += encode_string(buf + pos, client_id);
    
    if (will) {
        pos += encode_string(buf + pos, will->topic);
        buf[pos++] = (will->msg_len >> 8) & 0xFF;
        buf[pos++] = will->msg_len & 0xFF;
        memcpy(buf + pos, will->message, will->msg_len);
        pos += will->msg_len;
    }
    
    if (username) {
        pos += encode_string(buf + pos, username);
    }
    
    if (password) {
        pos += encode_string(buf + pos, password);
    }
    
    // 更新剩余长度
    uint32_t remaining_len = pos - 1;  // 减去第一个字节
    int header_len = 1;
    uint8_t temp[5];
    do {
        uint8_t encoded = remaining_len % 128;
        remaining_len /= 128;
        if (remaining_len > 0) {
            encoded |= 0x80;
        }
        temp[header_len++] = encoded;
    } while (remaining_len > 0 && header_len < 5);
    
    memmove(buf + header_len, buf + 1, pos - 1);
    memcpy(buf + 1, temp + 1, header_len - 1);
    
    return pos + header_len - 1;
}

// 解码CONNACK报文
int mqtt_decode_connack(const uint8_t *buf, 
                        int buf_len,
                        uint8_t *session_present,
                        uint8_t *return_code) {
    if (buf_len < 4) return -1;
    
    uint8_t msg_type, dup, qos, retain;
    uint32_t remaining_len;
    int pos = decode_fixed_header(buf, buf_len, &msg_type, &dup, &qos, &retain, &remaining_len);
    
    if (msg_type != MQTT_MSG_CONNACK) return -1;
    if (remaining_len != 2) return -1;
    
    *session_present = buf[pos++] & 0x01;
    *return_code = buf[pos++];
    
    return pos;
}

// 编码PUBLISH报文
int mqtt_encode_publish(uint8_t *buf,
                        const char *topic,
                        const uint8_t *payload,
                        uint32_t payload_len,
                        uint16_t packet_id,
                        uint8_t qos,
                        uint8_t retain,
                        uint8_t dup) {
    int pos = 0;
    
    // 先留出固定头位置
    pos += 5;  // 最大长度
    
    // 编码主题
    int topic_len = encode_string(buf + pos, topic);
    pos += topic_len;
    
    // 编码Packet ID
    if (qos > 0) {
        buf[pos++] = (packet_id >> 8) & 0xFF;
        buf[pos++] = packet_id & 0xFF;
    }
    
    // 编码有效载荷
    if (payload_len > 0) {
        memcpy(buf + pos, payload, payload_len);
        pos += payload_len;
    }
    
    // 编码固定头
    uint32_t remaining_len = pos - 5;
    int header_len = encode_fixed_header(buf, MQTT_MSG_PUBLISH, dup, qos, retain, remaining_len);
    
    // 移动数据
    if (header_len != 5) {
        memmove(buf + header_len, buf + 5, remaining_len);
    }
    
    return header_len + remaining_len;
}

// 解码PUBLISH报文
int mqtt_decode_publish(const uint8_t *buf,
                        int buf_len,
                        mqtt_message_t *msg) {
    uint8_t msg_type, dup, qos, retain;
    uint32_t remaining_len;
    int pos = decode_fixed_header(buf, buf_len, &msg_type, &dup, &qos, &retain, &remaining_len);
    
    if (msg_type != MQTT_MSG_PUBLISH) return -1;
    
    // 解码主题
    char *topic = NULL;
    int topic_len = decode_string(buf + pos, &topic);
    if (topic_len < 0) return -1;
    pos += topic_len;
    
    msg->topic = topic;
    msg->dup = dup;
    msg->qos = qos;
    msg->retain = retain;
    
    // 解码Packet ID
    if (qos > 0) {
        if (buf_len - pos < 2) {
            free(topic);
            return -1;
        }
        msg->packet_id = (buf[pos] << 8) | buf[pos + 1];
        pos += 2;
    } else {
        msg->packet_id = 0;
    }
    
    // 解码有效载荷
    int payload_len = remaining_len - (pos - (buf[1] & 0x80 ? 3 : 2));
    if (payload_len > 0) {
        msg->payload = malloc(payload_len);
        if (!msg->payload) {
            free(topic);
            return -1;
        }
        memcpy(msg->payload, buf + pos, payload_len);
        msg->payload_len = payload_len;
    } else {
        msg->payload = NULL;
        msg->payload_len = 0;
    }
    
    return pos + payload_len;
}

3.2 客户端实现

c 复制代码
// mqtt_client.c
#include "mqtt_client.h"
#include "mqtt_packet.h"
#include "mqtt_network.h"
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>

// 客户端内部结构
struct mqtt_client_s {
    // 配置
    mqtt_config_t config;
    
    // 网络
    mqtt_network_t *network;
    mqtt_state_t state;
    
    // 报文ID
    uint16_t next_packet_id;
    
    // 时间戳
    time_t last_send_time;
    time_t last_recv_time;
    time_t last_ping_time;
    
    // 接收缓冲区
    uint8_t *recv_buf;
    int recv_buf_size;
    int recv_buf_len;
    
    // 发送队列
    struct msg_queue {
        uint8_t *data;
        int len;
        int qos;
        uint16_t packet_id;
        int retry_count;
        time_t timestamp;
        struct msg_queue *next;
    } *send_queue;
    
    // 订阅列表
    struct subscription {
        char *topic;
        int qos;
        struct subscription *next;
    } *subscriptions;
    
    // 线程安全
    pthread_mutex_t lock;
    
    // 统计
    uint32_t msg_sent;
    uint32_t msg_recv;
    uint32_t bytes_sent;
    uint32_t bytes_recv;
};

// 创建客户端
mqtt_client_t* mqtt_client_new(void) {
    mqtt_client_t *client = calloc(1, sizeof(mqtt_client_t));
    if (!client) return NULL;
    
    client->state = MQTT_STATE_DISCONNECTED;
    client->next_packet_id = 1;
    
    // 初始化互斥锁
    pthread_mutex_init(&client->lock, NULL);
    
    // 初始化接收缓冲区
    client->recv_buf_size = 4096;
    client->recv_buf = malloc(client->recv_buf_size);
    if (!client->recv_buf) {
        free(client);
        return NULL;
    }
    
    return client;
}

// 初始化客户端
int mqtt_client_init(mqtt_client_t *client, mqtt_config_t *config) {
    if (!client || !config) return MQTT_ERROR_INIT;
    
    pthread_mutex_lock(&client->lock);
    
    // 复制配置
    memcpy(&client->config, config, sizeof(mqtt_config_t));
    
    // 创建网络模块
    client->network = mqtt_network_new();
    if (!client->network) {
        pthread_mutex_unlock(&client->lock);
        return MQTT_ERROR_INIT;
    }
    
    // 初始化网络
    mqtt_network_config_t net_config = {
        .timeout = config->timeout,
        .ca_cert = config->ca_cert,
        .client_cert = config->client_cert,
        .client_key = config->client_key
    };
    
    if (mqtt_network_init(client->network, &net_config) != 0) {
        mqtt_network_destroy(client->network);
        client->network = NULL;
        pthread_mutex_unlock(&client->lock);
        return MQTT_ERROR_INIT;
    }
    
    pthread_mutex_unlock(&client->lock);
    return MQTT_SUCCESS;
}

// 连接服务器
int mqtt_client_connect(mqtt_client_t *client) {
    if (!client) return MQTT_ERROR_INIT;
    
    pthread_mutex_lock(&client->lock);
    
    if (client->state != MQTT_STATE_DISCONNECTED) {
        pthread_mutex_unlock(&client->lock);
        return MQTT_ERROR_CONNECT;
    }
    
    client->state = MQTT_STATE_CONNECTING;
    
    // 连接服务器
    int ret = mqtt_network_connect(client->network, 
                                   client->config.host, 
                                   client->config.port);
    if (ret != 0) {
        client->state = MQTT_STATE_DISCONNECTED;
        pthread_mutex_unlock(&client->lock);
        
        if (client->config.log_cb) {
            client->config.log_cb(client, 2, "Connect failed: %s:%d", 
                                  client->config.host, client->config.port);
        }
        
        return MQTT_ERROR_CONNECT;
    }
    
    // 编码CONNECT报文
    uint8_t buf[1024];
    int len = mqtt_encode_connect(buf,
                                  client->config.client_id,
                                  client->config.username,
                                  client->config.password,
                                  client->config.keep_alive,
                                  client->config.clean_session,
                                  client->config.will);
    
    // 发送CONNECT报文
    ret = mqtt_network_send(client->network, buf, len);
    if (ret != len) {
        mqtt_network_disconnect(client->network);
        client->state = MQTT_STATE_DISCONNECTED;
        pthread_mutex_unlock(&client->lock);
        return MQTT_ERROR_SEND;
    }
    
    client->last_send_time = time(NULL);
    client->bytes_sent += len;
    
    // 接收CONNACK
    uint8_t connack_buf[4];
    ret = mqtt_network_recv(client->network, connack_buf, 4, 5000);
    if (ret < 4) {
        mqtt_network_disconnect(client->network);
        client->state = MQTT_STATE_DISCONNECTED;
        pthread_mutex_unlock(&client->lock);
        return MQTT_ERROR_TIMEOUT;
    }
    
    // 解码CONNACK
    uint8_t session_present, return_code;
    if (mqtt_decode_connack(connack_buf, ret, &session_present, &return_code) < 0) {
        mqtt_network_disconnect(client->network);
        client->state = MQTT_STATE_DISCONNECTED;
        pthread_mutex_unlock(&client->lock);
        return MQTT_ERROR_PROTOCOL;
    }
    
    if (return_code != CONNACK_ACCEPTED) {
        mqtt_network_disconnect(client->network);
        client->state = MQTT_STATE_DISCONNECTED;
        pthread_mutex_unlock(&client->lock);
        
        if (client->config.connect_cb) {
            client->config.connect_cb(client, return_code);
        }
        
        return MQTT_ERROR_CONNECT;
    }
    
    client->state = MQTT_STATE_CONNECTED;
    client->last_recv_time = time(NULL);
    client->bytes_recv += ret;
    
    pthread_mutex_unlock(&client->lock);
    
    if (client->config.connect_cb) {
        client->config.connect_cb(client, CONNACK_ACCEPTED);
    }
    
    if (client->config.log_cb) {
        client->config.log_cb(client, 1, "Connected to %s:%d", 
                              client->config.host, client->config.port);
    }
    
    return MQTT_SUCCESS;
}

// 发布消息
int mqtt_client_publish(mqtt_client_t *client, 
                        const char *topic, 
                        const void *payload, 
                        int payload_len, 
                        int qos, 
                        int retain) {
    if (!client || !topic) return MQTT_ERROR_INIT;
    
    pthread_mutex_lock(&client->lock);
    
    if (client->state != MQTT_STATE_CONNECTED) {
        pthread_mutex_unlock(&client->lock);
        return MQTT_ERROR_DISCONNECTED;
    }
    
    // 生成Packet ID
    uint16_t packet_id = 0;
    if (qos > 0) {
        packet_id = client->next_packet_id++;
        if (client->next_packet_id == 0) {
            client->next_packet_id = 1;
        }
    }
    
    // 编码PUBLISH报文
    uint8_t buf[2048];
    int len = mqtt_encode_publish(buf, topic, payload, payload_len, 
                                  packet_id, qos, retain, 0);
    
    // 发送报文
    int ret = mqtt_network_send(client->network, buf, len);
    if (ret != len) {
        pthread_mutex_unlock(&client->lock);
        return MQTT_ERROR_SEND;
    }
    
    client->last_send_time = time(NULL);
    client->msg_sent++;
    client->bytes_sent += len;
    
    // QoS 1/2 需要确认
    if (qos > 0) {
        // 添加到发送队列等待确认
        struct msg_queue *msg = calloc(1, sizeof(struct msg_queue));
        if (msg) {
            msg->data = malloc(len);
            if (msg->data) {
                memcpy(msg->data, buf, len);
                msg->len = len;
                msg->qos = qos;
                msg->packet_id = packet_id;
                msg->timestamp = time(NULL);
                msg->retry_count = 0;
                
                // 添加到队列头部
                msg->next = client->send_queue;
                client->send_queue = msg;
            } else {
                free(msg);
            }
        }
    }
    
    pthread_mutex_unlock(&client->lock);
    
    if (client->config.log_cb) {
        client->config.log_cb(client, 1, "Published to %s, QoS: %d, Len: %d", 
                              topic, qos, payload_len);
    }
    
    return MQTT_SUCCESS;
}

// 订阅主题
int mqtt_client_subscribe(mqtt_client_t *client, 
                          const char *topic, 
                          int qos) {
    if (!client || !topic) return MQTT_ERROR_INIT;
    
    pthread_mutex_lock(&client->lock);
    
    if (client->state != MQTT_STATE_CONNECTED) {
        pthread_mutex_unlock(&client->lock);
        return MQTT_ERROR_DISCONNECTED;
    }
    
    // 生成Packet ID
    uint16_t packet_id = client->next_packet_id++;
    if (client->next_packet_id == 0) {
        client->next_packet_id = 1;
    }
    
    // 编码SUBSCRIBE报文
    uint8_t buf[512];
    int pos = 0;
    
    // 固定头
    pos += encode_fixed_header(buf, MQTT_MSG_SUBSCRIBE, 0, 1, 0, 0);
    
    // Packet ID
    buf[pos++] = (packet_id >> 8) & 0xFF;
    buf[pos++] = packet_id & 0xFF;
    
    // 主题
    int topic_len = encode_string(buf + pos, topic);
    pos += topic_len;
    
    // QoS
    buf[pos++] = qos & 0x03;
    
    // 更新剩余长度
    uint32_t remaining_len = pos - 5;  // 减去固定头
    int header_len = encode_fixed_header(buf, MQTT_MSG_SUBSCRIBE, 0, 1, 0, remaining_len);
    
    if (header_len != 5) {
        memmove(buf + header_len, buf + 5, remaining_len);
    }
    
    int len = header_len + remaining_len;
    
    // 发送报文
    int ret = mqtt_network_send(client->network, buf, len);
    if (ret != len) {
        pthread_mutex_unlock(&client->lock);
        return MQTT_ERROR_SEND;
    }
    
    // 添加到订阅列表
    struct subscription *sub = calloc(1, sizeof(struct subscription));
    if (sub) {
        sub->topic = strdup(topic);
        sub->qos = qos;
        sub->next = client->subscriptions;
        client->subscriptions = sub;
    }
    
    client->last_send_time = time(NULL);
    client->bytes_sent += len;
    
    pthread_mutex_unlock(&client->lock);
    
    if (client->config.log_cb) {
        client->config.log_cb(client, 1, "Subscribed to %s, QoS: %d", topic, qos);
    }
    
    return packet_id;
}

// 主循环处理
int mqtt_client_loop(mqtt_client_t *client, int timeout_ms) {
    if (!client) return MQTT_ERROR_INIT;
    
    pthread_mutex_lock(&client->lock);
    
    if (client->state != MQTT_STATE_CONNECTED) {
        pthread_mutex_unlock(&client->lock);
        return MQTT_ERROR_DISCONNECTED;
    }
    
    // 接收数据
    int ret = mqtt_network_recv(client->network, 
                               client->recv_buf + client->recv_buf_len,
                               client->recv_buf_size - client->recv_buf_len,
                               timeout_ms);
    
    if (ret > 0) {
        client->recv_buf_len += ret;
        client->last_recv_time = time(NULL);
        client->bytes_recv += ret;
    } else if (ret < 0) {
        // 网络错误
        pthread_mutex_unlock(&client->lock);
        mqtt_client_disconnect(client);
        return MQTT_ERROR_RECV;
    }
    
    // 处理接收到的数据
    int pos = 0;
    while (pos < client->recv_buf_len) {
        uint8_t msg_type, dup, qos, retain;
        uint32_t remaining_len;
        
        int header_len = decode_fixed_header(client->recv_buf + pos, 
                                           client->recv_buf_len - pos,
                                           &msg_type, &dup, &qos, &retain, 
                                           &remaining_len);
        if (header_len <= 0) {
            break;  // 不完整的报文
        }
        
        int packet_len = header_len + remaining_len;
        if (pos + packet_len > client->recv_buf_len) {
            break;  // 数据不完整
        }
        
        // 处理报文
        switch (msg_type) {
            case MQTT_MSG_PUBLISH: {
                mqtt_message_t msg = {0};
                if (mqtt_decode_publish(client->recv_buf + pos, packet_len, &msg) > 0) {
                    if (client->config.message_cb) {
                        client->config.message_cb(client, &msg);
                    }
                    
                    // QoS 1 回复 PUBACK
                    if (msg.qos == 1) {
                        uint8_t puback[4] = {
                            MQTT_MSG_PUBACK << 4, 2,
                            (msg.packet_id >> 8) & 0xFF,
                            msg.packet_id & 0xFF
                        };
                        mqtt_network_send(client->network, puback, 4);
                    }
                    
                    // 清理
                    if (msg.topic) free(msg.topic);
                    if (msg.payload) free(msg.payload);
                }
                break;
            }
            
            case MQTT_MSG_PUBACK: {
                // 从发送队列中移除
                uint16_t packet_id = (client->recv_buf[pos + 2] << 8) | 
                                     client->recv_buf[pos + 3];
                
                struct msg_queue *prev = NULL;
                struct msg_queue *curr = client->send_queue;
                
                while (curr) {
                    if (curr->packet_id == packet_id) {
                        if (prev) {
                            prev->next = curr->next;
                        } else {
                            client->send_queue = curr->next;
                        }
                        
                        free(curr->data);
                        free(curr);
                        break;
                    }
                    prev = curr;
                    curr = curr->next;
                }
                break;
            }
            
            case MQTT_MSG_PINGRESP:
                client->last_ping_time = time(NULL);
                break;
        }
        
        pos += packet_len;
    }
    
    // 移动剩余数据
    if (pos > 0) {
        int remaining = client->recv_buf_len - pos;
        if (remaining > 0) {
            memmove(client->recv_buf, client->recv_buf + pos, remaining);
        }
        client->recv_buf_len = remaining;
    }
    
    // 检查心跳
    time_t now = time(NULL);
    if (now - client->last_send_time >= client->config.keep_alive) {
        mqtt_client_ping(client);
    }
    
    // 检查超时
    if (now - client->last_recv_time >= client->config.keep_alive * 3 / 2) {
        pthread_mutex_unlock(&client->lock);
        mqtt_client_disconnect(client);
        return MQTT_ERROR_TIMEOUT;
    }
    
    pthread_mutex_unlock(&client->lock);
    
    return MQTT_SUCCESS;
}

3.3 网络层

c 复制代码
// mqtt_network.c
#include "mqtt_network.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#ifdef WITH_SSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif

// 网络结构
struct mqtt_network_s {
    int sockfd;
    int timeout;
    
#ifdef WITH_SSL
    SSL_CTX *ssl_ctx;
    SSL *ssl;
#endif
    
    const char *ca_cert;
    const char *client_cert;
    const char *client_key;
};

// 创建网络实例
mqtt_network_t* mqtt_network_new(void) {
    mqtt_network_t *net = calloc(1, sizeof(mqtt_network_t));
    if (!net) return NULL;
    
    net->sockfd = -1;
    net->timeout = 5000;  // 默认5秒
    
    return net;
}

// 初始化网络
int mqtt_network_init(mqtt_network_t *net, mqtt_network_config_t *config) {
    if (!net || !config) return -1;
    
    net->timeout = config->timeout;
    net->ca_cert = config->ca_cert;
    net->client_cert = config->client_cert;
    net->client_key = config->client_key;
    
#ifdef WITH_SSL
    if (net->ca_cert || net->client_cert) {
        SSL_library_init();
        OpenSSL_add_all_algorithms();
        SSL_load_error_strings();
        
        net->ssl_ctx = SSL_CTX_new(TLS_client_method());
        if (!net->ssl_ctx) return -1;
        
        if (net->ca_cert) {
            if (SSL_CTX_load_verify_locations(net->ssl_ctx, net->ca_cert, NULL) != 1) {
                SSL_CTX_free(net->ssl_ctx);
                return -1;
            }
        }
        
        if (net->client_cert && net->client_key) {
            if (SSL_CTX_use_certificate_file(net->ssl_ctx, net->client_cert, SSL_FILETYPE_PEM) != 1 ||
                SSL_CTX_use_PrivateKey_file(net->ssl_ctx, net->client_key, SSL_FILETYPE_PEM) != 1) {
                SSL_CTX_free(net->ssl_ctx);
                return -1;
            }
        }
    }
#endif
    
    return 0;
}

// 连接服务器
int mqtt_network_connect(mqtt_network_t *net, const char *host, int port) {
    if (!net || !host) return -1;
    
    // 创建socket
    net->sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (net->sockfd < 0) return -1;
    
    // 设置非阻塞
    int flags = fcntl(net->sockfd, F_GETFL, 0);
    fcntl(net->sockfd, F_SETFL, flags | O_NONBLOCK);
    
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    inet_pton(AF_INET, host, &addr.sin_addr);
    
    // 发起连接
    int ret = connect(net->sockfd, (struct sockaddr *)&addr, sizeof(addr));
    if (ret < 0 && errno != EINPROGRESS) {
        close(net->sockfd);
        net->sockfd = -1;
        return -1;
    }
    
    // 等待连接完成
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(net->sockfd, &fds);
    
    struct timeval tv;
    tv.tv_sec = net->timeout / 1000;
    tv.tv_usec = (net->timeout % 1000) * 1000;
    
    ret = select(net->sockfd + 1, NULL, &fds, NULL, &tv);
    if (ret <= 0) {
        close(net->sockfd);
        net->sockfd = -1;
        return -1;
    }
    
    // 检查连接结果
    int error = 0;
    socklen_t len = sizeof(error);
    getsockopt(net->sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
    if (error != 0) {
        close(net->sockfd);
        net->sockfd = -1;
        return -1;
    }
    
    // 恢复阻塞模式
    fcntl(net->sockfd, F_SETFL, flags);
    
#ifdef WITH_SSL
    if (net->ssl_ctx) {
        net->ssl = SSL_new(net->ssl_ctx);
        if (!net->ssl) {
            close(net->sockfd);
            net->sockfd = -1;
            return -1;
        }
        
        SSL_set_fd(net->ssl, net->sockfd);
        if (SSL_connect(net->ssl) != 1) {
            SSL_free(net->ssl);
            close(net->sockfd);
            net->sockfd = -1;
            return -1;
        }
    }
#endif
    
    return 0;
}

// 发送数据
int mqtt_network_send(mqtt_network_t *net, const uint8_t *data, int len) {
    if (!net || net->sockfd < 0) return -1;
    
    int total_sent = 0;
    
    while (total_sent < len) {
        int sent = 0;
        
#ifdef WITH_SSL
        if (net->ssl) {
            sent = SSL_write(net->ssl, data + total_sent, len - total_sent);
        } else {
            sent = send(net->sockfd, data + total_sent, len - total_sent, 0);
        }
#else
        sent = send(net->sockfd, data + total_sent, len - total_sent, 0);
#endif
        
        if (sent <= 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                continue;
            }
            return -1;
        }
        
        total_sent += sent;
    }
    
    return total_sent;
}

// 接收数据
int mqtt_network_recv(mqtt_network_t *net, uint8_t *buf, int len, int timeout) {
    if (!net || net->sockfd < 0) return -1;
    
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(net->sockfd, &fds);
    
    struct timeval tv;
    tv.tv_sec = timeout / 1000;
    tv.tv_usec = (timeout % 1000) * 1000;
    
    int ret = select(net->sockfd + 1, &fds, NULL, NULL, &tv);
    if (ret <= 0) return 0;
    
    int received = 0;
    
#ifdef WITH_SSL
    if (net->ssl) {
        received = SSL_read(net->ssl, buf, len);
    } else {
        received = recv(net->sockfd, buf, len, 0);
    }
#else
    received = recv(net->sockfd, buf, len, 0);
#endif
    
    if (received < 0) {
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            return 0;
        }
        return -1;
    }
    
    return received;
}

四、高级功能

4.1 WebSocket 支持

c 复制代码
// mqtt_websocket.c
#include "mqtt_websocket.h"
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <string.h>
#include <stdlib.h>

// Base64编码
static char* base64_encode(const unsigned char* input, int length) {
    BIO *bmem, *b64;
    BUF_MEM *bptr;
    
    b64 = BIO_new(BIO_f_base64());
    bmem = BIO_new(BIO_s_mem());
    b64 = BIO_push(b64, bmem);
    
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
    BIO_write(b64, input, length);
    BIO_flush(b64);
    BIO_get_mem_ptr(b64, &bptr);
    
    char *buff = malloc(bptr->length + 1);
    memcpy(buff, bptr->data, bptr->length);
    buff[bptr->length] = 0;
    
    BIO_free_all(b64);
    
    return buff;
}

// 生成WebSocket密钥
static char* generate_websocket_key(void) {
    char key[16];
    for (int i = 0; i < 16; i++) {
        key[i] = rand() % 256;
    }
    return base64_encode((unsigned char*)key, 16);
}

// WebSocket握手
int mqtt_websocket_handshake(int sockfd, const char *host, int port, 
                             const char *path, const char *protocol) {
    // 生成密钥
    char *key = generate_websocket_key();
    
    // 构造握手请求
    char request[1024];
    snprintf(request, sizeof(request),
             "GET %s HTTP/1.1\r\n"
             "Host: %s:%d\r\n"
             "Upgrade: websocket\r\n"
             "Connection: Upgrade\r\n"
             "Sec-WebSocket-Key: %s\r\n"
             "Sec-WebSocket-Version: 13\r\n"
             "Sec-WebSocket-Protocol: %s\r\n"
             "\r\n",
             path, host, port, key, protocol);
    
    // 发送请求
    send(sockfd, request, strlen(request), 0);
    
    // 接收响应
    char response[2048];
    int len = recv(sockfd, response, sizeof(response) - 1, 0);
    if (len <= 0) {
        free(key);
        return -1;
    }
    response[len] = '\0';
    
    // 验证响应
    if (strstr(response, "101 Switching Protocols") == NULL) {
        free(key);
        return -1;
    }
    
    free(key);
    return 0;
}

4.2 会话持久化

c 复制代码
// mqtt_session.c
#include "mqtt_session.h"
#include <sqlite3.h>
#include <time.h>

typedef struct {
    sqlite3 *db;
    char *client_id;
} mqtt_session_t;

// 创建会话
mqtt_session_t* mqtt_session_create(const char *client_id, const char *db_path) {
    mqtt_session_t *session = malloc(sizeof(mqtt_session_t));
    if (!session) return NULL;
    
    session->client_id = strdup(client_id);
    
    // 打开数据库
    int rc = sqlite3_open(db_path, &session->db);
    if (rc != SQLITE_OK) {
        free(session->client_id);
        free(session);
        return NULL;
    }
    
    // 创建表
    const char *sql = 
        "CREATE TABLE IF NOT EXISTS messages ("
        "id INTEGER PRIMARY KEY,"
        "client_id TEXT,"
        "topic TEXT,"
        "payload BLOB,"
        "qos INTEGER,"
        "retain INTEGER,"
        "timestamp INTEGER,"
        "packet_id INTEGER"
        ");";
    
    sqlite3_exec(session->db, sql, NULL, NULL, NULL);
    
    return session;
}

// 保存消息
int mqtt_session_save(mqtt_session_t *session, 
                      const char *topic,
                      const void *payload,
                      int payload_len,
                      int qos,
                      int retain,
                      uint16_t packet_id) {
    const char *sql = 
        "INSERT INTO messages (client_id, topic, payload, qos, retain, timestamp, packet_id) "
        "VALUES (?, ?, ?, ?, ?, ?, ?);";
    
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(session->db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) return -1;
    
    sqlite3_bind_text(stmt, 1, session->client_id, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, topic, -1, SQLITE_STATIC);
    sqlite3_bind_blob(stmt, 3, payload, payload_len, SQLITE_STATIC);
    sqlite3_bind_int(stmt, 4, qos);
    sqlite3_bind_int(stmt, 5, retain);
    sqlite3_bind_int64(stmt, 6, time(NULL));
    sqlite3_bind_int(stmt, 7, packet_id);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return rc == SQLITE_DONE ? 0 : -1;
}

五、使用示例

5.1 基本使用

c 复制代码
// example_simple.c
#include "mqtt_client.h"
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

static volatile int running = 1;

void message_callback(void *client, mqtt_message_t *msg) {
    printf("Received message on topic: %s\n", msg->topic);
    printf("Payload: %.*s\n", msg->payload_len, msg->payload);
    printf("QoS: %d\n", msg->qos);
}

void connect_callback(void *client, int result) {
    if (result == CONNACK_ACCEPTED) {
        printf("Connected to server\n");
    } else {
        printf("Connection failed: %d\n", result);
    }
}

void signal_handler(int sig) {
    running = 0;
}

int main() {
    signal(SIGINT, signal_handler);
    
    // 创建客户端
    mqtt_client_t *client = mqtt_client_new();
    if (!client) {
        printf("Failed to create client\n");
        return 1;
    }
    
    // 配置
    mqtt_config_t config = {
        .host = "test.mosquitto.org",
        .port = 1883,
        .client_id = "example_client",
        .keep_alive = 60,
        .clean_session = 1,
        .message_cb = message_callback,
        .connect_cb = connect_callback,
        .timeout = 5000
    };
    
    // 初始化
    if (mqtt_client_init(client, &config) != 0) {
        printf("Failed to initialize client\n");
        mqtt_client_destroy(client);
        return 1;
    }
    
    // 连接
    if (mqtt_client_connect(client) != 0) {
        printf("Failed to connect\n");
        mqtt_client_destroy(client);
        return 1;
    }
    
    // 订阅主题
    mqtt_client_subscribe(client, "test/topic", 1);
    
    int counter = 0;
    
    while (running) {
        // 处理网络消息
        mqtt_client_loop(client, 100);
        
        // 每5秒发布消息
        static time_t last_publish = 0;
        time_t now = time(NULL);
        
        if (now - last_publish >= 5) {
            char message[128];
            sprintf(message, "Hello MQTT! Message #%d", ++counter);
            
            mqtt_client_publish(client, "test/topic", 
                               message, strlen(message), 1, 0);
            
            printf("Published: %s\n", message);
            last_publish = now;
        }
        
        usleep(10000);
    }
    
    // 断开连接
    mqtt_client_disconnect(client);
    mqtt_client_destroy(client);
    
    printf("Disconnected\n");
    return 0;
}

5.2 遗嘱消息

c 复制代码
// example_will.c
#include "mqtt_client.h"
#include <string.h>

int main() {
    mqtt_client_t *client = mqtt_client_new();
    
    // 配置遗嘱消息
    mqtt_will_t will = {
        .topic = "clients/status",
        .message = (uint8_t *)"offline",
        .msg_len = 7,
        .qos = 1,
        .retain = 1
    };
    
    mqtt_config_t config = {
        .host = "broker.hivemq.com",
        .port = 1883,
        .client_id = "will_client",
        .keep_alive = 30,
        .clean_session = 1,
        .will = &will
    };
    
    mqtt_client_init(client, &config);
    mqtt_client_connect(client);
    
    // 主循环
    while (1) {
        mqtt_client_loop(client, 1000);
    }
    
    return 0;
}

参考代码 基于c/c++语言实现mqtt物联网通信协议 www.youwenfan.com/contentcsv/103218.html

六、编译和测试

6.1 CMakeLists.txt

cmake 复制代码
cmake_minimum_required(VERSION 3.10)
project(mqtt_client C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

option(WITH_SSL "Enable SSL support" ON)
option(WITH_WEBSOCKET "Enable WebSocket support" OFF)

# 源文件
set(SOURCES
    mqtt_client.c
    mqtt_packet.c
    mqtt_network.c
)

if(WITH_SSL)
    find_package(OpenSSL REQUIRED)
    add_definitions(-DWITH_SSL)
    list(APPEND SOURCES mqtt_ssl.c)
endif()

if(WITH_WEBSOCKET)
    list(APPEND SOURCES mqtt_websocket.c)
endif()

# 主库
add_library(mqtt_client ${SOURCES})

if(WITH_SSL)
    target_link_libraries(mqtt_client OpenSSL::SSL OpenSSL::Crypto)
endif()

target_link_libraries(mqtt_client pthread)

# 示例
add_executable(example_simple example_simple.c)
target_link_libraries(example_simple mqtt_client)

add_executable(example_will example_will.c)
target_link_libraries(example_will mqtt_client)

6.2 Makefile

makefile 复制代码
CC = gcc
CFLAGS = -Wall -O2 -pthread
LDFLAGS = -lpthread
TARGET = libmqtt.a
EXAMPLES = example_simple example_will

ifeq ($(WITH_SSL),1)
	CFLAGS += -DWITH_SSL
	LDFLAGS += -lssl -lcrypto
endif

SOURCES = mqtt_client.c mqtt_packet.c mqtt_network.c
OBJECTS = $(SOURCES:.c=.o)

all: $(TARGET) $(EXAMPLES)

$(TARGET): $(OBJECTS)
	ar rcs $@ $^

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

example_simple: example_simple.o $(TARGET)
	$(CC) -o $@ $^ $(LDFLAGS)

example_will: example_will.o $(TARGET)
	$(CC) -o $@ $^ $(LDFLAGS)

clean:
	rm -f *.o $(TARGET) $(EXAMPLES)

6.3 单元测试

c 复制代码
// test_mqtt.c
#include "mqtt_client.h"
#include <assert.h>
#include <string.h>

void test_packet_encoding() {
    uint8_t buffer[1024];
    
    // 测试CONNECT编码
    const char *client_id = "test_client";
    int len = mqtt_encode_connect(buffer, client_id, NULL, NULL, 60, 1, NULL);
    assert(len > 0);
    
    // 验证固定头
    assert((buffer[0] >> 4) == MQTT_MSG_CONNECT);
    
    printf("Packet encoding test passed\n");
}

void test_client_create() {
    mqtt_client_t *client = mqtt_client_new();
    assert(client != NULL);
    
    mqtt_config_t config = {
        .host = "localhost",
        .port = 1883,
        .client_id = "test"
    };
    
    int ret = mqtt_client_init(client, &config);
    assert(ret == 0);
    
    mqtt_client_destroy(client);
    printf("Client lifecycle test passed\n");
}

int main() {
    test_packet_encoding();
    test_client_create();
    
    printf("All tests passed!\n");
    return 0;
}

七、性能优化

7.1 内存池

c 复制代码
// mqtt_mempool.c
#include <stdlib.h>
#include <string.h>

#define MEMPOOL_SIZE 4096
#define BLOCK_SIZE 256

typedef struct memblock {
    void *ptr;
    size_t size;
    uint8_t used;
    struct memblock *next;
} memblock_t;

typedef struct {
    memblock_t *blocks;
    void *memory;
    size_t total_size;
    size_t used_size;
} mempool_t;

mempool_t* mempool_create(size_t size) {
    mempool_t *pool = malloc(sizeof(mempool_t));
    if (!pool) return NULL;
    
    pool->memory = malloc(size);
    if (!pool->memory) {
        free(pool);
        return NULL;
    }
    
    pool->blocks = NULL;
    pool->total_size = size;
    pool->used_size = 0;
    
    return pool;
}

void* mempool_alloc(mempool_t *pool, size_t size) {
    if (pool->used_size + size > pool->total_size) {
        return NULL;
    }
    
    memblock_t *block = malloc(sizeof(memblock_t));
    if (!block) return NULL;
    
    block->ptr = (uint8_t *)pool->memory + pool->used_size;
    block->size = size;
    block->used = 1;
    block->next = pool->blocks;
    pool->blocks = block;
    
    pool->used_size += size;
    return block->ptr;
}

7.2 零拷贝发送

c 复制代码
// 使用writev实现零拷贝
#include <sys/uio.h>

int mqtt_send_zero_copy(int sockfd, struct iovec *iov, int iovcnt) {
    int total_sent = 0;
    
    for (int i = 0; i < iovcnt; i++) {
        int sent = write(sockfd, iov[i].iov_base, iov[i].iov_len);
        if (sent != iov[i].iov_len) {
            return -1;
        }
        total_sent += sent;
    }
    
    return total_sent;
}
相关推荐
牛油果子哥q2 小时前
【C++ const 】全场景深度精讲:修饰规则、底层常量折叠、指针 / 引用 / 成员函数实战、易错坑点与工程代码实现
开发语言·c++
郝学胜_神的一滴2 小时前
Qt 高级开发 025:打造优雅界面的艺术与高效重构之道
c++·qt
牛油果子哥q2 小时前
【C++指针与引用】C++指针与引用底层彻底精讲:本质区别、易错深坑、底层内存模型、工程选型、笔试面试满分解析
c++·面试
十五年专注C++开发2 小时前
CMake实践:VS2019控制台程序隐藏控制台方法
c++·windows·cmake·控制台隐藏
小欣加油2 小时前
leetcode3635 最早完成陆地和水上游乐设施的时间II
数据结构·c++·算法·leetcode
三品吉他手会点灯2 小时前
C语言学习笔记 - 46.运算符和表达式 - 运算符4 - 对初学运算符的一些建议
c语言·开发语言·笔记·学习
QT-Neal2 小时前
链接和库整理
c++
剑锋所指,所向披靡!3 小时前
C++多线程实现
开发语言·c++·chrome
十五年专注C++开发3 小时前
Qt之QScopedPointer、QScopeGuard、QScopedValueRollback使用及源码解读
开发语言·c++·qt·qscopedpointer·qscopeguard