物联网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!
相关文章
|
5月前
|
消息中间件 安全 物联网
海量接入、毫秒响应:易易互联基于 Apache RocketMQ + MQTT 构筑高可用物联网消息中枢
易易互联科技有限公司是吉利集团旗下专注于换电生态的全资子公司,致力于打造安全、便捷、便宜的智能换电网络。公司依托吉利GBRC换电平台,基于电池共享与车辆全生命周期运营,已布局超470座换电站,覆盖40多个城市,计划2027年达2000座。面对海量设备高并发连接、高实时性要求及数据洪峰挑战,易易互联采用阿里云MQTT与RocketMQ构建高效物联网通信架构,实现稳定接入、低延迟通信与弹性处理,全面支撑其全国换电网络规模化运营与智能化升级。
383 1
海量接入、毫秒响应:易易互联基于 Apache RocketMQ + MQTT 构筑高可用物联网消息中枢
|
11月前
|
消息中间件 存储 数据采集
4步实现状态机驱动的MQTT客户端,快速接入OneNet (1)
本文介绍了基于状态机驱动的MQTT客户端快速接入OneNet平台的实现方法,通过4步完成模块设计。文章以开源项目`Sparrow`为基础,引入`OneNetMqtt`业务模块,采用事件驱动模型和双层状态机设计,实现设备状态管理、消息处理及定时任务等功能。模块分为三层:`OneNetManager`负责核心逻辑,`OneNetDevice`管理设备信息,`OneNetDriver`处理Socket与MQTT通信。验证结果显示设备连接、数据上报及下线功能正常,稳定性良好。该设计简化了复杂条件判断,增强了系统灵活性与可扩展性,适用于实际项目参考。文末提供源码获取方式,助力读者实践与学习。
674 109
|
10月前
|
数据采集 监控 网络协议
​MCP协议深度解析:原理、应用与物联网时代的机遇-优雅草卓伊凡
​MCP协议深度解析:原理、应用与物联网时代的机遇-优雅草卓伊凡
1132 40
​MCP协议深度解析:原理、应用与物联网时代的机遇-优雅草卓伊凡
|
9月前
|
物联网
(手把手)在华为云、阿里云搭建自己的物联网MQTT消息服务器,免费IOT平台
本文介绍如何在阿里云搭建自己的物联网MQTT消息服务器,并使用 “MQTT客户端调试工具”模拟MQTT设备,接入平台进行消息收发。
3019 42
|
9月前
|
物联网
如何在腾讯云等平台搭建自己的物联网MQTT服务器Broker
物联网技术及MQTT协议被广泛应用于各种场景。本文介绍物联网MQTT服务助手下载,如何搭建自己的物联网平台,并使用 “MQTT客户端调试工具”模拟MQTT设备,接入平台进行消息收发。
720 37
|
9月前
|
运维 监控 网络协议
物联网设备状态监控全解析:从告警参数到静默管理的深度指南-优雅草卓伊凡
物联网设备状态监控全解析:从告警参数到静默管理的深度指南-优雅草卓伊凡
299 11
物联网设备状态监控全解析:从告警参数到静默管理的深度指南-优雅草卓伊凡
|
消息中间件 Java Apache
RocketMQ消息回溯实践与解析
在分布式系统和高并发应用的开发中,消息队列扮演着至关重要的角色,而RocketMQ作为阿里巴巴开源的一款高性能消息中间件,以其高吞吐量、高可用性和灵活的配置能力,在业界得到了广泛应用。本文将围绕RocketMQ的消息回溯功能进行实践与解析,分享工作学习中的技术干货。
360 4
|
11月前
|
监控 物联网 网络性能优化
【杂谈】-MQTT与HTTP在物联网中的比较:为什么MQTT是更好的选择
通过上述分析,可以看出MQTT在物联网应用中的确是更好的选择。其高效的通信模型、低带宽消耗、稳定的连接保持机制以及可靠的消息质量保证,使其在各种物联网场景中都能表现出色。开发者在设计和实现物联网系统时,应优先考虑采用MQTT协议,以充分发挥其在资源受限环境下的优势,提升系统的整体性能和可靠性。
2284 26
|
消息中间件 存储 监控
RocketMQ消息重试机制解析!
RocketMQ消息重试机制解析!
1289 1
RocketMQ消息重试机制解析!
|
消息中间件 存储 Java
RocketMQ文件刷盘机制深度解析与Java模拟实现
【11月更文挑战第22天】在现代分布式系统中,消息队列(Message Queue, MQ)作为一种重要的中间件,扮演着连接不同服务、实现异步通信和消息解耦的关键角色。Apache RocketMQ作为一款高性能的分布式消息中间件,广泛应用于实时数据流处理、日志流处理等场景。为了保证消息的可靠性,RocketMQ引入了一种称为“刷盘”的机制,将消息从内存写入到磁盘中,确保消息持久化。本文将从底层原理、业务场景、概念、功能点等方面深入解析RocketMQ的文件刷盘机制,并使用Java模拟实现类似的功能。
379 3

热门文章

最新文章

推荐镜像

更多
  • DNS