ソケットAPI上記のクロスプラットフォームのクライアントMQTTに基づいて、サポートqos2

mqttclient

ソケットAPIクロスプラットフォームクライアントMQTTに基づいて、

ソースアドレスhttps://github.com/jiejieTop/mqttclient

全体のフレームワーク

全体のフレームワーク

注:現在、唯一の実装Linuxプラットフォーム、TencentOS小さなおよびRT-スレッドを移植します

Linuxプラットフォームを使用してテストします

cmakeのをインストールします。

sudo apt-get install cmake

コンフィギュレーション

ではmqttclient/test/test.c、次のファイルが変更します。

    init_params.connect_params.network_params.network_ssl_params.ca_crt = test_ca_get();    /* CA证书 */
    init_params.connect_params.network_params.addr = "xxxxxxx";                             /* 服务器域名 */
    init_params.connect_params.network_params.port = "8883";
    init_params.connect_params.user_name = "xxxxxxx";                                       /* 用户名 */
    init_params.connect_params.password = "xxxxxxx";                                        /* 密码 */
    init_params.connect_params.client_id = "xxxxxxx";                                       /* 客户端id */

オープンsalof

salof名は:Synchronous Asynchronous Log Output Framework(出力フレーム同期非同期のログ)

これは、アイドル時間中に出力ログ情報、およびバンクに対応し、非同期ログ出力ライブラリであり、その後ではない場合、シームレスmqttclient LOG_IS_SALOF0として定義されます。

#define LOG_IS_SALOF    0

mqttclient/common/log/config.h開いた構成の出力レベルに対応するログファイル:

#define BASE_LEVEL      (0)
#define ASSERT_LEVEL    (BASE_LEVEL + 1)            /* 日志输出级别:断言级别(非常高优先级) */
#define ERR_LEVEL       (ASSERT_LEVEL + 1)          /* 日志输出级别:错误级别(高优先级) */
#define WARN_LEVEL      (ERR_LEVEL + 1)             /* 日志输出级别:警告级别(中优先级) */
#define INFO_LEVEL      (WARN_LEVEL + 1)            /* 日志输出级别:信息级别(低优先级) */
#define DEBUG_LEVEL     (INFO_LEVEL + 1)            /* 日志输出级别:调试级别(更低优先级) */

#define         SALOF_OS                    USE_LINUX       /* 选择对应的平台:Linux/FreeRTOS/TencentOS */
#define         LOG_LEVEL                   WARN_LEVEL      /* 日志输出级别 */

mqttclient設定

設定ファイルは次のとおりです。mqttclient/mqtt_config.hどこが自分のニーズに応じて設定情報を、対応することができます。
かどうかの選択mbedtls暗号化層:

#define     MQTT_NETWORK_TYPE_TLS               MQTT_YES

コンパイル&実行

./build.sh

実行build.shするスクリプトを./build/bin/実行ファイルのディレクトリが生成mqtt-client直接実行することができます。

デザインのアイデア

  • 階層設計の全体的な使用、非同期設計を使用してコード実装モードは、カップリングを減少させます。
  • コールバックメッセージ処理を使用して処理:ユーザーが指定し[订阅的主题]、指定を[消息的处理函数]
  • 外部依存性はありません

API

mqttclientこれは非常に単純でありapiへのインタフェースを

int mqtt_keep_alive(mqtt_client_t* c);
int mqtt_init(mqtt_client_t* c, client_init_params_t* init);
int mqtt_release(mqtt_client_t* c);
int mqtt_connect(mqtt_client_t* c);
int mqtt_disconnect(mqtt_client_t* c);
int mqtt_subscribe(mqtt_client_t* c, const char* topic_filter, mqtt_qos_t qos, message_handler_t msg_handler);
int mqtt_unsubscribe(mqtt_client_t* c, const char* topic_filter);
int mqtt_publish(mqtt_client_t* c, const char* topic_filter, mqtt_message_t* msg);
int mqtt_yield(mqtt_client_t* c, int timeout_ms);

コア

mqtt_client_t構造

typedef struct mqtt_client {
    unsigned short              packet_id;
    unsigned char               *read_buf;
    unsigned char               *write_buf;
    unsigned char               ping_outstanding;
    unsigned char               ack_handler_number;
    unsigned int                cmd_timeout;
    unsigned int                read_buf_size;
    unsigned int                write_buf_size;
    unsigned int                reconnect_try_duration;
    void                        *reconnect_date;
    reconnect_handler_t         reconnect_handler;
    client_state_t              client_state;
    platform_mutex_t            write_lock;
    platform_mutex_t            global_lock;
    list_t                      msg_handler_list;
    list_t                      ack_handler_list;
    network_t                   *network;
    platform_thread_t           *thread;
    platform_timer_t            reconnect_timer;
    platform_timer_t            ping_timer;
    connect_params_t            *connect_params;
} mqtt_client_t;

