一、架构设计
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;
}