天天看點

QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出

一、環境介紹

QT版本: 5.12.6

編譯器:  MinGW 32

MQTT協定: 參照3.1.1版本文檔自己編寫 (不是使用QT的qmqtt)

功能介紹:  使用QT編寫MQTT用戶端(根據mqtt官方文檔3.1.1,自己實作過程代碼,沒有使用其他庫),登入OneNet物聯網伺服器,完成主題訂閱、釋出等操作。

項目完整源碼下載下傳位址:

https://download.csdn.net/download/xiaolong1126626497/18725462 軟體運作效果圖: (2021/06/01 21:30 更新,修複已知BUG)  新加入:  心跳包、域名檢測功能
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出

二、在OneNet上建立産品

位址:

https://open.iot.10086.cn/
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
手機APP下載下傳位址: https://download.csdn.net/download/xiaolong1126626497/18697132
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出

三、OneNet的MQTT介紹

onenet伺服器的位址: 

https://open.iot.10086.cn/doc/mqtt/book/device-develop/manual.html
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
裝置接入說明:  https://open.iot.10086.cn/doc/mqtt/book/get-start/connect.html
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
主題訂閱與釋出的格式說明:  https://open.iot.10086.cn/doc/mqtt/book/device-develop/protocol.html
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
訂閱主題的格式:

$sys/{pid}/{device-name}/dp/post/json/accepted  訂閱裝置資料點上報成功的消息
$sys/{pid}/{device-name}/dp/post/json/rejected  訂閱裝置資料點上報失敗的消息
$sys/{pid}/{device-name}/dp/post/json/+ 訂閱裝置資料點上報結果
$sys/{pid}/{device-name}/cmd/request/+  訂閱裝置所有指令消息
$sys/{pid}/{device-name}/cmd/response/+/+   訂閱裝置所有指令應答結果消息
$sys/{pid}/{device-name}/cmd/#  訂閱裝置所有指令相關消息
$sys/{pid}/{device-name}/#  訂閱裝置所有相關消息
 
說明:  參數裡的pid就是産品的ID(注意是産品ID不是裝置ID),device-name 就是産品的名稱。
如果要訂閱裝置所有相關資訊,就可以這樣寫:
$sys/427519/GreeningManagement/#      

主題釋出(資料上傳): 

https://open.iot.10086.cn/doc/mqtt/book/device-develop/topics/dp-topics.html
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出
裝置可以通過向系統固定 topic:$sys/{pid}/{device-name}/dp/post/json 發送資料點存儲消息,消息中payload字段資料内容僅支援json格式.
 
釋出主題的格式: $sys/427519/GreeningManagement/dp/post/json
 
