物联网MQTT协议报文解析(简单的c语音客户端实现)

简介: 物联网MQTT协议报文解析(简单的c语音客户端实现)

MQTT(Message Queuing Telemetry Transport),是一个物联网传输协议,它被设计用于轻量级的发布/订阅式消息传输,旨在为低带宽和不稳定的网络环境中的物联网设备提供可靠的网络服务。MQTT是专门针对物联网开发的轻量级传输协议。MQTT协议针对低带宽网络,低计算能力的设备,做了特殊的优化,使得其能适应各种物联网应用场景。


如今很多第三方推送平台都采用了MQTT来实现,消息中间件ActiveMQ的订阅/发布模块也是基于MQTT实现。


以下为MQTT的 会话,订阅,发布的几个报文的解析:


先看下这张图,为整体的报文结构。大致分为 固定头(Control+PacketLength)+可变头+载体






PS E:\test\mqtt> .\test.exe


test socket:


->connect ok!


//会话,CONNECT


->send:


102900044D51545404C2003C000A0102303330343035303600067075626C696300097061677075626C6963


<-recv:


20020000


<-recv ok!len = 4


//订阅主题


->send:


821C000100176A6B2F636F6D6D616E642F7265616C79636F6E74726F6C00


<-recv:


9003000100


<-recv ok!len = 5


//订阅主题 .


->send:


821D000200186A6B2F636F6D6D616E642F72656164706172616D6574657200


<-recv:


9003000200


//发布消息


->send:


302200166A6B2F72657475726E2F7265616C79636F6E74726F6C61626331323938370000


//收到订阅的主题


<-recv:


301C00176A6B2F636F6D6D616E642F7265616C79636F6E74726F6C998866


<-recv ok!len = 30


请按任意键继续. . .


PS E:\test\mqtt>


报文解析:


//CONNECT  报文



->send:


102900044D51545404C2003C000A0102303330343035303600067075626C696300097061677075626C6963


解析:


//固定报文头


10 //固定报文头 byte1


29 //固定报文头之 byte2


//可变报文头


0004//长度


4D515454 //内容(MQTT)


04  //协议级别


C2//连接标志位


003C //保持连接时长(60秒)


//载体部分


000A//长度


01023033303430353036//用户名


0006


7075626C6963


0009


7061677075626C6963  //密码


<-recv:


20020000


解析:


20 //固定头


02//固定头 byte2 长度


00 //连接确认标志


00 //连接返回码



//订阅主题报文


->send:


821C000100176A6B2F636F6D6D616E642F7265616C79636F6E74726F6C00


解析:


82  //固定报文头 byte1


1C //固定报文头 byte2 (剩余长度)


//可变报文头


00//消息标识符byte1


01//消息标识符byte2


//载荷


0017//主题长度


6A6B2F636F6D6D616E642F7265616C79636F6E74726F6C   // 内容为 : jk/command/realycontrol


00 //服务质量要求Qos


<-recv:


9003000100


应答解析:


90//固定报文头


03 //固定报文头 (长度)


0001 //报文标识符


00


<-recv ok!len = 5


//订阅主题报文


->send:


821D000200186A6B2F636F6D6D616E642F72656164706172616D6574657200


<-recv:


301C00176A6B2F636F6D6D616E642F7265616C79636F6E74726F6C9988669003000200


<-recv ok!len = 35


//发送消息报文(固定头+可变头(主题长度+主题内容+标识符(可选)+载体内容))


->send:


302200166A6B2F72657475726E2F7265616C79636F6E74726F6C61626331323938370000


30  //固定头 (控制字)


22  //固定头(长度)


//可变头


0016


6A6B2F72657475726E2F7265616C79636F6E74726F6C   //主题名内容为: jk/return/realycontrol  


61626331323938370000  // 载体内容为: abc12987


只有当 QoS 等级是 1 或 2 时,报文标识符(Packet Identifier)字段才能出现在 PUBLISH 报文中