以下の主な構造のメンテナンス:

  1. 読み取りと書き込みデータバッファread_buf、write_buf
  2. コマンドのタイムアウト期間cmd_timeout(主に時間、応答待ち、再接続待ち時間をブロック書き込み)
  3. 维护ack链表ack_handler_list,这是异步实现的核心,所有等待响应的报文都会被挂载到这个链表上
  4. 维护消息处理列表msg_handler_list,这是mqtt协议必须实现的内容,所有来自服务器的publish报文都会被处理(前提是订阅了对应的消息)
  5. 维护一个网卡接口network
  6. 维护一个内部线程thread,所有来自服务器的mqtt包都会在这里被处理!
  7. 两个定时器,分别是掉线重连定时器与保活定时器reconnect_timer、ping_timer
  8. 一些连接的参数connect_params

初始化

int mqtt_init(mqtt_client_t* c, client_init_params_t* init)

主要是配置mqtt_client_t结构的相关信息,如果没有指定初始化参数,则系统会提供默认的参数。
但连接部分的参数则必须指定:

    init_params.connect_params.network_params.addr = "[你的mqtt服务器IP地址或者是域名]";
    init_params.connect_params.network_params.port = 1883;	//端口号
    init_params.connect_params.user_name = "jiejietop";
    init_params.connect_params.password = "123456";
    init_params.connect_params.client_id = "clientid";

连接服务器

int mqtt_connect(mqtt_client_t* c);

连接服务器则是使用非异步的方式设计,因为必须等待连接上服务器才能进行下一步操作。
过程如下

  1. 调用底层的连接函数连接上服务器:
c->network->connect(c->network);
  1. 序列化mqttCONNECT报文并且发送
MQTTSerialize_connect(c->write_buf, c->write_buf_size, &connect_data)
mqtt_send_packet(c, len, &connect_timer)
  1. 等待来自服务器的CONNACK报文
mqtt_wait_packet(c, CONNACK, &connect_timer)
  1. 连接成功后创建一个内部线程mqtt_yield_thread
platform_thread_init("mqtt_yield_thread", mqtt_yield_thread, c, MQTT_THREAD_STACK_SIZE, MQTT_THREAD_PRIO, MQTT_THREAD_TICK)

订阅报文

int mqtt_subscribe(mqtt_client_t* c, const char* topic_filter, mqtt_qos_t qos, message_handler_t handler)

订阅报文使用异步设计来实现的:
过程如下:

  1. 序列化订阅报文并且发送给服务器
MQTTSerialize_subscribe(c->write_buf, c->write_buf_size, 0, mqtt_get_next_packet_id(c), 1, &topic, (int*)&qos)
mqtt_send_packet(c, len, &timer)
  1. 创建对应的消息处理节点,这个消息节点在收到服务器的SUBACK订阅应答报文后会挂载到消息处理列表msg_handler_list
mqtt_msg_handler_create(topic_filter, qos, handler)
  1. 在发送了报文给服务器那就要等待服务器的响应了,记录这个等待SUBACK
mqtt_ack_list_record(c, SUBACK, mqtt_get_next_packet_id(c), len, msg_handler)

取消订阅

与订阅报文的逻辑基本差不多的~

发布报文

int mqtt_publish(mqtt_client_t* c, const char* topic_filter, mqtt_message_t* msg)

核心思想都差不多,过程如下:

  1. 先序列化发布报文,然后发送到服务器
MQTTSerialize_publish(c->write_buf, c->write_buf_size, 0, msg->qos, msg->retained, msg->id,
              topic, (unsigned char*)msg->payload, msg->payloadlen);
mqtt_send_packet(c, len, &timer)
  1. 对于QOS0的逻辑,不做任何处理,对于QOS1和QOS2的报文则需要记录下来,在没收到服务器应答的时候进行重发
    if (QOS1 == msg->qos) {
        rc = mqtt_ack_list_record(c, PUBACK, mqtt_get_next_packet_id(c), len, NULL);
    } else if (QOS2 == msg->qos) {
        rc = mqtt_ack_list_record(c, PUBREC, mqtt_get_next_packet_id(c), len, NULL);
    }

内部线程

static void mqtt_yield_thread(void *arg)

主要是对mqtt_yield函数的返回值做处理,比如在disconnect的时候销毁这个线程。

コアハンドラmqtt_yield

  1. パケット処理mqtt_packet_handle
static int mqtt_packet_handle(mqtt_client_t* c, platform_timer_t* timer)

異なるパッケージでの使用のためのさまざまな治療法:

    switch (packet_type) {
        case 0: /* timed out reading packet */
            break;

        case CONNACK:
            break;

        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->ping_outstanding = 0;
            break;

        default:
            goto exit;
    }

そして、プロセスをキープアライブありません。

mqtt_keep_alive(c)
  1. ackサーバは、パケット、ACKリストのスキャン操作を受信したときに、リストをスキャン