如果同時上傳溫度、濕度、光照度的消息就可以這樣寫:
{"id":666,"dp":{"temperature":[{"v":21}],"humidity":[{"v":40}],"Light":[{"v":100}]}}
 
 
在代碼裡寫上面這串資料時,裡面的"要記得轉義。
就應該這樣寫:
{\"id\":666,\"dp\":{\"temperature\":[{\"v\":21}],\"humidity\":[{\"v\":40}],\"Light\":[{\"v\":100}]}}      

安全鑒權(就是生成MQTT登入的密碼、非常重要): 

https://open.iot.10086.cn/doc/mqtt/book/manual/auth/token.html
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出

下載下傳密碼生成工具: 

https://open.iot.10086.cn/doc/mqtt/book/manual/auth/tool.html
QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出

生成登入面示例:

QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出

參數說明:

res選項參數的格式: products/{産品ID}/devices/{裝置名稱}

et是設定token過期時間:算出1970-1-1到你想要設定的到期時間,機關是秒,填入即可。

key的參數格式: 就是裝置建立之後,在裝置詳情頁的key聲明。

根據上面工具擷取、得到的MQTT協定登入密碼就是下面這個:

version=2018-10-31&res=products%2F427519%2Fdevices%2FGreeningManagement&et=1631378104&method=md5&sign=xz3tM8A31jIrkQ3S1mOcqQ%3D%3D      

計算到期時間的代碼:(Linux)

#include <stdio.h>
#include <time.h>
#include <time.h>
 
int main()
{
    time_t time_sec;
    time_sec=time(NULL);  //目前的秒機關時間--UTC時間
    printf("目前時間(秒):%ld\n",time_sec);
    printf("加120天的時間(秒):%ld\n",time_sec+120*24*60*60);
    return 0;
}
 
wbyq@wbyq:~$ ./a.out 
目前時間(秒):1621010104
加120天的時間(秒):1631378104      

四、QT實作mqtt協定核心代碼

QT應用程式設計: 編寫MQTT用戶端登入OnetNet伺服器完成主題訂閱與釋出

4.1 mqtt.cpp

#include "mqtt.h"
 
//連接配接成功伺服器回應 20 02 00 00
//用戶端主動斷開連接配接 e0 00
const quint8 parket_connetAck[] = {0x20,0x02,0x00,0x00};
const quint8 parket_disconnet[] = {0xe0,0x00};
const quint8 parket_heart[] = {0xc0,0x00};
const quint8 parket_heart_reply[] = {0xc0,0x00};
const quint8 parket_subAck[] = {0x90,0x03};
 
MQTT_WorkClass::~MQTT_WorkClass()
{
    qDebug()<<"析構函數---TCP";
}
 
void MQTT_WorkClass::run()
{
    qDebug()<<"執行:run";
 
    if(timer)
    {
        delete  timer;
        timer=nullptr;
    }
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(EndEvenLoop()));
 
    socket_type=0;
    //連接配接到伺服器
    ConnectMqttServer(m_ip,m_port);
    //開始事件循環
    StartEvenLoop();
 
    //初始化mqtt協定
    MQTT_Init();
 
    //連接配接mqtt協定
    if(MQTT_Connect(m_MQTT_ClientID.toUtf8().data(),m_MQTT_UserName.toUtf8().data(),m_MQTT_PassWord.toUtf8().data()))
    {
        LogSend("MQTT伺服器登入失敗.\n");
    }
    else
    {
        LogSend("MQTT伺服器登入成功.\n");
    }
}
 
 
void MQTT_WorkClass::MQTT_Init(void)
{
    //緩沖區指派
    mqtt_rxbuf = _mqtt_rxbuf;
    mqtt_rxlen = sizeof(_mqtt_rxbuf);
    mqtt_txbuf = _mqtt_txbuf;
    mqtt_txlen = sizeof(_mqtt_txbuf);
    memset(mqtt_rxbuf,0,mqtt_rxlen);
    memset(mqtt_txbuf,0,mqtt_txlen);
}
 