/*******************************************************************************
函数名称:MqttConnectPacket
函数功能:按照MQTT协议发送建立连接数据包
输入参数:*packet,连接数据包缓存指针,*id:用户ID号指针,*username:用户名指针 *password:用户密码指针
输出参数:无
备注说明:用户ID是必需的,用户名和密码可以为空
********************************************************************************/
u16 MqttConnectPacket(u8 *mqtt_message,char *client_id,char *username,char *password)
{
  u16 client_id_length = strlen(client_id);
  u16 username_length = strlen(username);
  u16 password_length = strlen(password);
  u16 packetLen;
  u16 i,baseIndex;
  packetLen = 12 + 2 + client_id_length;
  if(username_length > 0)
    packetLen = packetLen + 2 + username_length;
  if(password_length > 0)
    packetLen = packetLen+ 2 + password_length;
  mqtt_message[0] = 16;       //0x10 // MQTT Message Type CONNECT
  mqtt_message[1] = packetLen - 2;  //剩余长度,不包括固定头
  baseIndex = 2;
  if(packetLen > 127)
  {
    mqtt_message[2] = 1;      //packetLen/127;    
    baseIndex = 3;
  }
  mqtt_message[baseIndex++] = 0;    // Protocol Name Length MSB    
  mqtt_message[baseIndex++] = 4;    // Protocol Name Length LSB    
  mqtt_message[baseIndex++] = 77;   // ASCII Code for M    
  mqtt_message[baseIndex++] = 81;   // ASCII Code for Q    
  mqtt_message[baseIndex++] = 84;   // ASCII Code for T    
  mqtt_message[baseIndex++] = 84;   // ASCII Code for T    
  mqtt_message[baseIndex++] = 4;    // MQTT Protocol version = 4    
  mqtt_message[baseIndex++] = 0xC2;   // conn flags 需要用户名和密码认证
  mqtt_message[baseIndex++] = 0;    // Keep-alive Time Length MSB    
  mqtt_message[baseIndex++] = 60;   // Keep-alive Time Length LSB    
  mqtt_message[baseIndex++] = (0xff00&client_id_length)>>8;// Client ID length MSB    
  mqtt_message[baseIndex++] = 0xff&client_id_length;  // Client ID length LSB    
  // Client ID
  for(i = 0; i < client_id_length; i++)
  {
    mqtt_message[baseIndex + i] = client_id[i];    
  }
  baseIndex = baseIndex+client_id_length;
  if(username_length > 0)
  {
    //username    
    mqtt_message[baseIndex++] = (0xff00&username_length)>>8;//username length MSB    
    mqtt_message[baseIndex++] = 0xff&username_length; //username length LSB    
    for(i = 0; i < username_length ; i++)
    {
      mqtt_message[baseIndex + i] = username[i];    
    }
    baseIndex = baseIndex + username_length;
  }
  if(password_length > 0)
  {
    //password    
    mqtt_message[baseIndex++] = (0xff00&password_length)>>8;//password length MSB    
    mqtt_message[baseIndex++] = 0xff&password_length; //password length LSB    
    for(i = 0; i < password_length ; i++)
    {
      mqtt_message[baseIndex + i] = password[i];    
    }
    baseIndex += password_length; 
  } 
  return baseIndex;    
}
/*******************************************************************************
函数名称:MqttPublishPacket
函数功能:按照MQTT协议构建MQTT发布消息包
输入参数:*mqtt_message,连接数据包缓存指针,*topic;消息主题  message:消息内容  message_ln:消息内容长度 qos:服务质量0、1、2
输出参数:无
备注说明:
********************************************************************************/
u16 MqttPublishPacket(u8 *mqtt_message, char * topic,char * message,u16 message_ln, u8 qos)
{  
  u16 topic_length = strlen(topic);    
  u16 message_length = message_ln;//strlen(message);  
  u16 i,index=0;  
  u16 ln = 0;
  static u16 id=0;
  mqtt_message[index++] = 48; //0x30 // MQTT Message Type PUBLISH    
//20180927/// 
//  if(qos)
//    mqtt_message[index++] = 2 + topic_length + 2 + message_length;
//  else
//    mqtt_message[index++] = 2 + topic_length + message_length;   // Remaining length   
  ///以上是老代码,以下是新代码。老代码只能发送小于127字节的数据长度
  //新代码可以发送的长度为16383,即15K//
  ln = 2 + topic_length + message_length;
  if(ln <128)
    mqtt_message[index++] = ln;
  else if(ln < 16384)
  {
      mqtt_message[index++] = (0x80 |(ln%128)); 
    mqtt_message[index++] = ln/128;
  }
//20180927///  
  mqtt_message[index++] = (0xff00&topic_length)>>8;
  mqtt_message[index++] = 0xff&topic_length;
  // Topic    
  for(i = 0; i < topic_length; i++)
  {
    mqtt_message[index + i] = topic[i];
  }
  index += topic_length;
  if(qos)
  {
    mqtt_message[index++] = (0xff00&id)>>8;
    mqtt_message[index++] = 0xff&id;
    id++;
  }
  // Message
  for(i = 0; i < message_length; i++)
  {
    mqtt_message[index + i] = message[i];
  }
  index += message_length;
  return index;
}
/*******************************************************************************
函数名称:MqttPublishAckPacket
函数功能:按照MQTT协议构建MQTT发布消息确认包
输入参数:*mqtt_message,连接数据包缓存指针,*topic  message   qos:服务质量0、1、2
输出参数:无
备注说明://对QoS级别1的 PUBLISH 消息的回应当服务器发送 PUBLISH 消息给订阅者客户端,客户端回复 PUBACK 消息
********************************************************************************/
u8 MqttPublishAckPacket(u8 *mqtt_message)
{
  static u16 id=0;
  mqtt_message[0] = 64;       //0x40 //消息类型和标志 PUBACK
  mqtt_message[1] = 2;        //剩余长度(不包括固定头部)
  mqtt_message[2] = (0xff00&id)>>8; //消息标识符
  mqtt_message[3] = 0xff&id;      //消息标识符
  id++;
  return 4;
}
/*******************************************************************************
函数名称:MqtSubscribePacket
函数功能:按照MQTT协议构建MQTT订阅消息包
输入参数:*mqtt_message,连接数据包缓存指针,*topic;消息主题  message:消息内容   qos:服务质量0、1、2
输出参数:无
备注说明://whether=1,订阅; whether=0,取消订阅
********************************************************************************/ 
u16 MqtSubscribePacket(u8 *mqtt_message,char *topic,u8 qos,u8 whether)
{    
  u16 topic_len = strlen(topic);
  u16 i,index = 0;
  static u16 id=0;
  id++;
  if(whether)
    mqtt_message[index++] = 130;        //0x82 //消息类型和标志 SUBSCRIBE 订阅
  else
    mqtt_message[index++] = 162;        //0xA2 取消订阅
  mqtt_message[index++] = topic_len + 5;      //剩余长度(不包括固定头部)
  mqtt_message[index++] = (0xff00&id)>>8;     //消息标识符
  mqtt_message[index++] = 0xff&id;        //消息标识符
  mqtt_message[index++] = (0xff00&topic_len)>>8;  //主题长度(高位在前,低位在后)
  mqtt_message[index++] = 0xff&topic_len;     //主题长度 
  for (i = 0;i < topic_len; i++)
  {
    mqtt_message[index + i] = topic[i];
  }
  index += topic_len;
  if(whether)
  {
    mqtt_message[index] = qos;//QoS级别
    index++;
  }
  return index;
}
//构建MQTT PING请求包
u8 MqttPingPacket(u8 *mqtt_message)
{ 
  mqtt_message[0] = 192;  //0xC0 //消息类型和标志 PING
  mqtt_message[1] = 0;  //剩余长度(不包括固定头部)
  return 2;
}
//构建MQTT断开连接包
u8 MqttDisconnectPacket(u8 *mqtt_message)
{ 
  mqtt_message[0] = 224;  //0xE0 //消息类型和标志 DISCONNECT
  mqtt_message[1] = 0;  //剩余长度(不包括固定头部)
  return 2;
} 


