社区首页
技术讨论创新帖
全部新帖
资料区
社区活动
联系管理员
★ 社区积分制度
★ 新手必读
★ 申请版主★
请
登录
后使用快捷导航
没有帐号?
注册
首页
|
电子技术
|
嵌入式
模拟电子
单片机
电源管理
传感器
半导体
电子应用
|
工业控制
物联网
汽车电子
网络通信
医疗电子
手机便携
测试测量
安防电子
家用电子
机器人
新能源
电子头条
|
社区
|
论坛
测评
博客
大学堂
|
下载
|
下载中心
电路图
精品文集
电路图
|
参考设计
|
Datasheet
|
活动
|
直播
datasheet
datasheet
文章
搜索
登录
注册
论坛
切换旧版
电子工程世界-论坛
»
论坛
›
电子技术交流
›
国产芯片交流
›
百问MQTT协议分析 - 报文分析①
返回列表
发新帖
回复
阅
66
|
回
0
aleksib
当前离线
纯净的硅(中级)
最后登录
2024-12-13
在线时间
39 小时
威望
992分
芯积分
505分
(兑换)
E金币
0枚
(兑换)
(兑换)
好友
0
aleksib
285
帖子
0
TA的资源
纯净的硅(中级)
+ 好友
私信
楼主
发表于2024-12-13 09:43
只看该作者
百问MQTT协议分析 - 报文分析①
[复制链接]
## 16.3 报文分析 ### 16.3.1 CONNECT-连接服务端 客户端到服务端的网络连接建立(完成三次握手)后,客户端发送给服务端的第一个报文必须是 CONNECT 报文。 ![图3.1 三次握手与mqtt connect交互过程](http://photos.100ask.net/NewHomeSite/MQTT_Image0009.png) 在一个网络连接上,客户端只能发送一次 CONNECT 报文。服务端必须将客户端发送的第二个 CONNECT报文当作协议违规处理并断开客户端的连接。 有效载荷包含一个或多个编码的字段。 包括客户端的唯一标识符, Will 主题, Will 消息, 用户名和密码。 除了客户端标识之外, 其它的字段都是可选的, 基于标志位来决定可变报头中是否需要包含这些字段。 ![图3.2 connect报文组成](http://photos.100ask.net/NewHomeSite/MQTT_Image0010.png) #### 16.3.1.1 connect固定报头
bit
7
6
5
4
3
2
1
0
Byte1
Mqtt报文类型(1)
Reserved(保留位)
0
0
0
1
0
0
0
0
Byte2~n
剩余长度
表格3.1 #### 16.3.1.2 协议名字节组成
说明
7
6
5
4
3
2
1
0
协议名
Byte1
协议名长度MSB(0)
0
0
0
0
0
0
0
0
Byte2
协议名长度LSB(4)
0
0
0
0
0
1
0
0
Byte3
‘M’
0
1
0
0
1
1
0
1
Byte4
‘Q’
0
1
0
1
0
0
0
1
Byte5
‘T’
0
1
0
1
0
1
0
0
Byte6
‘T’
0
1
0
1
0
1
0
0
数据包检测工具, 例如防火墙, 可以使用协议名来识别 MQTT 流量。 #### 16.3.1.3 协议级别
说明
7
6
5
4
3
2
1
0
协议级别
Byte7
Level(4)
0
0
0
0
0
1
0
0
客户端用 8 位的无符号值表示协议的修订版本。对于 3.1.1 版协议,协议级别字段的值是 4(0x04)。如果发现不支持的协议级别,服务端必须给发送一个返回码为 0x01(不支持的协议级别)的CONNACK 报文响应CONNECT 报文, 然后断开客户端的连接。 #### 16.3.1.4 连接标记 | **bit** | **7** | **6** | **5** | **4** | **3** | **2** | **1** | **0** | | ---------- | ---------- | ------------ | ------------ | -------- | --------- | -------- | -------- | ----- | | | 用户名标记 | 用户密码标记 | Will retain | Will qos | Will flag | 清除会话 | reserved | | | **Byte 8** | x | x | x | x | x | x | 0 | | **bit1清除会话** 一般来说, 客户端连接时总是将清理会话标志设置为 0 或 1, 并且不交替使用两种值。 这个选择取决于具体的应用。 清理会话标志设置为 1 的客户端不会收到旧的应用消息, 而且在每次连接成功后都需要重新订阅任何相关的主题。清理会话标志设置为 0 的客户端会收到所有在它连接断开期间发布的 QoS 1 和 QoS 2 级别的消息。因此, 要确保不丢失连接断开期间的消息, 需要使用 QoS 1 或QoS 2 级别,同时将清理会话标志设置为 0。 **Bit2遗嘱标志** 遗嘱标志(Will Flag) 被设置为 1,表示如果连接请求被接受了, 遗嘱(Will Message) 消息必须被存储在服务端并且与这个网络连接关联。之后网络连接关闭时,服务端必须发布这个遗嘱消息, 除非服务端收到DISCONNECT 报文时删除了这个遗嘱消息。 **Bit3和 bit4遗嘱 QoS** 这两位用于指定发布遗嘱消息时使用的服务质量等级, 如果遗嘱标志被设置为 0, 遗嘱 QoS 也必须设置为 0(0x00),如果遗嘱标志被设置为 1, 遗嘱 QoS 的值可以等于 0(0x00), 1(0x01), 2(0x02), 它的值不能等于 3。 **Bit5遗嘱保留** 如果遗嘱消息被发布时需要保留,需要指定这一位的值, 如果遗嘱标志被设置为 0, 遗嘱保留(Will Retain) 标志也必须设置为 0 。 如果遗嘱标志被设置为 1: · 如果遗嘱保留被设置为 0, 服务端必须将遗嘱消息当作非保留消息发布 。 · 如果遗嘱保留被设置为 1, 服务端必须将遗嘱消息当作保留消息发布。 **Bit7** **用户名标志** 如果用户名(User Name) 标志被设置为 0, 有效载荷中不能包含用户名字段。 如果用户名(User Name) 标志被设置为 1, 有效载荷中必须包含用户名字段。 **Bit6** **用户名密码标记** 如果密码(Password) 标志被设置为 0, 有效载荷中不能包含密码字段 。 如果密码(Password) 标志被设置为 1, 有效载荷中必须包含密码字段 。 如果用户名标志被设置为 0, 密码标志也必须设置为 0 。 #### 16.3.1.5 保持连接
bit
7
6
5
4
3
2
1
0
Byte9
保持连接 Keep Alive MSB
Byte10
保持连接 Keep Alive LSB
a) 保持连接(Keep Alive) 是一个以秒为单位的时间间隔,表示为一个 16 位的字,它是指在客户端传输完成。 b) 一个控制报文的时刻到发送下一个报文的时刻, 两者之间允许空闲的最大时间间隔。 客户端负责保证控制。 c) 报文发送的时间间隔不超过保持连接的值。 如果没有任何其它的控制报文可以发送, 客户端必须发送一个PINGREQ 报文。 d) 不管保持连接的值是多少,客户端任何时候都可以发送 PINGREQ 报文,并且使用 PINGRESP 报文判断网络和服务端的活动状态。 e) 如果保持连接的值非零,并且服务端在一点五倍的保持连接时间内没有收到客户端的控制报文, 它必须断开客户端的网络连接, 认为网络连接已断开。 f) 客户端发送了 PINGREQ 报文之后, 如果在合理的时间内仍没有收到 PINGRESP 报文, 它应该关闭到服务端的网络连接。 g) 保持连接的值为零表示关闭保持连接功能。 这意味着,服务端不需要因为客户端不活跃而断开连接。 注意:不管保持连接的值是多少, 任何时候,只要服务端认为客户端是不活跃或无响应的, 可以断开客户端的连接。 #### 16.3.1.6 客户端标识符 服务端使用客户端标识符 (ClientId) 识别客户端。 连接服务端的每个客户端都有唯一的客户端标识符(ClientId) 。客户端和服务端都必须使用 ClientId 识别两者之间的 MQTT 会话相关的状态, 客户端标识符 (ClientId) 必须存在而且必须是 CONNECT 报文有效载荷的第一个字段,客户端标识符必须是UTF-8 编码字符串。 #### 16.3.1.7 遗嘱主题 如果遗嘱标志被设置为 1, 有效载荷的下一个字段是遗嘱主题(Will Topic) 。 遗嘱主题必须是 UTF-8 编码字符串。 #### 16.3.1.8 遗嘱消息 如果遗嘱标志被设置为 1, 有效载荷的下一个字段是遗嘱消息。 遗嘱消息定义了将被发布到遗嘱主题的应用消息。 #### 16.3.1.9 用户名和密码 如果用户名( User Name) 标志被设置为 1, 有效载荷的下一个字段就是它。 用户名必须是定义的UTF-8 编码字符串。服务端可以将它用于身份验证和授权。 如果密码( Password) 标志被设置为 1, 有效载荷的下一个字段就是它。密码字段包含一个两字节的长度字段, 长度表示二进制数据的字节数( 不包含长度字段本身占用的两个字节),后面跟着 0 到 65535 字节的二进制数据。 ![图3.2 用户名和密码在connect报文中的组成](http://photos.100ask.net/NewHomeSite/MQTT_Image0011.png) #### 16.3.10.1 wirshark抓包分析connect报文 从抓包可知,从上到下分别是固定报头,可变报头,连接标记,保持连接,用户名,用名密码,其中没有遗嘱相关消息字段,与3.1.1节分析的固定报头组成分析一致。 ![图 3.3使用wireshark抓包分析connect报文组成格式](http://photos.100ask.net/NewHomeSite/MQTT_Image0012.png) #### 16.3.10.2 c语言构造mqtt connect报文 ```C static uint8_t client_id[512] = {"mqtt_client"}; static uint8_t user_name[512] = {"mqtt"}; static uint8_t passwd[512] = {"12345678"}; #define KEEP_ALIVE 20 int mqtt_connect(int sockfd) { uint8 flags = 0x00; uint8 *packet = NULL; uint16 packet_length = 0; uint16 clientidlen = strlen(client_id); uint16 usernamelen = strlen(user_name); uint16 passwordlen = strlen(passwd); uint16 payload_len = clientidlen + 2; // Variable header uint8 var_header[10] = { 0x00,0x04,/*len*/ 0x4d,0x51,0x54,0x54,/*mqtt*/ 0x04,/*协议版本*/}; uint8 fixedHeaderSize = 2; // Default size = one byte Message Type + one byte Remaining Length uint8 remainLen = 0; uint8 *fixed_header = NULL; uint16 offset = 0; // Preparing the flags if(usernamelen) { /*用户名长度(可选)*/ payload_len += usernamelen + 2; flags |= MQTT_USERNAME_FLAG;/*或上用户名标记*/ } if(passwordlen) { /*用户密码(可选)*/ payload_len += passwordlen + 2; flags |= MQTT_PASSWORD_FLAG;/*用户密码标记位*/ } flags |= MQTT_CLEAN_SESSION; var_header[7] = flags;/*连接标记*/ var_header[8] = KEEP_ALIVE>>8;/*保持连接字段,占用两个字节*/ var_header[9] = KEEP_ALIVE&0xFF; remainLen = sizeof(var_header)+payload_len; /*剩余长度,也就是可变报头加上负载的长度*/ if (remainLen > 127) { fixedHeaderSize++;// add an additional byte for Remaining Length } fixed_header = (uint8 *)malloc(fixedHeaderSize); /*固定报头*/ // Message Type *fixed_header = MQTT_MSG_CONNECT;/*报文类型,connect*/ if (remainLen <= 127) {// Remaining Length,剩余长度计算,可变长编码 *(fixed_header+1) = remainLen; } else { // first byte is remainder (mod) of 128, then set the MSB to indicate more bytes *(fixed_header+1) = remainLen % 128; *(fixed_header+1) = *(fixed_header+1) | 0x80; // second byte is number of 128s *(fixed_header+2) = remainLen / 128; } packet_length = fixedHeaderSize+sizeof(var_header)+payload_len;/*固定报头+可变报头+负载长度*/ packet = (uint8 *)malloc(packet_length);/*分配内存*/ memset(packet, 0, packet_length); memcpy(packet, fixed_header, fixedHeaderSize);/*填充固定报头*/ free(fixed_header); offset += fixedHeaderSize; memcpy(packet+offset, var_header, sizeof(var_header));/*填充可变报头*/ offset += sizeof(var_header); packet[offset++] = clientidlen>>8;// Client ID - UTF encoded,填充clientid长度+clientid packet[offset++] = clientidlen&0xFF; memcpy(packet+offset, client_id, clientidlen); offset += clientidlen; if(usernamelen) {// Username - UTF encoded,填充用户名+用户名长度 packet[offset++] = usernamelen>>8; packet[offset++] = usernamelen&0xFF; memcpy(packet+offset, user_name, usernamelen); offset += usernamelen; } if(passwordlen) {// Password - UTF encoded,填充用户密码+用户名密码长度 packet[offset++] = passwordlen>>8; packet[offset++] = passwordlen&0xFF; memcpy(packet+offset, passwd, passwordlen); offset += passwordlen; } // Send the packet if (client_send(sockfd,packet, packet_length) < 0){ free(packet); return -1; } free(packet); return 1; } ``` ### 16.3.2 CONNACK-确认连接请求 服务端发送 CONNACK 报文响应从客户端收到的 CONNECT 报文。服务端发送给客户端的第一个报文必须是 CONNACK。 #### 16.3.2.1 固定报头
bit
7
6
5
4
3
2
1
0
Byte1
MQTT 控制报文类型 (2)
Reserved 保留位
0
0
1
0
0
0
0
0
Byte2
剩余长度
0
0
0
0
0
0
1
0
剩余长度字段表示可变报头的长度。 对于 CONNACK 报文这个值等于 2。 #### 16.3.2.2 可变报头
描述
7
6
5
4
3
2
1
0
连接确认标记
保留位
SP1
Byte1
0
0
0
0
0
0
0
X
连接返回码
Byte2
x
x
x
x
x
x
x
x
**Byte1,Bit0连接确认标志** 位 7-1 是保留位且必须设置为 0, 对于bit0,如果服务端收到一个 CleanSession 为 0 的连接, 当前会话标志的值取决于服务端是否已经保存了 ClientId对应客户端的会话状态。 如果服务端已经保存了会话状态, 它必须将 CONNACK 报文中的当前会话标志设置为 1 。 如果服务端没有已保存的会话状态, 它必须将 CONNACK 报文中的当前会话设置为 0。 还需要将 CONNACK 报文中的返回码设置为 0。 **连接返回码** 如果服务端发送了一个包含非零返回码的 CONNACK 报文, 那么它必须关 闭网络连接。 | **值** | **返回码响应** | **描述** | | --------- | -------------- | -------------------------------------------------------- | | **0** | 0x00 | 连接已被服务端接受 | | **1** | 0x01 | 服务端不支持客户端请求的协议版本 | | **2** | 0x02 | 客户端标识符是正确的 UTF-8 编码, 但服务 端不允许使用 | | **3** | 0x03 | 网络连接已建立, 但 MQTT 服务不可用 | | **4** | 0x04 | 用户名或密码的数据格式无效 | | **5** | 0x05 | 客户端未被授权连接到此服务器 | | **6-255** | | 保留 | CONNACK没有有效载荷。 #### 16.3.2.3 CONNACK报文wireshark抓包分析 ![图3.4 CONNACK 抓包报文](https://i-blog.csdnimg.cn/img_convert/ef0fbbf3c28f79686e163ab3d3cb4278.png) #### 16.3.2.4 c语言构造connect ack报文 ```c void mqtt_connect_ack(int sockfd) { uint8_t cmd[]={ 0x20/*报文类型*/, 0x02/*剩余长度*/ ,0x00,0x00/*最后两个字节可变报头表示返回状态码*/ }; send_msg(sockfd,cmd,sizeof(cmd)); socket_record_t *socket_record = look_up_by_sokfd(sockfd); if(socket_record==NULL){ return; } socket_record->is_connect=0x01; } ``` ### 16.3.3 PUBLISH-发布消息 PUBLISH 控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。 ![图 3.5 publish报文组成格式](https://i-blog.csdnimg.cn/img_convert/9c959463266236b8e92531190b33b73e.png) #### 16.3.3.1 固定报头
bit
7
6
5
4
3
2
1
0
Byte 1
MQTT报文类型(3)
dup
Qos等级
RETAIN
0
0
1
1
x
x
x
x
Byte2
剩余长度
**Bit3 dup** 如果 DUP 标志被设置为 0, 表示这是客户端或服务端第一次请求发送这个 PUBLISH 报文。 如果 DUP 标志被设置为 1,表示这可能是一个早前报文请求的重发。客户端或服务端请求重发一个 PUBLISH 报文时, 必须将 DUP 标志设置为 1.。 对于 QoS0 的消息, DUP 标志必须设置为 0。 **Bit1和bit2 qos等级** | Qos值 | bit2 | bit1 | 描述 | | ----- | ----- | ----- | ---------------- | | 0 | **0** | **0** | **最多分发一次** | | 1 | **0** | **1** | **至少分发一次** | | 2 | **1** | **0** | **只分发一次** | | - | **1** | **1** | **保留不使用** | qos由发送端决定,发送端发送什么qos的消息,接收端就回复什么qos的消息。 ![不同qos等级mqtt报文交互流程](https://i-blog.csdnimg.cn/img_convert/aaa8f74c1e6ce818f57094db6f895ba1.png) **Bit0** **保留标记位** 一般设置为0。 **剩余长度** 等于可变报头的长度加上有效载荷的长度。 **可变报头** 可变报头按顺序包含主题名和标识符。主题,用于识别有效载荷数据应该被发布到哪一个信息通道,标识符,只有当 QoS 等级是 1 或 2 时,报文标识符( Packet Identifier) 字段才能出现在 PUBLISH 报文中。 #### 16.3.3.2 抓包分析PUBLISH报文 ![图 3.6 PUBLISH 抓包报文](https://i-blog.csdnimg.cn/img_convert/0fe1deef08b30e8ed5cd6d313bbad71f.png) #### 16.3.3.3 构造publish 报文 ```c int mqtt_publish_with_qos(int sockfd,const char* topic, const char* msg, uint16 msgl, uint8 retain, uint8 qos, uint16* message_id) { socket_record_t *socket_record = look_up_by_sokfd(sockfd); if(NULL == socket_record){ return -1; } DEBUG_INFO("sockfd:%d",socket_record->sockfd); uint16 topiclen = strlen(topic); uint16 msglen = msgl; uint8 *var_header = NULL; // Topic size (2 bytes), utf-encoded topic uint8 *fixed_header = NULL; uint8 fixedHeaderSize = 0,var_headerSize = 0; // Default size = one byte Message Type + one byte Remaining Length uint16 remainLen = 0; uint8 *packet = NULL; uint16 packet_length = 0; uint8 qos_flag = MQTT_QOS0_FLAG; /*qos标记*/ uint8 qos_size = 0; // No QoS included if(qos == 1) { qos_size = 2; // 2 bytes for QoS qos_flag = MQTT_QOS1_FLAG; } else if(qos == 2) { qos_size = 2; // 2 bytes for QoS qos_flag = MQTT_QOS2_FLAG; } // Variable header var_headerSize = topiclen/*主题内容*/+2/*主题长度占用两字节*/+qos_size/*标识符*/; var_header = (uint8 *)malloc(var_headerSize); memset(var_header, 0, var_headerSize); *var_header = topiclen>>8; *(var_header+1) = topiclen&0xFF; memcpy(var_header+2, topic, topiclen); if(qos_size) {//qos1和qos2的报文需要填充标识符,有点像tcp的seq socket_record->publish_seq++; if(socket_record->publish_seq == 0){ //unsigned short 表示范围0~65535,标识符必须是非零整数 socket_record->publish_seq = 1; } var_header[topiclen+2] = (socket_record->publish_seq & 0xff00)>>8; var_header[topiclen+3] = socket_record->publish_seq & 0x00ff; if(message_id) { *message_id = socket_record->publish_seq; } } fixedHeaderSize = 2; // Default size = one byte Message Type + one byte Remaining Length remainLen = var_headerSize+msglen; if (remainLen > 127) {/*剩余长度*/ fixedHeaderSize++; // add an additional byte for Remaining Length } fixed_header = (uint8 *)malloc(fixedHeaderSize);/*固定报头+剩余长度*/ // Message Type, DUP flag, QoS level, Retain *fixed_header = MQTT_MSG_PUBLISH | qos_flag;/*报文类型和qos标记*/ if(retain) { *fixed_header |= MQTT_RETAIN_FLAG;/*是否保留*/ } // Remaining Length,剩余长度 if (remainLen <= 127) { *(fixed_header+1) = remainLen; } else { // first byte is remainder (mod) of 128, then set the MSB to indicate more bytes *(fixed_header+1) = remainLen % 128; *(fixed_header+1) = *(fixed_header+1) | 0x80; // second byte is number of 128s *(fixed_header+2) = remainLen / 128; } packet_length = fixedHeaderSize+var_headerSize+msglen;/*固定报头+可变报头+负载长度*/ packet = (uint8 *)malloc(packet_length); memset(packet, 0, packet_length); memcpy(packet, fixed_header, fixedHeaderSize);/*填充固定报头*/ memcpy(packet+fixedHeaderSize, var_header, var_headerSize);/*填充可变报头*/ memcpy(packet+fixedHeaderSize+var_headerSize, msg, msglen);/*负载*/ free(var_header); free(fixed_header); send_msg(sockfd,packet , packet_length); free(packet); return 1; } ``` ### 16.3.4 PUBREC-发布收到 PUBREC 报文是对 QoS 等级 2 的 PUBLISH 报文的响应。它是 QoS 2 等级协议交换的第二个报文。 #### 16.3.4.1 固定报头
bit
7
6
5
4
3
2
1
0
Byte 1
MQTT报文类型(5)
保留位
0
1
0
1
0
0
0
0
Byte2
剩余长度
**剩余长度** 表示可变报头的长度。 对 PUBREC 报文它的值等于 2。 **可变报头**
bit
7
6
5
4
3
2
1
0
Byte1
报文标识符MSB
Byte2
报文标识符LSB
**有效载荷** PUBREC 报文没有有效载荷。 #### 16.3.4.2 PUBREC抓包报文 ![图 3.7 PUBREC抓包报文图示](https://i-blog.csdnimg.cn/img_convert/7a2d734fc3da7af0827d92d7d87b8467.png) #### 16.3.4.3 c语言构造pubrec报文 ```c //如果是PUBREC报文,head_type=0x50 void mqtt_qos2_pubrec(int sockfd , unsigned char *data,unsigned char head_type) { uint16 msg_id = mqtt_parse_msg_id(data);/*报文标识符,回复报文和接受报文的标识符必须一样*/ unsigned char qos2_pubrec_respon[]={head_type/*固定报头*/,0x02/*剩余长度*/, (msg_id&0xff00)>>8 , msg_id&0x00ff/*最后两个字节是报文标识符*/}; send_msg(sockfd,qos2_pubrec_respon,sizeof(qos2_pubrec_respon)); } ``` ### 16.3.5 PUBREL-发布释放 PUBREL 报文是对 PUBREC 报文的响应。 它是 QoS 2 等级协议交换的第三个报文。 #### 16.3.5.1 固定报头
bit
7
6
5
4
3
2
1
0
Byte 1
MQTT报文类型(6)
保留位
0
1
1
0
0
0
0
0
Byte2
剩余长度
**剩余长度** 表示可变报头的长度。 对 PUBREL 报文它的值等于 2。 **可变报头**
bit
7
6
5
4
3
2
1
0
Byte1
报文标识符MSB
Byte2
报文标识符LSB
**有效载荷** PUBREL 报文没有有效载荷。 #### 16.3.5.2 PUBREL抓包报文 ![图 3.8 PUBREL抓包报文图示](https://i-blog.csdnimg.cn/img_convert/3ffe71c54b543e7dd6d3630cde2e019b.png) #### 16.3.5.3 c语言构造pubrel报文 ```c //head_type=0x62 void mqtt_qos2_pubrel(int sockfd , unsigned char *data,unsigned char head_type) { uint16 msg_id = mqtt_parse_msg_id(data); unsigned char qos2_pubrel_respon[]={head_type/*报文类型*/,0x02/*剩余长度*/, (msg_id & 0xff00)>>8 , msg_id & 0x00ff/*最后两个字节是报文标识符*/}; send_msg(sockfd,qos2_pubrel_respon,sizeof(qos2_pubrel_respon)); } ``` ### 16.3.6 PUBCOMP-发布完成 PUBCOMP 报文是对 PUBREL 报文的响应。 它是 QoS 2 等级协议交换的第四个也是最后一个报文。 #### 16.3.6.1 固定报头
bit
7
6
5
4
3
2
1
0
Byte 1
MQTT报文类型(7)
保留位
0
1
1
1
0
0
0
0
Byte2
剩余长度
**剩余长度** 表示可变报头的长度。 对 PUBCOMP 报文它的值等于 2。 **可变报头**
bit
7
6
5
4
3
2
1
0
Byte1
报文标识符MSB
Byte2
报文标识符LSB
**有效载荷** PUBCOMP 报文没有有效载荷。 #### 16.3.6.2 PUBCOMP抓包报文 ![图 3.9 PUBCOMP抓包报文图示](https://i-blog.csdnimg.cn/img_convert/3ae06ec107cc45fd5b7afc597c63f70e.png) #### 16.3.6.3 c语言构造pubcom报文 ```c //head_type=0x70 void mqtt_qos2_pubcomp(int sockfd , unsigned char *data,unsigned char head_type) { uint16 msg_id = mqtt_parse_msg_id(data);/*报文标识符*/ unsigned char qos2_pubcomp_respon[]={head_type/*报文类型*/,0x02/*剩余长度*/, (msg_id & 0xff00)>>8 , msg_id & 0x00ff/*最后两个字节报文标识符*/}; send_msg(sockfd,qos2_pubcomp_respon,sizeof(qos2_pubcomp_respon)); } ``` ### 16.3.7 PINGREQ-心跳请求 客户端发送 PINGREQ 报文给服务端的。用于: a) 在没有任何其它控制报文从客户端发给服务的时,告知服务端客户端还活着。 b) 请求服务端发送 响应确认它还活着。 c) 使用网络以确认网络连接没有断开。 #### 16.3.7.1 固定报头
bit
7
6
5
4
3
2
1
0
Byte 1
MQTT报文类型(12)
保留位
1
1
0
0
0
0
0
0
Byte2
剩余长度 0
**可变报头** 报文没有可变报头。 **有效载荷** PINGREQ 报文没有有效载荷。 #### 16.3.7.2 PINGREQ 抓包报文 ![图 3.10 PINGREQ抓包报文图示](https://i-blog.csdnimg.cn/img_convert/74e2cdd136ed0d2a7e4e8d00a6a5596d.png) #### 16.3.7.3 c语言构造pingreq报文 ```c int mqtt_ping(int sockfd) { uint8 packet[] = {MQTT_MSG_PINGREQ/*报文类型*/,0x00/*剩余长度*/}; int ret = send_msg(sockfd,packet, sizeof(packet)); return ret; } ```
此帖出自
国产芯片交流论坛
点赞
关注
(0)
回复
分享
扫一扫,分享给好友
复制链接分享
链接复制成功,分享给好友
举报
提升卡
变色卡
千斤顶
返回列表
发新帖
回复
您需要登录后才可以回帖
登录
|
注册
发表回复
回帖后跳转到最后一页
活动
更多>>
验证并选择心仪MOSFET,探寻选型奥秘!注册、体验双重好礼等你拿~
免费申请测评 | 泰坦触觉 TITAN Core开发套件
评论有奖:元器件采购的秘密法宝,助你做个自带“松弛感”的职场人!
免费申请测评 | RDK X3机器人开发套件
中星联华直播 | 高速信号完整性分析与测试 — “码”上行动系列线上讲堂
新栏目器件口碑专辑上线~快来点评吧!
2024 DigiKey 应用说:大模型时代的智能汽车
有奖直播报名中!抢占工业4.1先机 文晔科技日等你来!
开源项目
更多>>
LT8410高压电源典型应用电路无需变压器
LT1021DIN8-5 精密电压基准的典型应用
TA8215L 18W BTL x 2ch 音频功率放大器典型应用
基于电脑USB的按摩器(包括原理图和源程序下载)
【训练营】立创EDA智能开关
小夜灯
icebreaker++ :icebreaker FPGA 板,升级了 ECP5
使用基于 LTC4162EUFD-L40 USB PD(C 电缆)的电池充电器和电源路径的典型应用
MIC5255 的典型应用:150mA 低噪声电容 CMOS LDO
随便看看
物联网防火墙himqtt源码之MQTT协议分析
物联网防火墙himqtt源码之MQTT协议分析himqtt是首款完整源码的高性能MQTT物联网防火墙-MQTTApplicationFireWall,C语言编写,采用epoll模式支持IoT数十万的高并发连接,并且兼容ModSecurity部分规则。代码非常优秀,非常值得收藏和学习,今天笔者就从结合himq ...
CAN和CANOPEN的报文该如何分析?
相较于CAN和CANOPEN,MODBUS的报文是非常简单的,举例一帧MODBUS的报文,010620011000EFCA,01代表从机地址,06代表MODBUS的写指令(03是读),2001是丛集要修改的寄存器地址,1000是对这个地址写入的数据值,EFCA是我随便写的表示CRC校验;但是CAN报文和CANO ...
EEWORLD大学堂----直播回放: TI 使用基于 Arm 的 AM6xA 处理器设计智能化楼宇
直播回放:TI使用基于Arm的AM6xA处理器设计智能化楼宇:http://trainingcomcourse/68842
AD画板,把焊盘搞绿了
EEWORLD大学堂----直播回放: Keysight 应对未来高速算力芯片的设计与测试挑战
自用Altium+Access库免费共享了
用VS2005 生成wince时显示错误~~~
OK6410移植u-boot失败问题,u-boot成功生成,但无法在SD卡中启动
stm32F407关于i2s DMA的传输问题
开关电源后端电感尖峰怎么处理
查找数据手册?
搜索
EEWorld Datasheet 技术支持
热门标签
源代码
单片机
放大器
TI
ST
电源
分立器件
传感器
测试测量
模拟
霍尔原理电压传感器
压电式压力传感器
半驱式永磁电机
场强叠加原理
温度控制系统
采样保持电路
linux常用命令
双联电位器
单工通信
AFDX
相关文章
更多>>
英伟达的筹码,又少了一枚
自从生成式AI彻底火热以来,英伟达便乘势而上,着实火热一把。之所以数据中心离不开英伟达,一方面在于GPU,另一方面在于互连技术——英伟达的InfiniBand和NVLink。 用句人话解释,互连技
IBM发布全新光电共封装工艺:AI模型训练速度将提升5倍
12月12日消息,据报道,IBM在光学技术方面获得新进展,有望提升数据中心训练和运行生成式AI模型的效率。 IBM推出了新一代光电共封装(CPO)工艺。该技术利用光学连接,实现了数据中心内部的光速数
谷歌最强 TPU Trillium 芯片商用:性能提升 4.7 倍、内存带宽翻番、节能 67%
12 月 12 日消息,谷歌今天(12 月 12 日)发布博文,宣布正式向 Google Cloud 客户开放第六代 TPU Trillium,希望凭借大的计算能力、高效的性能和可持续特性,更好推动
欧洲汽车停滞,汽车半导体怎么样了?我们来看看ST、英飞凌和恩智浦的近况
美国商务部向美光科技提供 61 亿美元资金,用于在该国生产芯片
IBM 全新光学技术可缩短 GPU 闲置时间,大幅加快 AI 模型训练速度
安森美为什么要收购UnitedSiC ?
比亚迪远程挪车专利获授权,可解决发生事故时的责任归属问题
台积电 11 月销售额 2760.6 亿元新台币,同比增长 34%
谷歌最强量子计算芯片Willow面世
新帖速递
STM32和无源蜂鸣器播放声音的问题
车规级AECQ200介绍,混合铝电解电容器的选择
嵌入式教程_DSP技术_DSP实验箱操作教程:2-28 搭建轻量级WEB服务器实验
OPA847IDBVR运放器国产替代
AG32VF407测试UART
【得捷电子Follow Me第二期】第一章 收到货物的分享
请问这个红外接收头是什么型号?能用哪个型号代替?谢谢
出售全新未拆封ZYNQ 7Z020 FPGA核心板
用在锂电池供电的水表设置上的LORA模块,当有100块水表集中安装在一个楼道内时,节能
请问一下,当某个端口被设置为 RX0后,这个端口的输入输出方向还有必要设置吗
今年怎么这么难,比疫情时还难,三十了面临失业好迷茫
请教稳压管测试问题
【小华HC32F448测评】关于小华半导体的UART中断发送和PRINTF构造和重定向
【BIGTREETECH PI开发板】 HDMI输出测试
【BIGTREETECH PI开发板】+08.音频测试(zmj)
最后2天 | 探秘英飞凌双相电源模块
活动时间:即日起-12月15日
活动奖励:蓝牙音箱、氮化镓充电器套装、黑色小背包
查看 »
有奖直播 |高速信号完整性分析与测试 — “码”上行动系列线上讲堂
直播时间:12月20日(周五)14:00
直播奖励:京东卡、充电宝
查看 »
直播报名最后6天
直播主题:安世半导体理想二极管与负载开关,保障物联网应用的稳健高效运行
直播时间:12月17日(周二)下午14:00
报名就有机会获得:定制双肩商务包、30元京东卡、吸管玻璃杯
查看 »
有奖直播:抢占工业4.1先机 文晔科技日等你来!
直播主题:抢占工业4.1先机 文晔科技日等你来!
直播时间:12月18日(周三)14:00-16:00
预报名参与直播、直播间提问,均有机会获得惊喜好礼!
查看 »
验证并选择心仪MOSFET,探寻选型奥秘!注册、体验双重好礼等你拿~
MOSFET 选型有点难
选N沟道MOSFET?还是选P沟道MOSFET?
封装如何选:不同封装尺寸有不同的热阻和耗散功率。
瞬态散热更严苛,热设计需要如何处理?
用东芝在线电路仿真器,一键解锁MOSFET选型的秘密!
查看 »
评论有奖:元器件采购的秘密法宝,助你做个自带“松弛感”的职场人!
时间:即日起-12月25日
留言评论赢户外露营车、蓝牙音响!
查看 »
有奖直播报名| 看安世650V IGBT产品有何应用优势
为何是高可靠性IGBT的新选择,还能赢【双肩包、京东卡、吸管杯】
【直播时间】12月19日(周四)下午15:00-16:30
查看 »
免费申请测评:RDK X3机器人开发套件
RDK X3是一款面向生态开发者的嵌入式AI开发板,具备5Tops的端侧推理能力,40pin接口兼容树莓派。搭载了伯努利2.0BPU和4核ARM A53处理器。拥有先进的ISP处理算法,使得在宽动态、低照度场景下,也能得到高质量的图像;同时具备强大的视频处理能力,可同时处理多路Camera Sensor的输入并支持H.264/H.265编解码。
查看 »
下载资料赢好礼!看Vicor模块化电源解决方案如何推动创新
活动时间:即日起-2024年12月31日
如何参与:点击活动页内您想了解的模块,找到资料下载即可参与抽奖,活动结束后统一发奖!
查看 »
本周精选下载推荐:电源管理基础Dummies
本周小编给大家带来一本超简单、超干货的电子书——《电源管理基础Dummies》!内容深入浅出,排版舒服简洁,分分钟能get到电源管理最核心的知识内容。
查看 »
关闭
站长推荐
1
/10
电子工程世界版权所有
京B2-20211791
京ICP备10001474号-1
电信业务审批[2006]字第258号函
京公网安备 11010802033920号
Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复
返回顶部
返回列表
论坛首页
版块列表
专业技术中心
TI技术论坛
ST传感器与低功耗无线技术论坛
ADI参考电路
DigiKey得捷技术专区
ADI · 世健工业技术
电子技术交流
嵌入式系统
单片机
国产芯片交流
电机驱动控制
FPGA/CPLD
模拟电子
电源技术
PCB技术
RF/无线
传感器
综合技术交流
下载中心专版
大学堂专版
测评中心专版
创意与实践
电子竞赛
DIY/开源硬件专区
淘e淘
创意市集
行业应用
汽车电子
移动便携
医疗电子
工控电子
安防电子
休息一下
聊聊、笑笑、闹闹
工作这点儿事
为我们提意见&公告
EEWorld颁奖专区
信息发布
最新帖子
最新帖子
最新回复
精华
消灭零回复
测评中心
活动中心
积分兑换
E金币兑换
芯积分
厂商专区
TI技术论坛
ST传感器与低功耗无线技术论坛