/*
函數功能: 登入伺服器
函數傳回值: 0表示成功 1表示失敗
*/
quint8 MQTT_WorkClass::MQTT_Connect(char *ClientID,char *Username,char *Password)
{
    quint8 i,j;
    int ClientIDLen = strlen(ClientID);
    int UsernameLen = strlen(Username);
    int PasswordLen = strlen(Password);
    int DataLen;
    mqtt_txlen=0;
    //可變報頭+Payload  每個字段包含兩個位元組的長度辨別
    DataLen = 10 + (ClientIDLen+2) + (UsernameLen+2) + (PasswordLen+2);
    
    //固定報頭
    //控制封包類型
    mqtt_txbuf[mqtt_txlen++] = 0x10;        //MQTT Message Type CONNECT
    //剩餘長度(不包括固定頭部)
    do
    {
        quint8 encodedByte = DataLen % 128;
        DataLen = DataLen / 128;
        // if there are more data to encode, set the top bit of this byte
        if ( DataLen > 0 )
            encodedByte = encodedByte | 128;
        mqtt_txbuf[mqtt_txlen++] = encodedByte;
    }while ( DataLen > 0 );
        
    //可變報頭
    //協定名
    mqtt_txbuf[mqtt_txlen++] = 0;           // Protocol Name Length MSB    
    mqtt_txbuf[mqtt_txlen++] = 4;           // Protocol Name Length LSB    
    mqtt_txbuf[mqtt_txlen++] = 'M';         // ASCII Code for M    
    mqtt_txbuf[mqtt_txlen++] = 'Q';         // ASCII Code for Q    
    mqtt_txbuf[mqtt_txlen++] = 'T';         // ASCII Code for T    
    mqtt_txbuf[mqtt_txlen++] = 'T';         // ASCII Code for T    
    //協定級别
    mqtt_txbuf[mqtt_txlen++] = 4;               // MQTT Protocol version = 4   對于 3.1.1 版協定,協定級别字段的值是 4(0x04)   
    //連接配接标志
    mqtt_txbuf[mqtt_txlen++] = 0xc2;            // conn flags 
    mqtt_txbuf[mqtt_txlen++] = 0;               // Keep-alive Time Length MSB    
    mqtt_txbuf[mqtt_txlen++] = 100;         // Keep-alive Time Length LSB  100S心跳包    保活時間
    
    mqtt_txbuf[mqtt_txlen++] = BYTE1(ClientIDLen);// Client ID length MSB    
    mqtt_txbuf[mqtt_txlen++] = BYTE0(ClientIDLen);// Client ID length LSB   
    memcpy(&mqtt_txbuf[mqtt_txlen],ClientID,ClientIDLen);
    mqtt_txlen += ClientIDLen;
    
    if(UsernameLen > 0)
    {   
        mqtt_txbuf[mqtt_txlen++] = BYTE1(UsernameLen);      //username length MSB    
        mqtt_txbuf[mqtt_txlen++] = BYTE0(UsernameLen);      //username length LSB    
        memcpy(&mqtt_txbuf[mqtt_txlen],Username,UsernameLen);
        mqtt_txlen += UsernameLen;
    }
    
    if(PasswordLen > 0)
    {    
        mqtt_txbuf[mqtt_txlen++] = BYTE1(PasswordLen);      //password length MSB    
        mqtt_txbuf[mqtt_txlen++] = BYTE0(PasswordLen);      //password length LSB  
        memcpy(&mqtt_txbuf[mqtt_txlen],Password,PasswordLen);
        mqtt_txlen += PasswordLen; 
    }    
 
    //清空資料
    memset(mqtt_rxbuf,0,mqtt_rxlen);
    ReadData.clear();
 
    MQTT_SendBuf(mqtt_txbuf,mqtt_txlen);
    //開始事件循環
    StartEvenLoop();
 
    if(ReadData.length()==0)
    {
        //開始事件循環
        StartEvenLoop();
    }
    memcpy((char *)mqtt_rxbuf,ReadData.data(),ReadData.length());
 
    //CONNECT
    if(mqtt_rxbuf[0]==parket_connetAck[0] && mqtt_rxbuf[1]==parket_connetAck[1]) //連接配接成功
    {
        return 0;//連接配接成功
    }
    return 1;
}
 
 
/*
函數功能: MQTT訂閱/取消訂閱資料打包函數
函數參數:
    topic       主題   
    qos         消息等級 0:最多分發一次  1: 至少分發一次  2: 僅分發一次
    whether     訂閱/取消訂閱請求包 (1表示訂閱,0表示取消訂閱)
傳回值: 0表示成功 1表示失敗
*/
quint8 MQTT_WorkClass::MQTT_SubscribeTopic(char *topic,quint8 qos,quint8 whether)
{    
    quint8 i,j;
    mqtt_txlen=0;
    int topiclen = strlen(topic);
    
    int DataLen = 2 + (topiclen+2) + (whether?1:0);//可變報頭的長度(2位元組)加上有效載荷的長度
    //固定報頭
    //控制封包類型
    if(whether)mqtt_txbuf[mqtt_txlen++] = 0x82; //消息類型和标志訂閱
    else    mqtt_txbuf[mqtt_txlen++] = 0xA2;    //取消訂閱
 
    //剩餘長度
    do
    {
        quint8 encodedByte = DataLen % 128;
        DataLen = DataLen / 128;
        // if there are more data to encode, set the top bit of this byte
        if ( DataLen > 0 )
            encodedByte = encodedByte | 128;
        mqtt_txbuf[mqtt_txlen++] = encodedByte;
    }while ( DataLen > 0 ); 
    
    //可變報頭
    mqtt_txbuf[mqtt_txlen++] = 0;           //消息辨別符 MSB
    mqtt_txbuf[mqtt_txlen++] = 0x0A;        //消息辨別符 LSB
    //有效載荷
    mqtt_txbuf[mqtt_txlen++] = BYTE1(topiclen);//主題長度 MSB
    mqtt_txbuf[mqtt_txlen++] = BYTE0(topiclen);//主題長度 LSB   
    memcpy(&mqtt_txbuf[mqtt_txlen],topic,topiclen);
    mqtt_txlen += topiclen;
    
    if(whether)
    {
       mqtt_txbuf[mqtt_txlen++] = qos;//QoS級别
    }
 
    ReadData.clear();
    MQTT_SendBuf(mqtt_txbuf,mqtt_txlen);
 
    //開始事件循環
    StartEvenLoop();
 
    if(ReadData.length()==0)
    {
        //開始事件循環
        StartEvenLoop();
    }
    memcpy((char *)mqtt_rxbuf,ReadData.data(),ReadData.length());
 
    if(mqtt_rxbuf[0]==parket_subAck[0] && mqtt_rxbuf[1]==parket_subAck[1]) //訂閱成功
    {
        return 0;//訂閱成功
    }
 
    return 1; //失敗
}
 
