物联笔记-5

这次分享记录一下对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);
}

最后记录一下老师提供的参考资料链接:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值