目录
1.MQTT介绍:
随着 5G 时代的来临,万物物联的伟大构想正在成为现实。联网的物联网设备在 2018 年已经达到了 70 亿, 在未来两年,仅智能水电气表就将超过10亿
海量的设备接入和设备管理对网络带宽、通信协议以及平台服务架构都带来了很大挑战。对于物联网协议来说,必须针对性地解决物联网设备通信的几个关键问题:其网络环境复杂而不可靠、其内存和闪存容量小、其处理器能力有限,MQTT协议应运而生。
MQTT 是基于 Publish/Subscribe 模式的物联网通信协议,凭借简单易实现、支持 QoS、报文小等特点,占据了物联网协议的半壁江山:
1.1MQTT:
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅 (publish/subscribe)模式的"轻量级"通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。MQTT最大 优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽 占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。 MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这 些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网 (IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。
2.MQTT协议主要特性:
(1)开放消息协议,简单易实现。
(2)使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合。
(3)对负载(协议携带的应用数据)内容屏蔽的消息传输。
(4)基于TCP/IP网络连接,提供有序,无损,双向连接。 主流的MQTT是基于TCP连接进行数据推送的。
(5)消息服务质量(QoS)支持,可靠传输保证;有三种消息发布服务质量:
QoS0:"至多一次",消息发布完全依赖底层TCP/IP网络。会发生消息丢失或重复。这一级别可用于如下 情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。这一种方式主要普通APP的 推送,倘若你的智能设备在消息推送时未联网,推送过去没收到,再次联网也就收不到了。
QoS1:"至少一次",确保消息到达,但消息重复可能会发生。
QoS2:"只有一次",确保消息到达一次。在一些要求比较严格的计费系统中,可以使用此级别。在计费 系统中,消息重复或丢失会导致不正确的结果。这种最高质量的消息发布服务还可以用于即时通讯类的APP的 推送,确保用户收到且只会收到一次。
(6)1字节固定报头,2字节心跳报文,最小化传输开销和协议交换,有效减少网络流量。
(7)在线状态感知:使用Last Will和Testament特性通知有关各方客户端异常中断的机制。
3.MQTT协议原理:
3.1MQTT协议实现方式
实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者 (Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。
MQTT传输的消息分为:主题(Topic)和负载(payload)两部分,其中的负载就是所要发送的内容。
MQTT会构建底层网络传输,它将建立客户端到服务端的连接,在两者之间提供一个有序的、基于字节流的双向传输通道。之后会把相关的服务质量(QoS)与主题(Topic)相关联。
3.2.MQTT客户端
一个使用MQTT协议的应用程序或者设备它总是建立到服务端的网络连接,一个MQTT客户端可以进行如下操作:
(1)发布其他客户端可能会订阅的信息;
(2)订阅其它客户端发布的消息;
(3)退订或删除应用程序的消息;
(4)断开与服务器连接。
3.3MQTT服务端
MQTT服务端也就是服务器也称为"消息代理"(Broker),可以是一个应用程序或者是一台设备,它位于消息的发布者跟订阅者之间,它可以进行如下操作:
(1)接受来自客户的网络连接;
(2)接受客户发布的应用信息;
(3)处理来自客户端的订阅和退订请求;
(4)向订阅的客户转发应用程序消息。
3.4发布/订阅、主题
MQTT 是基于 发布(Publish)/订阅(Subscribe) 模式来进行通信及数据交换的,与 HTTP的 请求(Request)/应答(Response) 的模式有本质的不同。
订阅者(Subscriber) 会向 消息服务器(Broker) 订阅一个 主题(Topic) 。成功订阅后,消息服务器会将该主题下的消息转发给所有的订阅者。
主题(Topic)以 ‘/’ 为分隔符区分不同的层级。包含通配符 ‘+’ 或 ‘#’ 的主题又称为 主题过滤器(Topic Filters); 不 含通配符的称为 主题名(Topic Names) 例如:
chat/room/1
sensor/10/temperature
sensor/+/temperature
$SYS/broker/metrics/packets/received
$SYS/broker/metrics/#
+': 表示通配一个层级,例如a/+,匹配a/x, a/y
'#': 表示通配多个层级,例如a/#,匹配a/x, a/b/c/d
注: ‘+’ 通配一个层级,’#’ 通配多个层级(必须在末尾)。
发布者(Publisher)只能向 '主题名' 发布消息,而订阅者(Subscriber) 可以通过订阅 主题过滤器(Topic Filters) 来订阅多个主题。
3.5MQTT协议中的动作
MQTT中定义的一些动作来表示对确定的资源或进程所进行的操作:
(1)CONNECT:客户端连接到服务器
(2)CONNACK:连接确认
(3)PUBLISH:发布消息
(4)PUBACK:发布确认
(5)PUBREC:发布的消息已接收
(6)PUBREL:发布的消息已释放
(7)PUBCOMP:发布完成
(8)SUBSCRIBE:订阅请求
(9)SUBACK:订阅确认
(10)UNSUBSCRIBE:取消订阅
(11)UNSUBACK:取消订阅确认
(12)PINGREQ:客户端发送心跳
(13)PINGRESP:服务端心跳响应
(14)DISCONNECT:断开连接
(15)AUTH:认证
4.MQTT协议数据包
在MQTT协议中,一个MQTT数据包由:固定头(Fixed header)、可变头(Variable header)、消息体 (payload)三部分构成。MQTT数据包结构如下:
固定头:
(1)固定头(Fixed header)。存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识, 如连接,发布,订阅,心跳等。其中固定头是必须的,所有类型的MQTT协议中,都必须包含固定头。
可变头:
(2)可变头(Variable header)。存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其 具体内容。可变头的内容可能包括主题名称、消息ID等。
消息体:
(3)消息体(Payload)。存在于部分MQTT数据包中,表示客户端收到的具体内容。 与可变头一样, 在有些协议类型中有消息内容,有些协议类型中没有消息内容。消息体的具体格式和内容由消息类型和应用程序决定。
MQTT通信过程与QoS:
QoS 0:
第一步:Publisher 将消息PUBLISH到Broker中去,发出去后自己将消息删除。
第二步:Broker接收到消息之后直接发往Subscriber,他们三者之间是不存在确认关系的,没有确认机制,消息收不收的到无所谓。完成一次服务的通信。
Qos 1:
第一步:Publisher 将消息 Store(存储)一份在自己这里,然后PUBLISH到Broker中去。
第二步:Broker存储一份消息之后就将信息PUBLISH到Subscriber中,然后给Publisher一个PUBACK的确认,确认已经发布到Subscrliber中,Publisher才将自己的信息删除。
第三步:Subscrliber收到信息之后,会给Broker发送一个PUBACK确认,确认信息已收到,Broker拿到确认之后删除自身的信息。完成一次服务的通信。
Qos 2:
第一步:Publisher先存储一份消息,再将消息PUBLISH到Broker中。
第二步:Broker得到消息之后先存一份消息,然后PUBREC告诉Publisher我已经收到消息,Publisher接收到请求之后给Broker回一个PUBREL可以发送信息到Subscrliber了,然后Broker就把信息PUBLISH到Subscrliber上,接着就给Publisher回一个PUBCOMP表示消息已发送完成,Publisher接收到这个请求之后就将自身存储的信息删除。
第三步:Subscrliber接收到信息之后先Store存储信息,存储完成之后向Broker发送一个PUBREC表示消息已经收到,Broker接收到请求之后回应一个PUBREL表示消息可以释放,也就是说Subscrliber可以用此消息去处理业务请求了,Subscrliber将消息Notify(通知)上层应用进行业务处理,业务处理完成Subscrliber会给Broker回复一个PUBCOMP表示业务处理已经完成,Broker就可以把信息删除,接着Subscrliber也把自身信息删除,完成一次服务的通信。
EMQX:
EMQX概述:
EMQX 是一款开源的大规模分布式 MQTT 消息服务器,功能丰富,专为物联网和实时通信应用而设计。EMQX 5.0 单集群支持 MQTT 并发连接数高达 1 亿条,单服务器的传输与处理吞吐量可达每秒百万级 MQTT 消息,同时保证毫秒级的低时延。
EMQX支持多种协议,包括MQTT、HTTP、QUIC、WebSocket等,采用无主分布式的结构确保高可用性和水平扩展性,提供友好的用户体验和出色的可观测性。
EMQX特点:
EMQX 5.0 单集群可支持 MQTT 并发连接数高达 1 亿条。
单服务器的传输与处理吞吐量可达每秒百万级 MQTT 消息。
近乎实时的信息传递,保证延迟在亚毫秒级。
100% 符合 MQTT 5.0 和 3.x 标准,具有更好的可扩展性、安全性和可靠性。
通过无主节点分布式架构实现高可用和水平扩展性。
EMQX Dashboard:
1.启动:
安装好docker以及emqx之后使用命令开启docker:
#开启docker
sudo systemctl start docker
#查看所有Docker容器
docker ps -a
复制这个ID
使用命令
#启动容器
docker start 容器ID
之后就是访问18083端口就可以看到Dashboard啦:
用户名:admin 密码: public
2.认证
身份认证是物联网应用的重要组成部分,可以帮助有效阻止非法客户端的连接。EMQX支持多种认证以及双向认证的方式。
认证方式:
内置数据源: | 外部数据库: | 其他: |
Username 认证 | LDAP 认证 | HTTP 认证 |
Cliend ID 认证 | MySQL 认证 | JWT 认证 |
PostgreSQL 认证 | ||
Redis 认证 | ||
MongoDB 认证 |
认证结果:
任何一种认证方式最终都会返回一个结果:
认证成功:经过比对客户端认证成功 ,允许登录客户端
认证失败:经过比对客户端认证失败,数据源中密码与当前密码不一致
匿名认证:
EMQ X 默认配置中启用了匿名认证,任何客户端都能接入 EMQ X。没有启用认证插件或认证插件没有显式允 许/拒绝(ignore)连接请求时,EMQ X 将根据匿名认证启用情况决定是否允许客户端连接。
配置匿名认证:
# etc/emqx.conf
## Value: true | false
#true表示不需要提供用户名以及密码(也就是开启匿名认证)
#false表示需要提供用户名密码(关闭匿名认证)
allow_anonymous = true
由于种类繁多,接下来只讲一种认证方式:
Username 认证:
使用用户名密码作为认证需要先开启插件:
emqx_auth_username
哈希方法:
认证过程中为确保用户名密码太简单而导致的安全隐患,Username认证采用哈希方法进行加密(也就是加盐),在虚拟机中emqx的配置文件可以找到加密的规则以及修改配置:
# etc/plugins/emqx_auth_username.conf
## Value: plain | md5 | sha | sha256
auth.user.password_hash = sha256
预设认证数据:
认证数据可以有两种方式设置,一就是通过修改配置文件预设认证数据,二就是使用HTTP API 管理认证数据。
配置文件预设认证数据:
# etc/plugins/emqx_auth_username.conf
##--------------------------------------------------------------------
## Username Authentication Plugin
##--------------------------------------------------------------------
## Examples:
##auth.user.1.username = admin
##auth.user.1.password = public
##auth.user.2.username = feng@emqtt.io
##auth.user.2.password = public
##auth.user.3.username = name~!@#$%^&*()_+
##auth.user.3.password = pwsswd~!@#$%^&*()_+
## Password hash.
##
## Value: plain | md5 | sha | sha256
auth.user.password_hash = sha256
HTTP API 管理认证数据:
EMQ X提供了对应的HTTP API用以维护内置数据源中的认证信息,我们可以添加/查看/取消/更改认证数据。
使用VSCode来访问EMQ X的API /auth_username 完成认证数据的相关操作:
注意:HTTP API的格式比较严格,注意不要把格式写错,否则会出现报错。
查看已有认证用户数据:
@hostname = 192.168.79.130
@port=18083
@contentType=application/json
@userName=admin
@password=public
#############查看已有用户认证数据##############
GET http://{{hostname}}:{{port}}/api/v4/auth_username HTTP/1.1
Content-Type: {{contentType}}
Authorization: Basic {{userName}}:{{password}}
返回:
HTTP/1.1 200 OK
connection: close
content-length: 20
content-type: application/json
date: Thu, 04 Jun 2020 08:02:39 GMT
server: Cowboy
{
"data": [],
"code": 0
}
添加认证数据API 定义:
########添加用户认证数据##############
POST http://{{hostname}}:{{port}}/api/v4/auth_username HTTP/1.1
Content-Type: {{contentType}}
Authorization: Basic {{userName}}:{{password}}
{
"username": "user",
"password": "123456"
}
code返回 0 即为成功:
HTTP/1.1 200 OK
connection: close
content-length: 10
content-type: application/json
date: Thu, 04 Jun 2020 08:08:09 GMT
server: Cowboy
{
"code": 0
}
更改指定用户名的密码API 定义:
###########更改指定用户名的密码#############
PUT http://{{hostname}}:{{port}}/api/v4/auth_username/user HTTP/1.1
Content-Type: {{contentType}}
Authorization: Basic {{userName}}:{{password}}
{
"password": "user"
}
返回:
HTTP/1.1 200 OK
connection: close
content-length: 10
content-type: application/json
date: Thu, 04 Jun 2020 08:12:58 GMT
server: Cowboy
{
"code": 0
}
查看指定用户名信息API 定义:
###########查看指定用户名信息#############
GET http://{{hostname}}:{{port}}/api/v4/auth_username/user HTTP/1.1
Content-Type: {{contentType}}
Authorization: Basic {{userName}}:{{password}}
返回:
HTTP/1.1 200 OK
connection: close
content-length: 115
content-type: application/json
date: Thu, 04 Jun 2020 08:14:47 GMT
server: Cowboy
{
"data": {
"username": "user",
"password": "687f54edc0779f4e26314d9cc957eba27a078a39997069d3c52c9dda3db4aa07"
},
"code": 0
}
删除认证数据API 定义:
###########删除指定的用户信息#############
DELETE http://{{hostname}}:{{port}}/api/v4/auth_username/user HTTP/1.1
Content-Type: {{contentType}}
Authorization: Basic {{userName}}:{{password}}
返回:
HTTP/1.1 200 OK
connection: close
content-length: 10
content-type: application/json
date: Thu, 04 Jun 2020 08:17:09 GMT
server: Cowboy
{
"code": 0
}
WebSocket客户端验证 :
添加好预设的认证数据之后,使用WebSoket连接客户端,状态已连接表明连接成功(记得先关匿名认证,插件的启动和关闭要重启emqx)
总结:
MQTT适合处理网络环境复杂而不可靠、其内存和闪存容量小、其处理器能力有限的情况,而且具有开放、简单易实现的特性,而且不同的Qos具有不同的应用场景。EMQX为设备之间的通信提供了高效、可靠的解决方案。同时,EMQX对MQTT协议进行了优化和扩展,提供了更多的功能和特性,以满足不同应用场景的需求。
(以上部分资料来自黑马程序员,自用笔记,侵删。)