测试demo:


int main()
{
      int i,rcode,num = 0;
      U32 rxlen=0;
        U08 ip[8]= {47,95,221,93};
        U16 port = 1883;
        U08 txbuf[1024];
        U08 rxbuf[1024];
    u8  *packet;
    u16 ln = 0;
    char  str[100];
    u8 deviceID[6] = {1,2,3,4,5,6};
    u8 userID[6] = {0x31,0x32,0x33,0x34,0x35,0x36};
    u8 companyID[6] ={0x31,0x32,0x33,0x34,0x35,0x36};
    u8 txbuf1[20]={'a','b','c',0x31,0x32,0x39,0x38,0x37};
    memset(txbuf,0,1024);
        memset(rxbuf,0,1024);
        printf("test socket:\r\n");
        rcode = Com_Dev_Connect(ip,port,0,0);
        if(rcode == 0)
        {
          printf("->connect ok!\r\n");  
        }
    RefreshTopicParam(Mqtt.TopicParam,(char*)&userID,(char*)&companyID,(char*)&deviceID);
    packet = &Mqtt.TxBuf[0];     
    DeiveidTostr((char*)deviceID,str);
    ln = MqttConnectPacket(packet,str,"publ","pag");
        rcode = Com_Dev_TxData(packet,ln,0,0);
    sleep(1); 
    rcode = Com_Dev_RxData(rxbuf,&rxlen,1024,0,0);
    if(rcode == 0)
    {
       printf("<-recv ok!len = %d\r\n",rxlen);  
    }
    system("pause");
    //订阅各种消息
    strcpy((char*)str,"jk/command/realycontrol");//订阅继电器控制命令主题
    //strcat((char *)str,(char const *)Mqtt.TopicParam);  
    packet = &Mqtt.TxBuf[0];
    ln = MqtSubscribePacket(packet,(char*)&str,0,1);
    rcode = Com_Dev_TxData(packet,ln,0,0);
    sleep(1); 
    rcode = Com_Dev_RxData(rxbuf,&rxlen,1024,0,0);
    if(rcode == 0)
    {
       printf("<-recv ok!len = %d\r\n",rxlen);  
    }
    system("pause");
    strcpy((char*)str,"jk/command/readparameter");//订阅读参数主题
    //strcat((char *)str,(char const *)Mqtt.TopicParam);  
    packet = &Mqtt.TxBuf[0];
    ln = MqtSubscribePacket(packet,(char*)&str,0,1);
    rcode = Com_Dev_TxData(packet,ln,0,0);
    sleep(1); 
    rcode = Com_Dev_RxData(rxbuf,&rxlen,1024,0,0);
    if(rcode == 0)
    {
       printf("<-recv ok!len = %d\r\n",rxlen);  
    }
    system("pause");
    //发布消息
    strcpy((char*)str,"jk/return/realycontrol");//
    //strcat((char *)str,(char const *)Mqtt.TopicParam);    
    num = 10;
    ln = MqttPublishPacket((u8*)&Mqtt.TxBuf[0],(char*)&str,(char*)&txbuf1,num,0);//继电器应答帧长度为10
    rcode = Com_Dev_TxData(Mqtt.TxBuf,ln,0,0);
    sleep(1); 
    rcode = Com_Dev_RxData(rxbuf,&rxlen,1024,0,0);
    if(rcode == 0)
    {
       printf("<-recv ok!len = %d\r\n",rxlen);  
    }
    system("pause");
  return 0;
}