//MQTT釋出資料打包函數
//topic   主題 
//message 消息
//qos     消息等級 
quint8 MQTT_WorkClass::MQTT_PublishData(char *topic, char *message, quint8 qos)
{  
    int topicLength = strlen(topic);    
    int messageLength = strlen(message);     
    static quint16 id=0;
    int DataLen;
    mqtt_txlen=0;
    //有效載荷的長度這樣計算:用固定報頭中的剩餘長度字段的值減去可變報頭的長度
    //QOS為0時沒有辨別符
    //資料長度             主題名   封包辨別符   有效載荷
    if(qos) DataLen = (2+topicLength) + 2 + messageLength;       
    else    DataLen = (2+topicLength) + messageLength;   
 
    //固定報頭
    //控制封包類型
    mqtt_txbuf[mqtt_txlen++] = 0x30;    // MQTT Message Type PUBLISH  
 
    //剩餘長度
    do
    {
        quint8 encodedByte = DataLen % 128;
        DataLen = DataLen / 128;
        // if there are more data to encode, set the top bit of this byte
        if ( DataLen > 0 )
            encodedByte = encodedByte | 128;
        mqtt_txbuf[mqtt_txlen++] = encodedByte;
    }while ( DataLen > 0 ); 
    
    mqtt_txbuf[mqtt_txlen++] = BYTE1(topicLength);//主題長度MSB
    mqtt_txbuf[mqtt_txlen++] = BYTE0(topicLength);//主題長度LSB 
    memcpy(&mqtt_txbuf[mqtt_txlen],topic,topicLength);//拷貝主題
    mqtt_txlen += topicLength;
 
    //封包辨別符
    if(qos)
    {
        mqtt_txbuf[mqtt_txlen++] = BYTE1(id);
        mqtt_txbuf[mqtt_txlen++] = BYTE0(id);
        id++;
    }
    memcpy(&mqtt_txbuf[mqtt_txlen],message,messageLength);
    mqtt_txlen += messageLength;
        
    ReadData.clear();
    MQTT_SendBuf(mqtt_txbuf,mqtt_txlen);
 
    //開始事件循環
    StartEvenLoop();
    return mqtt_txlen;
}
 
void MQTT_WorkClass::MQTT_SentHeart(void)
{
    MQTT_SendBuf((quint8 *)parket_heart,sizeof(parket_heart));
}
 
void MQTT_WorkClass::MQTT_Disconnect(void)
{
    MQTT_SendBuf((quint8 *)parket_disconnet,sizeof(parket_disconnet));
}
 
