这次分享记录一下对mqtt框架的理解,这节课有点绕,看了两遍才勉强理清思路
一、大体思路介绍
1、网络通信通俗介绍
① mqtt是基于tcp上建立的长连接,所以需要分服务器端和客户端,客户端和服务器相连的时候需要有ip地址和端口号
② 服务器端是一个存储鉴别、转发的功能,客户端发送和接收报文,服务器根据客户端订阅的主题鉴别消息并投递
2、框架介绍
二、详细源码介绍
首先:初始化客户端
mqtt_client_t *client = NULL;
int main(void)
{
mqtt_message_t msg;
memset(&msg, 0, sizeof(msg));
1、实例化客户端
client = mqtt_lease();
//初始化客户端
mqtt_set_port(client, "1883");
mqtt_set_host(client, "47.114.187.247");
mqtt_set_client_id(client, "clint_id");
mqtt_set_user_name(client, "clint_name");
mqtt_set_password(client, "clint_password");
mqtt_set_clean_session(client, 1);
2、连接服务器
mqtt_connect(client);
//初始化消息
msg.payload = (void *)"first message";
msg.qos = QOS0;
msg.payloadlen = strlen(buf);
3、发布消息
mqtt_publish(client, "home", &msg);
4、订阅消息
mqtt_subscribe(client, "home", QOS0, smarthome_msg_handler);
while (1)
{
msg.payload = (void *)"test message";
msg.qos = QOS0;
msg.payloadlen = strlen(buf);
err = mqtt_publish(client, "home", &msg);
osDelay(10000);
}
}
下面依次阐述上面标黄的四个关键点
1、客户端实例化
mqtt_client_t *mqtt_lease(void)
{
int rc;
mqtt_client_t* c;
分配空间
c = (mqtt_client_t *)platform_memory_alloc(sizeof(mqtt_client_t));
if (NULL == c)
return NULL;
memset(c, 0, sizeof(mqtt_client_t));
初始化成员信息
rc = mqtt_init(c);
if (MQTT_SUCCESS_ERROR != rc)
return NULL;
return c;
}
static int mqtt_init(mqtt_client_t* c)
{
c->mqtt_network = (network_t*) platform_memory_alloc(sizeof(network_t));
if (NULL == c->mqtt_network)
{
...
}
memset(c->mqtt_network, 0, sizeof(network_t));
c->mqtt_packet_id = 1;
c->mqtt_clean_session = 0; //no clear session by default
c->mqtt_will_flag = 0;
c->mqtt_cmd_timeout = MQTT_DEFAULT_CMD_TIMEOUT;
c->mqtt_client_state = CLIENT_STATE_INITIALIZED;
c->mqtt_ping_outstanding = 0;
c->mqtt_ack_handler_number = 0;
c->mqtt_client_id_len = 0;
c->mqtt_user_name_len = 0;
c->mqtt_password_len = 0;
c->mqtt_keep_alive_interval = MQTT_KEEP_ALIVE_INTERVAL;
c->mqtt_version = MQTT_VERSION;
c->mqtt_reconnect_try_duration = MQTT_RECONNECT_DEFAULT_DURATION;
c->mqtt_will_options = NULL;
c->mqtt_reconnect_data = NULL;
c->mqtt_reconnect_handler = NULL;
c->mqtt_interceptor_handler = NULL;
mqtt_list_init(&c->mqtt_msg_handler_list);
mqtt_list_init(&c->mqtt_ack_handler_list);
platform_mutex_init(&c->mqtt_write_lock);
platform_mutex_init(&c->mqtt_global_lock);
platform_timer_init(&c->mqtt_reconnect_timer);
platform_timer_init(&c->mqtt_last_sent);
platform_timer_init(&c->mqtt_last_received);
RETURN_ERROR(MQTT_SUCCESS_ERROR);
}
2、连接服务器
int mqtt_connect(mqtt_client_t* c)
{
return mqtt_connect_with_results(c);
}
static int mqtt_connect_with_results(mqtt_client_t* c)
{
int len = 0;
int rc = MQTT_CONNECT_FAILED_ERROR;
platform_timer_t connect_timer;
mqtt_connack_data_t connack_data = {0};
MQTTPacket_connectData connect_data = MQTTPacket_connectData_initializer;
//客户端实例成员参数检查
if (c->...OK?)
....
//网络初始化
rc = network_init(c->mqtt_network, c->mqtt_host, c->mqtt_port, NULL);
(1)网络连接
rc = network_connect(c->mqtt_network);
//connect_data 初始化
connect_data.keepAliveInterval = c->mqtt_keep_alive_interval;
connect_data.cleansession = c->mqtt_clean_session;
connect_data.MQTTVersion = c->mqtt_version;
connect_data.clientID.cstring = c->mqtt_client_id;
connect_data.username.cstring = c->mqtt_user_name;
connect_data.password.cstring = c->mqtt_password;
//计数器减减
platform_timer_cutdown(&c->mqtt_last_received, (c->mqtt_keep_alive_interval * 1000));
platform_mutex_lock(&c->mqtt_write_lock);
/* serialize connect packet */
if ((len = MQTTSerialize_connect(
c->mqtt_write_buf,
c->mqtt_write_buf_size,
&connect_data)) <= 0)
goto exit;
//...
(2)发送
if ((rc = mqtt_send_packet(c, len, &connect_timer)) != MQTT_SUCCESS_ERROR)
goto exit;
(3)接收
if (mqtt_wait_packet(c, CONNACK, &connect_timer) == CONNACK)
{
if (MQTTDeserialize_connack(
&connack_data.session_present,
&connack_data.rc,
c->mqtt_read_buf,
c->mqtt_read_buf_size) == 1)
{
rc = connack_data.rc;
}
else {
rc = MQTT_CONNECT_FAILED_ERROR;
}
} else {
rc = MQTT_CONNECT_FAILED_ERROR;
}
exit:
if (rc == MQTT_SUCCESS_ERROR)
{
if(NULL == c->mqtt_thread)
{
(4)如果连接成功,则创建一个网络任务
c->mqtt_thread= platform_thread_init(
"mqtt_yield_thread",
mqtt_yield_thread, //线程函数
c,
MQTT_THREAD_STACK_SIZE,
MQTT_THREAD_PRIO,
MQTT_THREAD_TICK);
}
else
{
mqtt_set_client_state(c, CLIENT_STATE_CONNECTED);
}
c->mqtt_ping_outstanding = 0;
}
else
{
mqtt_set_client_state(c, CLIENT_STATE_INITIALIZED);
}
platform_mutex_unlock(&c->mqtt_write_lock);
RETURN_ERROR(rc);
}
(1)network_connect(c->mqtt_network);
int network_connect(network_t *n)
{
return nettype_tcp_connect(n);
}
int nettype_tcp_connect(network_t* n)
{
n->socket = platform_net_socket_connect(n->host, n->port, PLATFORM_NET_PROTO_TCP);
//...
}
int platform_net_socket_connect(const char *host, const char *port, int proto)
{
ESP32 TCP连接服务器
}
(2)mqtt_send_packet(c, len, &connect_timer)
static int mqtt_send_packet(mqtt_client_t* c, int length, platform_timer_t* timer)
{
//...
while ((sent < length) && (!platform_timer_is_expired(timer))) {
len = network_write(c->mqtt_network, &c->mqtt_write_buf[sent], length, platform_timer_remain(timer));
if (len <= 0) // there was an error writing the data
break;
sent += len;
}
//...
RETURN_ERROR(MQTT_SEND_PACKET_ERROR);
}
int network_write(network_t *n, unsigned char *buf, int len, int timeout)
{
return nettype_tcp_write(n, buf, len, timeout);
}
int nettype_tcp_write(network_t *n, unsigned char *write_buf, int len, int timeout)
{
return platform_net_socket_write_timeout(n->socket, write_buf, len, timeout);
}
int platform_net_socket_write_timeout(int fd, unsigned char *buf, int len, int timeout)
{
//ESP32 TCP发送
}
(3)mqtt_wait_packet(c, CONNACK, &connect_timer)
static int mqtt_wait_packet(mqtt_client_t* c, int packet_type, platform_timer_t* timer)
{
//...
rc = mqtt_packet_handle(c, timer);
//...
}
static int mqtt_packet_handle(mqtt_client_t* c, platform_timer_t* timer)
{
rc = mqtt_read_packet(c, &packet_type, timer);
switch (packet_type) {
case CONNACK:
goto exit;
//...
}
exit:
if (rc == MQTT_SUCCESS_ERROR)
rc = packet_type;
RETURN_ERROR(rc);
}
static int mqtt_read_packet(mqtt_client_t* c, int* packet_type, platform_timer_t* timer)
{
/* 1. read the header byte. This has the packet type in it */
rc = network_read(c->mqtt_network, c->mqtt_read_buf, len, platform_timer_remain(timer));
/* 2. read the remaining length. This is variable in itself */
mqtt_decode_packet(c, &remain_len, platform_timer_remain(timer));
/* 3. read the rest of the buffer using a callback to supply the rest of the data */
if ((remain_len > 0) && ((rc = network_read(c->mqtt_network, c->mqtt_read_buf + len, remain_len, platform_timer_remain(
}
int network_read(network_t *n, unsigned char *buf, int len, int timeout)
{
return nettype_tcp_read(n, buf, len, timeout);
}
int nettype_tcp_read(network_t *n, unsigned char *read_buf, int len, int timeout)
{
return platform_net_socket_recv_timeout(n->socket, read_buf, len, timeout);
}
int platform_net_socket_recv_timeout(int fd, unsigned char *buf, int len, int timeout)
{
ESP32 TCP接收
}
(4)创建线程任务:任务名称 mqtt_yield_thread
static void mqtt_yield_thread(void *arg)
{
int rc;
client_state_t state;
mqtt_client_t *c = (mqtt_client_t *)arg;
state = mqtt_get_client_state(c);
if (CLIENT_STATE_CONNECTED != state)
{
platform_thread_stop(c->mqtt_thread);
}
while (1)
{
rc = mqtt_yield(c, c->mqtt_cmd_timeout);
if (? == rc)
{
...
}
}
exit:
platform_thread_destroy(c->mqtt_thread);
}
static int mqtt_yield(mqtt_client_t* c, int timeout_ms)
{
...
while (!platform_timer_is_expired(&timer))
{
state = mqtt_get_client_state(c);
if (CLIENT_STATE_CLEAN_SESSION == state) / else if (CLIENT_STATE_CONNECTED != state)
{
...
}
/* mqtt connected, handle mqtt packet */
rc = mqtt_packet_handle(c, &timer);
if (rc >= 0) /else if (MQTT_NOT_CONNECT_ERROR == rc) /else
{
...
}
}
RETURN_ERROR(rc);
}
static int mqtt_packet_handle(mqtt_client_t* c, platform_timer_t* timer)
{
int rc = MQTT_SUCCESS_ERROR;
int packet_type = 0;
rc = mqtt_read_packet(c, &packet_type, timer);
switch (packet_type) {
case 0: break;
case CONNACK: goto exit;
case PUBACK:
case PUBCOMP:
rc = mqtt_puback_and_pubcomp_packet_handle(c, timer);
break;
case SUBACK:
rc = mqtt_suback_packet_handle(c, timer);
break;
case UNSUBACK:
rc = mqtt_unsuback_packet_handle(c, timer);
break;
case PUBLISH:
rc = mqtt_publish_packet_handle(c, timer);
break;
case PUBREC:
case PUBREL:
rc = mqtt_pubrec_and_pubrel_packet_handle(c, timer);
break;
case PINGRESP:
c->mqtt_ping_outstanding = 0; /* keep alive ping success */
break;
default:
goto exit;
}
rc = mqtt_keep_alive(c);
exit:
if (rc == MQTT_SUCCESS_ERROR)
rc = packet_type;
RETURN_ERROR(rc);
}
3、发布消息
int mqtt_publish(mqtt_client_t* c, const char* topic_filter, mqtt_message_t* msg)
{
int len = 0;
int rc = MQTT_FAILED_ERROR;
platform_timer_t timer;
MQTTString topic = MQTTString_initializer;
topic.cstring = (char *)topic_filter;
...
if ((NULL != msg->payload) && (0 == msg->payloadlen))
msg->payloadlen = strlen((char*)msg->payload);
platform_mutex_lock(&c->mqtt_write_lock);
if (QOS0 != msg->qos)
{
if (mqtt_ack_handler_is_maximum(c)) {
rc = MQTT_ACK_HANDLER_NUM_TOO_MUCH_ERROR;
goto exit;
}
msg->id = mqtt_get_next_packet_id(c);
}
//发布消息---数据序列化
len = MQTTSerialize_publish(c->mqtt_write_buf, c->mqtt_write_buf_size, 0, msg->qos, msg->retained, msg->id,topic, (uint8_t*)msg->payload, msg->payloadlen);
...
if ((rc = mqtt_send_packet(c, len, &timer)) != MQTT_SUCCESS_ERROR)
goto exit;
if (QOS0 != msg->qos) {
...
}
exit:
msg->payloadlen = 0;
platform_mutex_unlock(&c->mqtt_write_lock);
if ((MQTT_ACK_HANDLER_NUM_TOO_MUCH_ERROR == rc) || (MQTT_MEM_NOT_ENOUGH_ERROR == rc)) {
...
mqtt_set_client_state(c, CLIENT_STATE_DISCONNECTED);
}
RETURN_ERROR(rc);
}
mqtt_send_packet --> ...--> platform_net_socket_write_timeout(){ESP32...}
4、订阅消息
int mqtt_subscribe(mqtt_client_t* c, const char* topic_filter, mqtt_qos_t qos, message_handler_t handler)
{
...
uint16_t packet_id;
platform_timer_t timer;
MQTTString topic = MQTTString_initializer;
topic.cstring = (char *)topic_filter;
...
packet_id = mqtt_get_next_packet_id(c);
订阅消息---序列化数据
len = MQTTSerialize_subscribe(c->mqtt_write_buf, c->mqtt_write_buf_size, 0, packet_id, 1, &topic, (int*)&qos);
...
if ((rc = mqtt_send_packet(c, len, &timer)) != MQTT_SUCCESS_ERROR)
goto exit;
if (NULL == handler)
handler = default_msg_handler;
msg_handler = mqtt_msg_handler_create(topic_filter, qos, handler);
if (NULL == msg_handler) {
platform_mutex_unlock(&c->mqtt_write_lock); /* 100ask */
RETURN_ERROR(MQTT_MEM_NOT_ENOUGH_ERROR);
}
rc = mqtt_ack_list_record(c, SUBACK, packet_id, len, msg_handler);
exit:
platform_mutex_unlock(&c->mqtt_write_lock);
RETURN_ERROR(rc);
}
最后记录一下老师提供的参考资料链接:
-
kawaii-mqtt源码:
-
博客
-
APP