ESP8266SDK开发——MQTT例程源码分析之PUBLISH报⽂
⽣成
MQTT PUBLISH消息报⽂格式
查看MQTT官⽅的⽂档,得到固定完整PUBLISH消息发送报⽂格式:
固定报头 + 主题名(可变报头) + 有效载荷
固定报头:MQTT控制报⽂类型【7:4】已经固定为0011b、【3:0】位全部采⽤0。即固定报头:0x30
固定报头代码:
static int ICACHE_FLASH_ATTR init_message(mqtt_connection_t* connection)
{
//由于腾讯云只⽀持单条mqtt信息长度为1Kb以内,所以只需采⽤2个字节
//另⼀个字节是固定报头的第⼀个字节(标志之类的)
connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE;
return MQTT_MAX_FIXED_HEADER_SIZE;
canaan}
剩余长度:主题名的长度(占⽤的字节数) + 主题名长度占⽤的字节数(占⽤2个字节,因为采⽤了UTF-8编码) + 有效载荷长度(占⽤的字节数)
计算剩余长度的代码
static mqtt_message_t* ICACHE_FLASH_ATTR fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain)
{
//计算剩余长度
int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE;
//buffer[0]应该是固定报头: 0x30(PUBLISH报⽂类型;DUP=0;Qos=0;RETAIN=0)
amaranth//buffer[1]:剩余长度:低字节
//buffer[2]:剩余长度:⾼字节
//但是腾讯云MQTT的报⽂长度没有达到1024字节。
if(remaining_length > 127)
{
connection->buffer[0] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1);
connection->buffer[1] = 0x80 | (remaining_length % 128); //剩余长度:低字节
connection->buffer[2] = remaining_length / 128; //剩余长度:⾼字节
connection->message.length = remaining_length + 3; //多此⼀举(可以删除).这⾥connection->message.length已经是整个mqtt报⽂长度了
connection->message.data = connection->buffer;
}
el //实际进⼊到这⾥了
{
connection->buffer[1] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1);
connection->buffer[2] = remaining_length; //剩余长度
connection->message.length = remaining_length + 2; //多此⼀举(可以删除).这⾥connection->message.length已经是整个mqtt报⽂长度了
connection->message.data = connection->buffer + 1; //丢弃buffer[0]因为这⼀个字节没有⽤到。固定
报⽂头只占2个字节
}
//->的优先级⾼于&,所以代码等效为&(connection->message)
return &connection->message;
}
⽂档关于剩余长度的说明:剩余长度字段使⽤⼀个变长度编码⽅案,对⼩于128的值它使⽤单字节编码。更⼤的值按下⾯
的⽅式处理。低7位有效位⽤于编码数据,最⾼有效位⽤于指⽰是否有更多的字节。因此每个
字节可以编码128个数值和⼀个延续位(continuation bit)。剩余长度字段最⼤4个字节。
注:由于我们ESP8266 SDK中带的MQTT例程源码中定义的MQTT数据包长度为1024Kb,所以这⾥我们这⾥的剩余长度只要考虑 2个字节的情况就⾏了。
描述76543210
Topic Name 主题名
byte 1Length MSB X X X X X X X X
byte 2Length LSB X X X X X X X X
X X X X X X X X
针对以上表格说明:
byte1和byte2表⽰主题名长度,主题名长度采⽤低字节在前编码。所以byte1其实是低字节,byte2表⽰⾼字节。
⽐如:byte1为0xc0,byte2为0x01。那么实际主题名长度为0x01c0换算成10进制为128+64=192
<表⽰:这⾥有多个字节,这个需要根据具体的主题名决定。
Topic主题添加的代码:
static int ICACHE_FLASH_ATTR append_string(mqtt_connection_t* connection, const char* string, int len)
{
//整个可变报头长度:Topic Name内容长度+Topic Name内容长度占⽤了2个字节 = len + 2
if(connection->message.length + len + 2 > connection->buffer_length)
return -1;
//可变报头:存储Topic Name的长度需要以UTF-8编码存储。2个字节:低字节在前,⾼字节在后
connection->buffer[connection->message.length++] = len >> 8; //获取低字节数据
connection->buffer[connection->message.length++] = len & 0xff; //⾼字节数据
//可变报头:存储Topic Name的内容
memcpy(connection->buffer + connection->message.length, string, len);
//消息长度:加上Topic Name的内容长度
connection->message.length += len;
/
/返回“可变报头”转换成mqtt格式所占⽤的字节长度(len + 2)
return len + 2;
}
有效载荷
有效载荷包含将被发布的应⽤消息。数据的内容和格式是应⽤特定的(⼀般⽤JOSN格式)。
有效载荷的长度:就是有效载荷字符串的长度。包含零长度有效载荷的 PUBLISH报⽂是合法的
MQTT PUBLISH源码分析:
因为ESP8266 SDK中的MQTT例程中MQTT数据包buffer长度最⼤为1024字节。所以剩余长度最多占⽤2个字节有效载荷添加代码
//判断当前data长度+已经保存的Topic Name + Topic Name长度占⽤的2字节 + 固定报头长度(3字节)
//是否⽐缓存空间⼤
if(connection->message.length + data_length > connection->buffer_length)健康管理师有什么用
return fail_message(connection);
//有效载荷:拼接data数据到buffer,即将data数据填充到“有效载荷”缓冲区域
雅思学习资料//注意:有效载荷没有表⽰长度的字节
memcpy(connection->buffer + connection->message.length, data, data_length);
//记录保存了“有效载荷”后的message长度
connection->message.length += data_length;
完整的MQTT PUBLISH报⽂打包代码:
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, i {
//connection->buffer空间为1024字节
//初始化固定报头:给它预留3个字节空间⽤于储存数据
jillian murrayinit_message(connection);
if(topic == NULL || topic[0] == '\0')
return fail_message(connection);
//拼接topic数据到buffer中
if(append_string(connection, topic, strlen(topic)) < 0)
return fail_message(connection);
if(qos > 0)
{
if((*message_id = append_message_id(connection, 0)) == 0)
return fail_message(connection);electra
}
el
*message_id = 0;
//判断当前data长度+已经保存的Topic Name + Topic Name长度占⽤的2字节 + 固定报头长度(3字节)
圣经在线阅读有声朗读//是否⽐缓存空间⼤
if(connection->message.length + data_length > connection->buffer_length)
欢迎的英文
return fail_message(connection);
//有效载荷:拼接data数据到buffer,即将data数据填充到“有效载荷”缓冲区域
//注意:有效载荷没有表⽰长度的字节
brownsugarmemcpy(connection->buffer + connection->message.length, data, data_length);
//记录保存了“有效载荷”后的message长度
connection->message.length += data_length;
//剩余长度:fini_message函数中将message中buffer指针
金贤重官网
return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain);
}