附带makefile:



########################################
#makefile
########################################
#编译主程序
BINARY  := test
OBJ_DIR := ./
CC= gcc
LD= ld
CFLAGS= -std=c99 -Wall -g
LDSCRIPT= -lws2_32
LDFLAGS= 
SRC  = $(wildcard *.c)
DIR  = $(notdir $(SRC))
OBJS = $(patsubst %.c,$(OBJ_DIR)%.o,$(DIR))
.PHONY: clean 
all:  prebuild  $(BINARY).exe
prebuild:
  @echo Building app...
$(BINARY).exe : $(OBJS)
  @echo Generating ...
  $(CC) -o $(BINARY).exe $(OBJS) $(LDFLAGS) $(LDSCRIPT) 
  @echo OK!
$(OBJ_DIR)%.o : %.c
  $(CC) -c $(CFLAGS) $< -o  $@  
clean:
  rm -f $(OBJ_DIR)*.o
  @echo Removed!
相关文章
|
消息中间件 安全 物联网
海量接入、毫秒响应:易易互联基于 Apache RocketMQ + MQTT 构筑高可用物联网消息中枢
易易互联科技有限公司是吉利集团旗下专注于换电生态的全资子公司,致力于打造安全、便捷、便宜的智能换电网络。公司依托吉利GBRC换电平台,基于电池共享与车辆全生命周期运营,已布局超470座换电站,覆盖40多个城市,计划2027年达2000座。面对海量设备高并发连接、高实时性要求及数据洪峰挑战,易易互联采用阿里云MQTT与RocketMQ构建高效物联网通信架构,实现稳定接入、低延迟通信与弹性处理,全面支撑其全国换电网络规模化运营与智能化升级。
859 1
海量接入、毫秒响应:易易互联基于 Apache RocketMQ + MQTT 构筑高可用物联网消息中枢
|
数据采集 传感器 监控
Modbus 与 MQTT 协议兼容:MyEMS 的泛在能源数据采集技术实现
MyEMS深度融合Modbus与MQTT协议,破解能源数据采集中协议碎片化、网络异构、数据孤岛等难题。通过Modbus接入95%以上工业设备,实现现场数据精准“拉取”;依托MQTT构建高效物联网传输通道,支持多源数据主动“推送”与云端集成。边缘侧采集规整,中心侧汇聚分析,形成统一、可靠、低延迟的数据流。该架构兼具高兼容性、强扩展性与低运维成本,广泛应用于工业园区、商业楼宇及集团型企业,支撑实时监控、AI分析与跨系统融合,打造泛在互联的能源数据底座,助力企业实现全面智慧能源管理。
771 6
|
物联网
(手把手)在华为云、阿里云搭建自己的物联网MQTT消息服务器,免费IOT平台
本文介绍如何在阿里云搭建自己的物联网MQTT消息服务器,并使用 “MQTT客户端调试工具”模拟MQTT设备,接入平台进行消息收发。
4276 42
|
物联网
如何在腾讯云等平台搭建自己的物联网MQTT服务器Broker
物联网技术及MQTT协议被广泛应用于各种场景。本文介绍物联网MQTT服务助手下载,如何搭建自己的物联网平台,并使用 “MQTT客户端调试工具”模拟MQTT设备,接入平台进行消息收发。
1082 37
|
监控 物联网 网络性能优化
【杂谈】-MQTT与HTTP在物联网中的比较:为什么MQTT是更好的选择
通过上述分析,可以看出MQTT在物联网应用中的确是更好的选择。其高效的通信模型、低带宽消耗、稳定的连接保持机制以及可靠的消息质量保证,使其在各种物联网场景中都能表现出色。开发者在设计和实现物联网系统时,应优先考虑采用MQTT协议,以充分发挥其在资源受限环境下的优势,提升系统的整体性能和可靠性。
3896 26
|
监控 安全 Java
Java 开发中基于 Spring Boot 3.2 框架集成 MQTT 5.0 协议实现消息推送与订阅功能的技术方案解析
本文介绍基于Spring Boot 3.2集成MQTT 5.0的消息推送与订阅技术方案,涵盖核心技术栈选型(Spring Boot、Eclipse Paho、HiveMQ)、项目搭建与配置、消息发布与订阅服务实现,以及在智能家居控制系统中的应用实例。同时,详细探讨了安全增强(TLS/SSL)、性能优化(异步处理与背压控制)、测试监控及生产环境部署方案,为构建高可用、高性能的消息通信系统提供全面指导。附资源下载链接:[https://pan.quark.cn/s/14fcf913bae6](https://pan.quark.cn/s/14fcf913bae6)。
2895 0
|
传感器 人工智能 物联网
健康监测设备的技术革命:AI+物联网如何让你随时掌握健康数据?
健康监测设备的技术革命:AI+物联网如何让你随时掌握健康数据?
1688 19
|
存储 安全 物联网
政府在推动物联网技术标准和规范的统一方面可以发挥哪些作用?
政府在推动物联网技术标准和规范的统一方面可以发挥哪些作用?
635 60
|
安全 物联网 物联网安全
制定统一的物联网技术标准和规范的难点有哪些?
制定统一的物联网技术标准和规范的难点有哪些?
809 58
|
存储 数据采集 物联网
物联网技术在物流领域的应用会遇到哪些挑战?
物联网技术在物流领域的应用会遇到哪些挑战?
1054 60

热门文章

最新文章

推荐镜像

更多
  • DNS