一、使用MQTT协议与服务器建立连接
1.在图纸中的位置输入EMQ服务器地址与EMQ服务器的端口。
2.接下来我们来看一下MQTT协议中的CONNECT报文、
(1)固定报头
byte1为0x10,表示向服务器端发送的为CONNECT报文,剩余长度值后等学到再继续介绍。我们先用##来代替。
那么现在CONNECT报文的前两个字节为 01 ##
(2)可变报头
CONNECT的可变报头按顺序有以下四个字段:协议名、协议级别、连接标志、保持连接。
a)协议名
按照MQTT文档所给出的表格,把他们表示为16进制为:
00 04 4D 51 54 54。
b)协议级别
现在所用的MQTT协议版本为3.1.1,那么协议级别的值为4(0x40),其他的协议级别,在对应的协议文档中应该都会给出。那么第七位字节为 04
c) 连接标志
在EMQ服务器的设置中,我启动了emqx_auth_mysql,开启了认证。所以登录的时候是需要用户名与密码的,服务质量QoS一般都设为0。清理会话标志位,一般也会设置为1(等我理解了如何使用后在来详细介绍)。根据自己的需求,设置此字节为 11000010转换为十六进制对应 C2。
d) 保持连接
保持连接是一个以秒为单位的时间间隔,表示为一个16位的字。我们把它设置为100秒。那么这两位为 00 64
可变报头这样前期我们就基本固定为:00 04 4D 51 54 54 04 C2 00 64
(3) 有效负荷
可变报头中的连接标志决定了有效负荷中所要包含的信息,如果包含信息的话,则必须按照以下顺序填写:客户端标识符、遗嘱主题、遗嘱消息、用户名、密码。
1.首先我们设置客户标识符:
举个例子:当把客户标识符设为sun时,会在EMQ客户端出现
这种标识,该设备的客户端ID就被设置为了sun方便以后查找使用。那么此时客户端ID的十六进制表示为 73 75 6E,接下来在这三个字节的前面加上客户端ID的字节长度占用两字节:00 03,组合一下就是00 03 73 75 6E
由于没有设置遗嘱相关,所以我们就跳过遗嘱相关信息,直接填写用户的相关信息。应为在之前已配置好并启动了emqx_auth_mysql插件,在数据库中存入了一个user。其用户名为222,密码为111111,所以我们所要填入的就是这两个信息。
首先是用户名,将222转变为十六进制形式,注意这里的222并不是十进制的222而是字符串,转换的时候要留意,变为 32 32 32,同样在前面加上两个字节表示长度 00 03 ,组合下,变为:00 03 32 32 32。
接下来是用户密码,同用户名一样的处理变为:00 06 31 31 31 31 31 31。
当这些都转化完成后,我们要开始连接我们的服务器了,把上面的所有转化好的十六进制字节合并起来,变为:10 ## 00 04 4D 51 54 54 04 C2 00 64 00 03 73 75 6E 00 03 32 32 32 00 06 31 31 31 31 31 31
就剩下一个##是位置的了,先用最笨的方法,数一下##之后共有多少字节,那么##就是多少。后面会学到使用算法来计算剩余长度,所以就先数一下把。数完以后,我们这里是一共28个字节,所以 ## = 1C 。那么完整版就为:10 1C 00 04 4D 51 54 54 04 C2 00 64 00 03 73 75 6E 00 03 32 32 32 00 06 31 31 31 31 31 31
在网络助手中在下面选择HEX形式发送,把配置好的CONNECT报文复制到发送框,发送之后会接受到服务器返回的 20 02 00 00,此时就表示我们成功连接到EMQ服务器啦!在EMQ的客户端页面就可以查看到我们的设备已经上线了。
3.之前在做实验的时候,用到过中移物联OneNET,终端设备是通过EDP协议与OneNET建立连接的。就试着模仿EDP协议来写了一个简单的MQTT协议,来实现基本的通讯。下面是MQTT连接报文的C语言实现:
/*************** 动态申请内存 *************************/
#define MQTT_MALLOC_BUFF malloc
#define MQTT_FREE_BUFF free
/*************** 高低位先行 *************************/
#define MSB(A) (uint8)((A & 0xFF00) >> 8)
#define LSB(A) (uint8)(A & 0x00FF)
typedef struct Buffer
{
uint8 * _data; //协议数据
uint32 _len; //写入的数据长度
uint32 _size; //缓存总大小
uint8 _memFlag; //内存使用的方案:0-未分配 1-使用的动态分配 2-使用的固定内存
} MQTT_PACKET_STRUCTURE;
/**
* 函数名: MQTT_NewBuff
* 作用 : 申请内存
* 参数 : MQTT_Packet:包体结构
* size: 所要分配的内存大小
*/
void MQTT_NewBuff(MQTT_PACKET_STRUCTURE *MQTT_Packet,uint32 size)
{
uint32 i = 0;
if(MQTT_Packet->_data == NULL)
{
MQTT_Packet->_memFlag = MEM_FLAG_ALLOC;
// 分配内存,MALLOCK返回值为所分配内存的开始位置的指针
MQTT_Packet->_data = (uint8 *)MQTT_MALLOC_BUFF(size);
if(MQTT_Packet->_data != NULL)
{
// 内存分配成功
MQTT_Packet->_len = 0;
MQTT_Packet->_size = size;
for (i = 0; i < size; i++)
{
// 清空数据
MQTT_Packet->_data[i] = 0;
}
}
}
else
{
// 使用全局变量或者局部变量来分配内存
MQTT_Packet->_memFlag = MEM_FLAG_STATIC;
for(i = 0; i < MQTT_Packet->_size; i++)
{
MQTT_Packet->_data[i] = 0;
}
MQTT_Packet->_len = 0;
if(MQTT_Packet->_size < size)
{
// 如果所给的内存小于数据内存 则分配失败
MQTT_Packet->_data = NULL;
}
}
}
/**
* 函数名: MQTT_FreeBuff
* 作用 : 释放内存
* 参数 : MQTT_Packet:包体结构
*
*/
void MQTT_FreeBuff(MQTT_PACKET_STRUCTURE *MQTT_Packet)
{
MQTT_FREE_BUFF(MQTT_Packet->_data);
// 重置包体结构
MQTT_Packet->_data = NULL;
MQTT_Packet->_len = 0;
MQTT_Packet->_size = 0;
MQTT_Packet->_memFlag = MEM_FLAG_NULL;
}
uint8 MQTT_Connect(const int8 * user_name,const int8 * password,uint16 keep_time,MQTT_PACKET_STRUCTURE * MQTT_Packet)
{
// ClientID设置为与用户名相同
const int8 * ClientID = user_name;
uint8 ClientID_len = strlen(user_name);
// 用户名称暂时为10
uint8 user_name_len = strlen(user_name);
// 密码长度为36
uint8 password_len = strlen(password);
// 80足够
MQTT_NewBuff(MQTT_Packet,80);
if(MQTT_Packet->_data == NULL)
{
// 内存分配失败
printf("内存分配失败!\n");
return 0;
}
/* byte0: 消息类型 */
MQTT_Packet->_data[0] = CONNECT;
MQTT_Packet->_len++;
/* byte1: 剩余消息长度 */
// 16 = 10 + 2 + 2 + 2 (10个固定长度,2表示ClientID,user_name,password长度表示位)
MQTT_Packet->_data[1] = 16 + ClientID_len + user_name_len + password_len;
MQTT_Packet->_len++;
/* byte2-7: 协议描述 */
MQTT_Packet->_data[2] = 0;
MQTT_Packet->_data[3] = 4;
strncat((int8 *)MQTT_Packet->_data + 4,"MQTT",4);
MQTT_Packet->_len += 6;
/* byte8: 协议级别 */
MQTT_Packet->_data[8] = 4;
MQTT_Packet->_len++;
/* byte9: 连接标志 */
MQTT_Packet->_data[9] = 0xC0; // 保留会话(没有遗嘱,存在用户名与密码消息)
MQTT_Packet->_len++;
/* byte10-11: 保持连接时间 */
MQTT_Packet->_data[10] = MSB(keep_time);
MQTT_Packet->_data[11] = LSB(keep_time);
MQTT_Packet->_len += 2;
/* ClientID */
MQTT_Packet->_data[12] = MSB(ClientID_len);
MQTT_Packet->_data[13] = LSB(ClientID_len);
MQTT_Packet->_len += 2;
strncat((int8 *)MQTT_Packet->_data + 14,ClientID,ClientID_len);
MQTT_Packet->_len += ClientID_len;
/* user_name */
MQTT_Packet->_data[14 + ClientID_len] = MSB(user_name_len);
MQTT_Packet->_data[15 + ClientID_len] = LSB(user_name_len);
MQTT_Packet->_len += 2;
strncat((int8 *)MQTT_Packet->_data + 16 + ClientID_len,user_name,user_name_len);
MQTT_Packet->_len += user_name_len;
/* password */
MQTT_Packet->_data[16 + ClientID_len + user_name_len] = MSB(password_len);
MQTT_Packet->_data[17 + ClientID_len + user_name_len] = LSB(password_len);
MQTT_Packet->_len += 2;
strncat((int8 *)MQTT_Packet->_data + 18 + ClientID_len + user_name_len,password,password_len);
MQTT_Packet->_len += password_len;
return 1;
}
最后放上这个网络调试助手的下载地址吧
http://free.cmsoft.cn/download/cmsoft/netassist.zip