void MQTT_WorkClass::MQTT_SendBuf(quint8 *buf,quint16 len)
{
    if(socket_type)
    {
//        qDebug()<<"len:"<<len;
//        for(int i=0;i<len;i++)
//        {
//            qDebug("%#x ",buf[i]);
//        }
       LocalTcpClientSocket->write((const char *)buf,len);
    }
}   
 
 
//用戶端模式:建立用戶端
void MQTT_WorkClass::ConnectMqttServer(QString ip,quint16 port)
{
    if(LocalTcpClientSocket)
    {
        LocalTcpClientSocket->close();
        delete  LocalTcpClientSocket;
        LocalTcpClientSocket=nullptr;
    }
    /*1. 建立本地用戶端TCP套接字*/
    LocalTcpClientSocket = new QTcpSocket;
    /*2. 設定伺服器IP位址*/
    QHostAddress FarServerAddr(ip);
    /*3. 連接配接用戶端的信号槽*/
    connect(LocalTcpClientSocket,SIGNAL(connected()),this,SLOT(LocalTcpClientConnectedSlot()));
    connect(LocalTcpClientSocket,SIGNAL(disconnected()),this,SLOT(LocalTcpClientDisconnectedSlot()));
    connect(LocalTcpClientSocket,SIGNAL(readyRead()),this,SLOT(LocalTcpClientReadDtatSlot()));
    connect(LocalTcpClientSocket,SIGNAL(bytesWritten(qint64)),this,SLOT(LocalTcpClientBytesWrittenSlot(qint64)));
 
    /*4. 嘗試連接配接伺服器主機*/
    LocalTcpClientSocket->connectToHost(FarServerAddr,port);
}
 
void MQTT_WorkClass::Set_MQTT_Addr(QString ip,quint16 port,QString MQTT_ClientID,QString MQTT_UserName,QString MQTT_PassWord)
{
    m_ip=ip;
    m_port=port;
    m_MQTT_ClientID=MQTT_ClientID;
    m_MQTT_UserName=MQTT_UserName;
    m_MQTT_PassWord=MQTT_PassWord;
}
 
 
//用戶端模式:響應連接配接上伺服器之後的操作
void MQTT_WorkClass::LocalTcpClientConnectedSlot()
{
    socket_type=1;
    //通知外部
    emit MQTT_ConnectState(socket_type);
    //結束事件循環
    EndEvenLoop();
}
 
//用戶端模式:斷開伺服器
void MQTT_WorkClass::LocalTcpClientDisconnectedSlot()
{
    socket_type=0;
 
    //通知外部
    emit MQTT_ConnectState(socket_type);
}
 
 
//用戶端模式:讀取伺服器發過來的資料
void MQTT_WorkClass::LocalTcpClientReadDtatSlot()
{
   ReadData=LocalTcpClientSocket->readAll();
   qDebug()<<"讀取伺服器發過來的資料:"<<ReadData.length();
   EndEvenLoop(); //退出事件循環
}
 
//用戶端模式:資料發送成功
void MQTT_WorkClass::LocalTcpClientBytesWrittenSlot(qint64 byte)
{
    LogSend(QString("資料發送成功:%1\n").arg(byte));
    EndEvenLoop(); //退出事件循環
}
 
//訂閱主題
void MQTT_WorkClass::slot_SubscribeTopic(QString topic)
{
    if(MQTT_SubscribeTopic(topic.toUtf8().data(),0,1))
    {
        LogSend(QString("主題訂閱失敗.\n"));
    }
    else
    {
         LogSend(QString("主題訂閱成功.\n"));
    }
}
 
//釋出消息
void MQTT_WorkClass::slot_PublishData(QString topic,QString message)
{
     MQTT_PublishData(topic.toUtf8().data(),message.toUtf8().data(),0);
}
 
 
void MQTT_WorkClass::EndEvenLoop()
{
    //停止定時器
    timer->stop();
    //先退出事件循環
    loop.exit();
    //qDebug()<<"退出事件循環";
}
 
 
//開始事件循環
void MQTT_WorkClass::StartEvenLoop()
{
    //qDebug()<<"開始事件循環";
    timer->start(5000);
    loop.exec();
}
 
 
//斷開連接配接
void MQTT_WorkClass::slot_tcp_close()
{
    if(socket_type)
    {
        timer->stop();
        loop.exit();
        LocalTcpClientSocket->close();
    }
}
       

4.2 mqtt.h

#ifndef XL_MQTT_H
#define XL_MQTT_H
extern "C"
{
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
}
#include <iostream>
#include <QWidget>
#include <QTcpServer>
#include <QHostInfo>  //擷取計算機網絡資訊
#include <QUdpSocket>
#include <QtNetwork>
#include <QHostInfo>
#include <QDebug>
#include <QTcpSocket>
#include <QHostAddress>
#include <QDebug>
#include <QMessageBox>
#include <QLineEdit>
#include <QHBoxLayout>
#include <QComboBox>
#include <QFile>
#include <QTimer>
#include <QScrollBar>
 