mqtt_ack_list_scan(c);

ときに破壊ACKリストノードの後に​​タイムアウト:

mqtt_ack_handler_destroy(ack_handler);

もちろん、これらの種類のメッセージ以下のあなたは、再送信操作にする必要があります:( PUBACK 、PUBREC、 PUBREL 、PUBCOMP保証するサービスQOS1 QOS2の品質)

if ((ack_handler->type ==  PUBACK) || (ack_handler->type ==  PUBREC) || (ack_handler->type ==  PUBREL) || (ack_handler->type ==  PUBCOMP))
	mqtt_ack_handler_resend(c, ack_handler);
  1. 時間を保持している活動は、それが破棄される可能性があり、渡された、と再接続動作に必要ました
mqtt_try_reconnect(c);

再サブスクライブメッセージに成功した試みの後に再接続、元の状態を復元するために確保〜

mqtt_try_resubscribe(c)

发布应答そして、发布完成パケット処理

static int mqtt_puback_and_pubcomp_packet_handle(mqtt_client_t *c, platform_timer_t *timer)
  1. 直列化復元のメッセージ
MQTTDeserialize_ack(&packet_type, &dup, &packet_id, c->read_buf, c->read_buf_size)
  1. 対応するACKレコードをキャンセル
mqtt_ack_list_unrecord(c, packet_type, packet_id, NULL);

订阅应答パケット処理

static int mqtt_suback_packet_handle(mqtt_client_t *c, platform_timer_t *timer)
  1. 直列化復元のメッセージ
MQTTDeserialize_suback(&packet_id, 1, &count, (int*)&granted_qos, c->read_buf, c->read_buf_size)
  1. 対応するACKレコードをキャンセル
mqtt_ack_list_unrecord(c, packet_type, packet_id, NULL);
  1. それが既にインストール中に存在していない場合は、インストール、サブスクリプションメッセージ処理機能に対応
mqtt_msg_handlers_install(c, msg_handler);

取消订阅应答パケット処理

static int mqtt_unsuback_packet_handle(mqtt_client_t *c, platform_timer_t *timer)
  1. 直列化復元のメッセージ
MQTTDeserialize_unsuback(&packet_id, c->read_buf, c->read_buf_size)
  1. 対応するACKレコードをキャンセル
mqtt_ack_list_unrecord(c, UNSUBACK, packet_id, &msg_handler)
  1. 破壊対応するサブスクリプション・メッセージ処理機能
mqtt_msg_handler_destory(msg_handler);

サーバからの发布メッセージ処理

static int mqtt_publish_packet_handle(mqtt_client_t *c, platform_timer_t *timer)
  1. 直列化復元のメッセージ
MQTTDeserialize_publish(&msg.dup, &qos, &msg.retained, &msg.id, &topic_name,
        (unsigned char**)&msg.payload, (int*)&msg.payloadlen, c->read_buf, c->read_buf_size)
  1. メッセージQOS0について、QOS1は直接メッセージを処理します
mqtt_deliver_message(c, &topic_name, &msg);
  1. メッセージQOS1についても送信する必要があるPUBACKサーバに応答メッセージを
MQTTSerialize_ack(c->write_buf, c->write_buf_size, PUBACK, 0, msg.id);
  1. メッセージのためとして、あなたはQOS2に送信する必要がPUBREC必要なレコードに加えて、サーバーにメッセージをPUBREL、メッセージサーバを解放し、最後にメッセージを処理するために行くのを待って、リスト上でACKします
MQTTSerialize_ack(c->write_buf, c->write_buf_size, PUBREC, 0, msg.id);
mqtt_ack_list_record(c, PUBREL, msg.id + 1, len, NULL)
mqtt_deliver_message(c, &topic_name, &msg);

説明:反復パケットが再登録されていないした場合、リストにACKパケットを登録したら、それが通過するmqtt_ack_list_node_is_exist関数は、応答MSGIDのメッセージタイプを待つに主に依存して、ノードが存在するかどうかを判断します。

发布收到そして、发布释放パケット処理

static int mqtt_pubrec_and_pubrel_packet_handle(mqtt_client_t *c, platform_timer_t *timer)
  1. 直列化復元のメッセージ
MQTTDeserialize_ack(&packet_type, &dup, &packet_id, c->read_buf, c->read_buf_size)
  1. これは、対応する応答メッセージを生成します
mqtt_publish_ack_packet(c, packet_id, packet_type);
  1. 対応するACKレコードをキャンセル
mqtt_ack_list_unrecord(c, UNSUBACK, packet_id, &msg_handler)

ソースアドレスhttps://github.com/jiejieTop/mqttclient

公開された115元の記事 ウォンの賞賛283 ビュー190 000 +

おすすめ

転載: blog.csdn.net/jiejiemcu/article/details/103845392