一文带你了解 MQTT 协议(连接 ONE-NET平台)

2023-11-06

MQTT 协议连接 ONE-NET 详解

写在前面

​  本文采用 网络调试助手 发送MQTT协议报文(16进制) 连接 ONE-NET 平台,采用的 为 MQTT v3.1.1 标准协议。带你直接 学会 MQTT 协议。

一、ONE-NET 端创建设备

​   由于我们需要使用 MQTT 协议 连接 ONE-NET 平台,所以我们需要先创建一个设备。

1. 进入 ONE-NET 平台,进入控制台首页,切换置旧版

2. 点击最左侧圆球,选择多协议接入

3. 我们这次采用的是 MQTT 协议,所以直接创建MQTT 协议的产品即可。

4. 联网方式选择 wifi 即可

5. 再添加设备,注意:鉴权信息就是你后面需要使用的 密码,下面的鉴权信息是 博主本人的 QQ号,有需要讨论问题可以添加!

6. 这时候我们需要记录三个连接 ONE-NET 的数据,一个是设备 ID,一个是产品 ID,一个是鉴权信息

​  设备ID 和 密码的话:就是你前面设置的鉴权信息。如果遗忘的话,可以在设备详情里查找

 最后,ONE-NET 端 的设备就创建完毕了。

二、通过 MQTT v3.1.1协议 编写对应的报文

​  MQTT 协议报文由三部分组成,固定报文,可变报头和有效载荷,所以我们编写 MQTT 协议报文也需要从这三方面入手。

1. 通过网络调试助手连接报文 连接 ONE-NET 端
1. 固定报头

 首先是固定报头,上述是 CONNECT 报文的固定报头,由图可知 byte1 = 10,剩余长度是可变报头+有效载荷,如下图所示,剩余长度最多为四个字节,最高位如果 为 1 ,代表下面还有 至少一个字节数。后面当所有报文都编写完后再计算剩余长度大小。

2. 可变报头

​ 如上图所示,我们可以编写出 byte1 - byte7,大概为 00 04 4D 51 54 54 04 (16进制数)

​ byte 8 表示,由于 ONE-NET 端不允许匿名登录,所以我们需要将 用户名标志,密码标志置 1,由于我们为初学者,我们只需要再掌握一个QoS服务质量即可,下面是服务质量分析图。

​  一般我们采用 最多分发一次即可。也就是 QoS = 0 ,bit2 bit1 为 00

​  byte 9 和 byte 10 为 保活时间,我这边采取 100 的 保活时间,变成16进制数也就是 64

 ​ 所以 byte9 和byte 10 分别为 00 64

​   所以固定报头和可变报头为:10 xx(未计算剩余长度) 00 04 4d 51 54 54 04 c0 00 64

3. 有效载荷

 我们需要验证 设备号,用户名(产品 ID)和密码(鉴权信息)。
由于报文为16进制数,我们可以通过网络助手快速计算出这些数据的16进制数和字节长度。

 先选择 UDP 形式,连接远程主机,并且分别把设备 ID,产品ID,密码输入

​  再点击 16 进制数,将该数值转化为 16 进制数。发送的数值为 9 ,注意:要转换成16进制数,换算成 16 进制数 也是 9。所以不必对其进行额外的 进制转换。

 所以有效载荷就为:00 09 37 38 39 35 34 36 38 30 35 00 06 34 35 38 39 34 35 00 09 31 33 36 39 32 38 38 33 31 (先输入 设备号 大小 00 - 09 再输入其设备号,后续一样操作)

 最后将 可变报头和有效载荷 一起输入到网络调试助手计算字节大小,注意:复制数值前先勾选16进制数,因为我们已经将前面的转换为16进制数了

 我这边为 40 ,转化为 16进制数为 28 ,所以我的剩余长度就为 28

 CONNECT连接报文总共为:10 28 00 04 4d 51 54 54 04 c0 00 64 00 09 37 38 39 35 34 36 38 30 35 00 06 34 35 38 39 34 35 00 09 31 33 36 39 32 38 38 33 31

 这时候我们将 网络调试助手 换成 TCP Client 端,远程主机地址为:183.230.40.39 :6002,并且在复制前先勾选16进制发送,和16进制数接受,换行等。

 由图我们发送连接报文,发现返回 20 02 00 00,并且设备已经在线了,说明我们连接成功。

 CONNACK - 确认连接请求 固定报头 就为 20 02 ,也再一次证明为 连接确认请求