#define BYTE0(dwTemp)       (*(char *)(&dwTemp))
#define BYTE1(dwTemp)       (*((char *)(&dwTemp) + 1))
#define BYTE2(dwTemp)       (*((char *)(&dwTemp) + 2))
#define BYTE3(dwTemp)       (*((char *)(&dwTemp) + 3))
 
typedef enum
{
    //名字        值           封包流動方向  描述
    M_RESERVED1 =0  ,   //  禁止  保留
    M_CONNECT       ,   //  用戶端到服務端 用戶端請求連接配接服務端
    M_CONNACK       ,   //  服務端到用戶端 連接配接封包确認
    M_PUBLISH       ,   //  兩個方向都允許 釋出消息
    M_PUBACK        ,   //  兩個方向都允許 QoS 1消息釋出收到确認
    M_PUBREC        ,   //  兩個方向都允許 釋出收到(保證傳遞第一步)
    M_PUBREL        ,   //  兩個方向都允許 釋出釋放(保證傳遞第二步)
    M_PUBCOMP       ,   //  兩個方向都允許 QoS 2消息釋出完成(保證互動第三步)
    M_SUBSCRIBE     ,   //  用戶端到服務端 用戶端訂閱請求
    M_SUBACK        ,   //  服務端到用戶端 訂閱請求封包确認
    M_UNSUBSCRIBE   ,   //  用戶端到服務端 用戶端取消訂閱請求
    M_UNSUBACK      ,   //  服務端到用戶端 取消訂閱封包确認
    M_PINGREQ       ,   //  用戶端到服務端 心跳請求
    M_PINGRESP      ,   //  服務端到用戶端 心跳響應
    M_DISCONNECT    ,   //  用戶端到服務端 用戶端斷開連接配接
    M_RESERVED2     ,   //  禁止  保留
}_typdef_mqtt_message;
 
 
class MQTT_WorkClass:public QObject
{
    Q_OBJECT
public:
 
    QTimer *timer=nullptr;
    MQTT_WorkClass(QObject* parent=nullptr):QObject(parent){}
    ~MQTT_WorkClass();
 
    //使用者名初始化
    void OneNet_LoginInit(char *ProductKey,char *DeviceName,char *DeviceSecret);
    //MQTT協定相關函數聲明
    quint8 MQTT_PublishData(char *topic, char *message, quint8 qos);
    quint8 MQTT_SubscribeTopic(char *topic,quint8 qos,quint8 whether);
    void MQTT_Init(void);
    quint8 MQTT_Connect(char *ClientID,char *Username,char *Password);
    void MQTT_SentHeart(void);
    void MQTT_Disconnect(void);
    void MQTT_SendBuf(quint8 *buf,quint16 len);
    void ConnectMqttServer(QString ip,quint16 port);
    void Set_MQTT_Addr(QString ip,quint16 port,QString MQTT_ClientID,QString MQTT_UserName,QString MQTT_PassWord);
    void StartEvenLoop();
public slots:
    void EndEvenLoop();
    void run();
    void LocalTcpClientConnectedSlot();
    void LocalTcpClientDisconnectedSlot();
    void LocalTcpClientReadDtatSlot();
    void LocalTcpClientBytesWrittenSlot(qint64 byte);
    //訂閱主題
    void slot_SubscribeTopic(QString topic);
    //釋出消息
    void slot_PublishData(QString topic,QString message);
    //斷開連接配接
    void slot_tcp_close();
signals:
    void LogSend(QString text);
    void MQTT_ConnectState(bool state);
private:
    quint8 *mqtt_rxbuf;
    quint8 *mqtt_txbuf;
    quint16 mqtt_rxlen;
    quint16 mqtt_txlen;
    quint8 _mqtt_txbuf[256];//發送資料緩存區
    quint8 _mqtt_rxbuf[256];//接收資料緩存區
 
    QTcpSocket *LocalTcpClientSocket=nullptr;
    QString m_ip;
    quint16 m_port;
 
    bool socket_type=0;  //這是網絡的狀态: 1表示已經連接配接 0表示未連接配接
 
    QString m_MQTT_ClientID;
    QString m_MQTT_UserName;
    QString m_MQTT_PassWord;
 
    QEventLoop loop;
 
    QByteArray  ReadData;
};
#endif      

繼續閱讀