​  其他的报文也和这个 连接报文 差不多,可以自己阅读 MQTT v3.1.1 标准协议。

 下面是 Keil5 编写的 MQTT 连接报文

uint8 MQTT_PacketConnect(const int8 *user, const int8 *password, const int8 *devid,
						uint16 cTime, uint1 clean_session, uint1 qos,
						const int8 *will_topic, const int8 *will_msg, int32 will_retain,
						MQTT_PACKET_STRUCTURE *mqttPacket)
{
	//flags 用于接收 byte 8 ,判断用户名,密码,服务质量,遗嘱信息等	
	uint8 flags = 0;															
	uint8 will_topic_len = 0;															
	uint16 total_len = 15;
	int16 len = 0, devid_len = strlen(devid);   //设备号
	
	if(!devid)									//如果设备号为空,直接退出
		return 1;
	
	total_len += devid_len + 2;       			// + 2 表示需要使用两个字节表示 设备号的起始和结束位置
	
	//判断断线后,是否清理离线消息:1-清理, 0-不清理
	if(clean_session)		//默认为 0 就好
	{
		flags |= MQTT_CONNECT_CLEAN_SESSION; //MQTT_CONNECT_CLEAN_SESSION = 0x02,根据 byte8 第2位为 1 的话代表清理离线消息
	}
	
	//异常掉线是,服务器发送的 Topic
	if(will_topic)				//默认为 NULL 即可
	{
		flags |= MQTT_CONNECT_WILL_FLAG;
		will_topic_len = strlen(will_topic);
		total_len += 4 + will_topic_len + strlen(will_msg);
	}
	
	//qos 级别,主要用于 publish 消息
	switch((unsigned char)qos)
	{
		case MQTT_QOS_LEVEL0:
			flags |= MQTT_CONNECT_WILL_QOS0;			//最多一次
		break;
		
		case MQTT_QOS_LEVEL1:
			flags |= (MQTT_CONNECT_WILL_FLAG | MQTT_CONNECT_WILL_QOS1);	//最少一次
		break;
		
		case MQTT_QOS_LEVEL2:
			flags |= (MQTT_CONNECT_WILL_FLAG | MQTT_CONNECT_WILL_QOS2);	//只有一次
		break;
		
		default:
		return 2;
	}
	
	//主要用于 publish 消息,代表服务器要保留这次推送的信息,如果有新的订阅者,就把该消息推送给它
	if(will_retain)
	{
		flags |= (MQTT_CONNECT_WILL_FLAG | MQTT_CONNECT_WILL_RETAIN);
	}
	
	//ONE-NET 不允许匿名登录,需要判断
	if(!user || !password)
	{
		return 3;
	}
	flags |= MQTT_CONNECT_USER_NAME | MQTT_CONNECT_PASSORD; //byte8 判断是否有用户名,密码
	
	total_len += strlen(user) + strlen(password) + 4;		//总长度加 4
	
	//分配新内存
	MQTT_NewBuffer(mqttPacket, total_len);   //本质是 malloc 创建
	if(mqttPacket->_data == NULL)			//由于创建的时候,将data全部置0,所以做个判断
		return 4;
	
	memset(mqttPacket->_data, 0, total_len); //清空下 data,保证无数据
	
/*************************************固定头部******************************************/
	
	//byte1---------------------连接报文的话为 10 ------------------------------------------
	mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_CONNECT << 4;//1 左移四位
	
	//byte2----------------------剩余长度计算----------------------------------------------
	len = MQTT_DumpLength(total_len - 5, mqttPacket->_data + mqttPacket->_len);
	if(len < 0)
	{
		MQTT_DeleteBuffer(mqttPacket);
		return 5;
	}
	else
		mqttPacket->_len += len;    //添加剩余长度字节大小 len 大小为0-4
	
/*************************************可变报头************************************/
	
	//可变报头----------------------协议长度和协议名(固定的)---------------------------------
	mqttPacket->_data[mqttPacket->_len++] = 0;
	mqttPacket->_data[mqttPacket->_len++] = 4;
	mqttPacket->_data[mqttPacket->_len++] = 'M';
	mqttPacket->_data[mqttPacket->_len++] = 'Q';
	mqttPacket->_data[mqttPacket->_len++] = 'T';
	mqttPacket->_data[mqttPacket->_len++] = 'T';
	
	//----------------------protocol level 4(固定)---------------------------------------
	mqttPacket->_data[mqttPacket->_len++] = 4;
	
	//byte8----------------------判断是否存在用户名,密码等标志-----------------------------
    mqttPacket->_data[mqttPacket->_len++] = flags;
	
	//byte9,byte10---------------保活时间----------------------------------------
	//#define MOSQ_MSB(A)         (uint8)((A & 0xFF00) >> 8)
	//#define MOSQ_LSB(A)         (uint8)(A & 0x00FF)

	mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(cTime);
	mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(cTime);
	 
/*************************************有效载荷*****************/

	//有效载荷----------------------------devid长度,devid---------------------------------
	mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(devid_len);//
	mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(devid_len);
	
	strncat((int8 *)mqttPacket->_data + mqttPacket->_len, devid, devid_len);
	mqttPacket->_len += devid_len;
	
	//有效载荷----------------------------will_flag 和 will_msg(默认为 NULL 0)-------------
	//不执行下列代码
	if(flags & MQTT_CONNECT_WILL_FLAG)
	{
		unsigned short mLen = 0;	
		if(!will_msg)				
			will_msg = "";
		
		mLen = strlen(will_topic);
		mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(mLen);
		mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(mLen);
		strncat((int8 *)mqttPacket->_data + mqttPacket->_len, will_topic, mLen);
		mqttPacket->_len += mLen;
		
		mLen = strlen(will_msg);
		mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(mLen);
		mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(mLen);
		strncat((int8 *)mqttPacket->_data + mqttPacket->_len, will_msg, mLen);
		mqttPacket->_len += mLen;
	}
	
	//有效载荷----------------------------use用户名-----------------------------------------
	if(flags & MQTT_CONNECT_USER_NAME)	//判断用户名这一位是否置 1,置1代表存在用户名	
	{
		unsigned short user_len = strlen(user);
		mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(user_len);	
		mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(user_len);
		//使用 strncat 函数添加用户名
		strncat((int8 *)mqttPacket->_data + mqttPacket->_len, user, user_len);
		mqttPacket->_len += user_len;
	}

	//有效载荷----------------------------password密码-------------------------------------
	if(flags & MQTT_CONNECT_PASSORD)	//判断密码这一位是否置 1,置1代表存在密码
	{
		unsigned short psw_len = strlen(password);
		mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(psw_len);
		mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(psw_len);
		strncat((int8 *)mqttPacket->_data + mqttPacket->_len, password, psw_len);
		mqttPacket->_len += psw_len;
	}

	return 0;

}

最后

 ​本专栏完结了,最后给大家提供下所有的源代码。有需要自取。

 ​github代码地址

 ​如果可以的话,能帮我 github 上加个星么。

 ​若是该文章对你有作用或是觉得文章写得还行,帮忙点点赞,三连!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一文带你了解 MQTT 协议(连接 ONE-NET平台) 的相关文章

  • GCDAsyncUDPSocket源地址返回null

    谷歌代码问题镜像 https groups google com forum topic cocoaasyncsocket grhjZSMLr3U https groups google com forum topic cocoaasync
  • UDP 数据包在交付时是否保证是完整的、具有实际意义的?

    众所周知 UDP 用户数据报协议 并不安全 因为用它发送的数据包的顺序可能不按顺序传送 甚至根本不按顺序传送 但是 如果发送了 UDP 数据包 该数据包中的信息在实际意义上 99 99 及以上 是否保证正确 在实际意义上 99 99 及以上
  • 什么是 STUN?它是否需要端口转发服务器?

    我对没有基础服务器的 p2p 通信进行了一些研究 并通过了 STUN 据我所知 STUN 是 NAT 打孔 的一种方式 不需要对等方进行端口转发即可连接 这是正确的吗 打孔到底是什么意思 这一切看起来都很脆弱 因为如果不需要端口转发 它就会
  • Spark Scala UDP 在侦听端口上接收

    中提到的例子http spark apache org docs latest streaming programming guide html http spark apache org docs latest streaming pro
  • Java:使用多个 DatagramSocket 接收 UDP 数据报包

    我正在尝试实现一种将 UDP 数据包发送到多个接收者的方法 我认为这应该是可行的设置setReuseAddress true 在接收 DatagramSocket 实例上 我的问题是 在某些情况下 我需要限制与本地计算机的通信 因此限制本地
  • udp数据包被tcpdump捕获,但没有被套接字接收[关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我编写了一个 rawudp 程序 通过原始套接字发送 udp 数据包 按照网页http www tenouk com Module43a html h
  • NodeJS UDP 多播如何

    我正在尝试将 UDP 多播数据包发送到 230 185 192 108 以便每个订阅的人都会收到 有点卡住了 我相信它的广播正确 但似乎无法从任何客户端获取任何信息 Server var news Borussia Dortmund win
  • Java UDP 服务器,并发客户端

    下面的代码足以接受并发 UDP 传输吗 更具体地说 如果 2 个客户端同时传输 当我调用 receive 时 DatagramSocket 会将传输排队并一一传送它们 还是只有一个能够通过 DatagramSocket socket new
  • iOS 14 在进行本地网络广播时给出“操作系统错误:错误的文件描述符,errno = 9”

    做一点Jeopardy 风格问答 https stackoverflow blog 2011 07 01 its ok to ask and answer your own questions here 我正在 Flutter 中开发一个应
  • 什么是消息边界?

    什么是 消息边界 在以下情况下 TCP 和 UDP 之间的区别之一是 UDP 保留消息 边界 我理解之间的区别TCP and UDP 但我不确定的定义 消息边界 由于 UDP 在每个单独的数据包中包含目的地和端口信息 因此是否可以为消息提供
  • 当端点和 PMA 地址均更改时,CubeMX 生成的 USB HID 设备发送错误数据

    我正在调试我正在创建的复合设备的问题 并在新生成的仅 CubeMX 代码中重新创建了该问题 以使其更容易解决 我添加了少量代码main 让我发送 USB HID 鼠标点击 并在按下蓝色按钮时使 LED 闪烁 uint8 t click re
  • 为什么我们可以将 sockaddr 转换为 sockaddr_in

    我明白为什么强制转换很有用sockaddr to sockaddr in 但我不明白这怎么可能 据我所知 它们的大小相同sockaddr in添加了sin zero使其大小相同 我想知道编译器如何知道从哪里获取信息sockaddr in如果
  • 为什么通过UdpClient发送会导致后续接收失败?

    我正在尝试创建一个 UDP 服务器 它可以向所有向其发送消息的客户端发送消息 真实情况要复杂一些 但最简单的方法是将其想象为一个聊天服务器 之前发送过消息的每个人都会收到其他客户端发送的所有消息 所有这一切都是通过UdpClient 在单独
  • 数据包丢失和数据包重复

    我试图找出数据包丢失和数据包重复问题之间的区别 有谁知道 数据包重复 是什么意思 和TCP检测到丢失时重传数据包一样吗 No In TCP 数据包 的传递是可靠的 我认为在这种情况下术语数据应该更好 因为它是面向流的协议 数据包丢失和重复是
  • 接收UDP数据包

    假设我的程序通过网络 UDP 发送 1000 字节 它是否保证接收方将 一批 接收 1000 个字节 或者他可能需要执行多次 读取 直到收到完整的消息 如果后者为真 我如何确保同一消息的数据包顺序不会 混淆 按顺序 或者协议可能保证这一点
  • 使用 STUN 打孔

    我目前正在尝试通过 Internet 发送 UDP 消息 并且必须为端点 A 和 B 都位于 NAT 后面 设置防火墙 为此 我想使用 STUN 服务器进行打孔 当 A 创建对 STUN 服务器的请求 例如 私有 85 1 1 12 600
  • 在 macOS 10.12 上绑定到套接字时出现 NSPOSIXErrorDomain

    我正在玩CocoaAsyncSocket https github com robbiehanson CocoaAsyncSocket在 Swift 中绑定到 UDP 套接字并通过本地网络接收消息 我正在初始化一个套接字 并尝试绑定到一个端
  • recvfrom() 中的 addrlen 字段有何用途?

    我在程序中使用 recvfrom 从我在 src addr 中指定的服务器获取 DGRAM 数据 但是 我不确定为什么需要初始化并传入addrlen 我读了手册页 但不太明白它的意思 如果src addr不为NULL 并且底层协议提供了源地
  • 我应该害怕使用 UDP 进行客户端/服务器广播通话吗?

    我在过去的两天里阅读了每一篇StackOverflow问题和答案 以及googling当然 关于印地TCP and UDP协议 以便决定在我的用户应用程序和 Windows 服务之间的通信方法中应该使用哪一种 从我目前所看到的来看 UDP是
  • 如何将udp发送到udp node.js服务器?

    我对此很陌生 所以我真的不知道我在做什么 但我已经设置了一个 node js udp 服务器 我想从客户端 来自网站 向它发送一个数据包 但我不知道如何在 javascript 中做到这一点 或者是否可能 我不是在研究如何从 Node js

随机推荐

  • springBoot整合kafka配置

    pom xml
  • Java中定义常量(Constant) 的几种方法

    Method One interface ConstantInterface String SUNDAY SUNDAY String MONDAY MONDAY String TUESDAY TUESDAY String WEDNESDAY
  • python 之 使用 for-in 循环遍历范围,元组,列表,字典

    语法格式 for 变量 in 范围 元素 集合等 for循环语法结构列如序列或迭代器作为其参数每次迭代其中一个元素 与while循环一样 支持break continue else语句 一般情况下 循环次数未知采用while循环 循环次数已
  • 硬件安全技术——芯片安全设计技术2

    硬件安全技术 芯片安全设计技术2 芯片安全设计技术2 一 常见的公钥密码算法 1 公钥密码概述 2 RSA密码算法 3 RSA算法描述 4 ECC椭圆曲线算法 二 常见的使用场景 1 非对称算法应用 2 常见应用1 网络认证 3 常见应用2
  • 关于oracle和mysql等服务不能开机自启的解决方法

    oracle服务设置自启动 开始菜单点重启服务是可以正常启动的 但点关机后再通过开关机键启动windows 服务无法正常启动 尝试了更改注册表 查看系统日志 修复ntdll dll文件 更换dll文件修改权限 重装系统等方式后 都无法解决
  • 【C语言初阶】 数组

    博客主页 小王又困了 系列专栏 C语言 人之为学 不日近则日退 感谢大家点赞 收藏 评论 目录 一 认识数组 二 一维数组的创建和初始化 1 数组的创建 2 数组的初始化 字符的初始化 3 数组的引用 4 数组在内存的存储 三 二维数组的创
  • 【Qt】QModbusDevice类

    1 概述 QModbusDevice类是Modbus类 QModbusServer和 QModbusClient的基类 Header include qmake QT serialbus Since Qt 5 8 Inherits QObj
  • 献给童鞋们python教程:Collections模块的Counter容器类使用

    本文来源于公众号 csdn2299 喜欢可以关注公众号 程序员学府 文章目录 1 collections模块 2 Counter类 2 1 创建 2 2 计数值的访问与缺失的键 2 3 计数器的更新 update和subtract 2 4
  • 爬取植物数据库:使用 Python 获取植物信息

    在这篇博客中 我们将学习如何使用 Python 编写一个网络爬虫 从植物数据库网站中获取植物信息 我们将使用 requests BeautifulSoup 和 pandas 库来实现这个功能 文章将包括以下内容 目录 1 爬虫的基本概念 2
  • elasticsearch基本查询(此处为2.x版本)

    public class JavaESQuery private TransportClient client Before public void testBefore Settings settings Settings setting
  • Canal解决Mysql和Redis数据同步问题

    目录 前言 一 Mysql主从工作原理 主从复制步骤 二 使用方法 1 软件下载 软件需求 所有安装包 我的资源都有 2 修改配置 1 数据库配置修改 2 canal配置修改 3 RocketMQ配置 4 RocketMQ可视化工具配置 3
  • iPad越狱是什么?iPad越狱有什么好处和坏处

    1 iPad越狱是什么 iPad越狱有什么好处和坏处 不越狱又有啥缺点 越狱就是解除一些原版固件的限制 最大的好处是可以安装破解的软件和游戏 这些软件和游戏本来都是收费的 而且 有些功能很强大的软件 并不是花钱能在官方的App Store里
  • docker方式部署的mysql 8.0远程连接1045问题解决

    项目场景 docker方式部署 mysql8 0 远程连接 报 1045 问题描述 原因分析 在docker 部署创建mysql容器的时候 配置用户权限 mysql user表中host未配置权限 解决方案 1 找到mysql容器 dock
  • MATLAB中自带的核密度估计函数

    我们在统计数据处理时 经常计算一个样本的概率密度估计 也就是说给出一组统计数据 要求你绘制出它的概率分布曲线 matlab的统计工具箱中有直接的函数 就是 Ksdensity 核心平滑密度估计 f xi ksdensity x 计算样本向量
  • MySQL JDBC驱动版本与MySQL数据库版本对应关系

    前言 前段时间发现在家使用和公司一样的mysql jdbc驱动版本发生了异常 原因 家里mysql数据库版本与公司不一致导致 查询了相关资料 发现mysql jdbc驱动版本与mysql数据库版本有一定的对应关系 用错了版本就会出现连接不上
  • Python3.7.5一键安装脚本

    python3 7 5一键安装脚本install python3 7 5 sh 适用于linuix环境 内容如下 which python3 7 if 0 then echo python3 7 exists return 0 fi f P
  • 学习笔记☞ MySQL(二)

    1 字符类型的宽度和数值类型的宽度区别 1 数值类型的宽的仅仅为显示宽度 只用于select查询显示 和占用的存储空间大小无关 可用zerofill查看效果 2 字符类型的宽度超过则无法存储 2 where 条件子句 配合查询 修改 删除操
  • ECDH secp256k1 集成

    在Android 原生api是不支持secp256k1算法的 所以要先集成以下库 implementation com madgag spongycastle core 1 58 0 0 compile com madgag spongyc
  • JavaScript学习总结【12】、JS AJAX应用

    1 AJAX 简介 AJAX 音译为 阿贾克斯 Asynchronous JavaScript and XML 异步的 JavaScript 和 XML 是指一种创建交互式网页应用的网页开发技术 也就是在无需重新加载整个网页的情况下 能够更
  • 一文带你了解 MQTT 协议(连接 ONE-NET平台)

    MQTT 协议连接 ONE NET 详解 写在前面 本文采用 网络调试助手 发送MQTT协议报文 16进制 连接 ONE NET 平台 采用的 为 MQTT v3 1 1 标准协议 带你直接 学会 MQTT 协议 一 ONE NET 端创建