当前位置:首页 » 密码管理 » mqttssl加密

mqttssl加密

发布时间: 2022-12-06 01:35:35

‘壹’ 3. MQTT简要介绍

——

[1.MQTT项目工程](https://github.com/LiamBindle/MQTT-C)

[2.MQTT API说明文档](https://liambindle.ca/MQTT-C/group__api.html)

[3.MQTT协议中文版](https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introction.html)

MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议。它的设计思想是轻巧、开放、简单、规范,易于实现。这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。

MQTT协议通过交换预定义的MQTT控制报文来通信。

报文格式: 固定包头+可变包头+payload。

固定包头: 由两个字节组成

                byte1 高四位表示控制报文的类型,低四位表示控制报文类型的标志位。

                byte2表示剩余长度,包括可变报头和负载的数据。剩余长度不包括用于编码剩余长度字段本身的字节数。

可变包头:

                某些MQTT控制报文包含一个可变报头部分。它在固定报头和负载之间。可变报头的内容根据报文类型的不同而不同。可变报头的报文标(Packet Identifier)字段存在于在多个类型的报文里。

有效载荷:

                对于PUBLISH来说有效载荷就是应用消息。

——1. 网络建立连接后,客户端发送的第一个报文必须是CONNECT报文

——2. 客户端只能发送一次CONNECT 报文,发送两次会当作协议违规处理,并断开连接。

——3. 有效载荷包括:客户端唯一标识符,will主题,will消息,用户名和密码。

——4.可变包头:协议名(protocol name)+协议级别(protocol level)+连接标志(connect flags)+保持连接(keep alive)。

——5. 协议名: MQTT的UTF-8编码的字符串。(MSB+LSB +MQTT 六个字节)

——6. 协议级别:一个字节

——7. 连接标志:一个字节,包含一些用于指定MQTT连接行为的参数。同时还指出有效载荷中的字段是否存在。服务端必须验证CONNECT控制报文的保留标志位(第0位)是否为0,如果不为0必须断开客户端连接。

——8. 清理会话:byte8 的bit1位标志。

        这个二进制位指定了会话状态的处理方式。客户端和服务端可以保存会话状态,以支持跨网络连接的可靠消息传输。这个标志位用于控

       制会话状态的生存时间。

        标志设置为0:服务端必须基于当前会话(使用客户端标识符识别)的状态恢复与客户端的通信。

        标志设置为1:客户端和服务端必须丢弃之前的任何会话并开始一个新的会话。会话仅持续和网络连接同样长的时间。

——9. 遗嘱标志 WILL FLAG: byte8的bit2位标志。

        遗嘱标志(Will Flag)被设置为1,表示如果连接请求被接受了,遗嘱(Will Message)消息必须被存储在服务端并且与这个网络连接关联。之后网络连接关闭时,服务端必须发布这个遗嘱消息,除非服务端收到DISCONNECT报文时删除了这个遗嘱消息。

服务端发送CONNACK报文响应从客户端收到的CONNECT报文。服务端发送给客户端的第一个报文必须是CONNACK。

如果客户端在合理的时间内没有收到服务端的CONNACK报文,客户端应该关闭网络连接。合理 的时间取决于应用的类型和通信基础设施。

CONNACK报文没有有效载荷。

PUBLISH控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。

固定包头:

注意byte1的bit3为重发标志DUP。

如果DUP标志被设置为0,表示这是客户端或服务端第一次请求发送这个PUBLISH报文。如果DUP标志被设置为1,表示这可能是一个早前报文请求的重发。

服务端发送PUBLISH报文给订阅者时,收到(入站)的PUBLISH报文的DUP标志的值不会被传播。发送(出站)的PUBLISH报文与收到(入站)的PUBLISH报文中的DUP标志是独立设置的,它的值必须单独的根据发送(出站)的PUBLISH报文是否是一个重发来确定。

可变包头:

主题名 :topic name

报文标识符 :packet identitfier。

有效载荷 :有效载荷包含将被发布的应用消息。数据的内容和格式是应用特定的。有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度。包含零长度有效载荷的PUBLISH报文是合法的。

响应:PUBLISH报文的接收者必须按照根据PUBLISH报文中的QoS等级发送响应。

PUBACK报文是对QoS 1等级的PUBLISH报文的响应。

PUBACK报文没有有效载荷。

PUBREC报文是对QoS等级2的PUBLISH报文的响应。它是QoS 2等级协议交换的第二个报文。

PUBREC报文没有有效载荷。

PUBREL报文是对PUBREC报文的响应。它是QoS 2等级协议交换的第三个报文。

PUBREL报文没有有效载荷。

PUBCOMP报文是对PUBREL报文的响应。它是QoS 2等级协议交换的第四个也是最后一个报文。

PUBCOMP报文没有有效载荷。

客户端向服务端发送 SUBSCRIBE 报文用于创建一个或多个订阅。每个订阅注册客户端关心的一个或多个主题。为了将应用消息转发给与那些订阅匹配的主题,服务端发送PUBLISH报文给客户端。SUBSCRIBE报文也(为每个订阅)指定了最大的QoS等级,服务端根据这个发送应用消息给客户端。

有效载荷:

SUBSCRIBE报文的有效载荷包含了一个主题过滤器列表,它们表示客户端想要订阅的主题。SUBSCRIBE报文的有效载荷必须包含至少一对主题过滤器 和 QoS等级字段组合。没有有效载荷的SUBSCRIBE报文是违反协议的。

响应:

服务端收到客户端发送的一个SUBSCRIBE报文时,必须使用SUBACK报文响应,SUBACK报文必须和等待确认的SUBSCRIBE报文有相同的报文标识符。

服务端发送SUBACK报文给客户端,用于确认它已收到并且正在处理SUBSCRIBE报文。SUBACK报文包含一个返回码清单,它们指定了SUBSCRIBE请求的每个订阅被授予的最大QoS等级。

有效载荷:

有效载荷包含一个返回码清单。每个返回码对应等待确认的SUBSCRIBE报文中的一个主题过滤器。返回码的顺序必须和SUBSCRIBE报文中主题过滤器的顺序相同。

客户端发送UNSUBSCRIBE报文给服务端,用于取消订阅主题。

有效载荷 :

UNSUBSCRIBE报文的有效载荷包含客户端想要取消订阅的主题过滤器列表。

UNSUBSCRIBE报文中的主题过滤器必须是连续打包的、按照定义的UTF-8编码字符串 

UNSUBSCRIBE报文的有效载荷必须至少包含一个消息过滤器。没有有效载荷的UNSUBSCRIBE报文是违反协议的。

响应:

UNSUBSCRIBE报文提供的主题过滤器(无论是否包含通配符)必须与服务端持有的这个客 户端的当前主题过滤器集合逐个字符比较。如果有任何过滤器完全匹配,那么它(服务端)自己的订阅将被删除,否则不会有进一步的处理。

如果服务端删除了一个订阅:

——它必须停止分发任何新消息给这个客户端 []。

——它必须完成分发任何已经开始往客户端发送的QoS 1和QoS 2的消息 []。

——它可以继续发送任何现存的准备分发给客户端的缓存消息。

            服务端必须发送UNSUBACK报文响应客户端的UNSUBSCRIBE请求。UNSUBACK报文必须包含和UNSUBSCRIBE报文相同的报文标识符 。即使没有删除任何主题订阅,服务端也必须发送一个UNSUBACK响应 。

            如果服务端收到包含多个主题过滤器的UNSUBSCRIBE报文,它必须如同收到了一系列的多个UNSUBSCRIBE报文一样处理那个报文,除了将它们的响应合并到一个单独的UNSUBACK报文外。 

服务端发送UNSUBACK报文给客户端用于确认收到UNSUBSCRIBE报文。

UNSUBACK报文没有有效载荷。

客户端发送PINGREQ报文给服务端的。用于:

1. 在没有任何其它控制报文从客户端发给服务的时,告知服务端客户端还活着。

2. 请求服务端发送 响应确认它还活着。

3. 使用网络以确认网络连接没有断开。

保持连接(Keep Alive)处理中用到这个报文。

——PINGREQ报文没有可变报头。

——PINGREQ报文没有有效载荷。

响应:

服务端必须发送 PINGRESP报文响应客户端的PINGREQ报文。

服务端发送PINGRESP报文响应客户端的PINGREQ报文。表示服务端还活着。

保持连接(Keep Alive)处理中用到这个报文。

PINGRESP报文没有可变报头。

PINGRESP报文没有有效载荷。

DISCONNECT报文是客户端发给服务端的最后一个控制报文。表示客户端正常断开连接。

DISCONNECT报文没有可变报头。

DISCONNECT报文没有有效载荷。

响应:

客户端发送DISCONNECT报文之后:

—— 必须关闭网络连接 [MQTT-3.14.4-1]。

——不能通过那个网络连接再发送任何控制报文。

服务端在收到DISCONNECT报文时:

——必须丢弃任何与当前连接关联的未发布的遗嘱消息。

——应该关闭网络连接,如果客户端 还没有这么做。

为了提供服务质量保证,客户端和服务端有必要存储会话状态。在整个会话期间,客户端和服务端都必须存储会话状态 。会话必须至少持续和它的活跃网络连接同样长的时间。服务端的保留消息不是会话状态的组成部分。服务端应该保留那种消息直到客户端删除它。

MQTT协议要求基础传输层能够提供有序的、可靠的、双向传输(从客户端到服务端 和从服务端到客户端)的字节流。

无连接的网络传输协议如UDP是不支持的,因为他们可能会丢失数据包或对数据包重排序。

MQTT按照这里定义的服务质量 (QoS) 等级分发应用消息。分发协议是对称的,在下面的描述中,客户端和服务端既可以是发送者也可以是接收者。分发协议关注的是从单个发送者到单个接收者的应用消息。服务端分发应用消息给多个客户端时,每个客户端独立处理。分发给客户端的出站应用消息和入站应用消息的QoS等级可能是不同的。

qos0:最多分发一次

qos1:至少分发一次

qos2:仅分发一次

客户端设置清理会话(CleanSession)标志为0重连时,客户端和服务端必须使用原始的报文标识符重发任何未确认的PUBLISH报文(如果QoS>0)和PUBREL报文 [MQTT-4.4.0-1]。这是唯一要求客户端或服务端重发消息的情况。

服务端接管入站应用消息的所有权时,它必须将消息添加到订阅匹配的客户端的会话状态。正常情况下,客户端收到发送给它的订阅的消息。客户端也可能收到不是与它的订阅精确匹配的消息。如果服务端自动给客户端分配了一个订阅,可能发生这种情况。正在处理UBSUBSCRIBE请求时也可能收到消息。客户端必须按照可用的服务质量(QoS)规则确认它收到的任何PUBLISH报文,不管它选择是否处理报文包含的应用消息 。

实现本章定义的协议流程时,客户端必须遵循下列规则:

重发任何之前的PUBLISH报文时,必须按原始PUBLISH报文的发送顺序重发(适用于QoS 1和QoS 2消息)[MQTT-4.6.0-1]。

——必须按照对应的PUBLISH报文的顺序发送PUBACK报文(QoS 1消息)。

——必须按照对应的PUBLISH报文的顺序发送PUBREC报文(QoS 2消息。

——必须按照对应的PUBREC报文的顺序发送PUBREL报文(QoS 2消息)。

服务端必须默认认为每个主题都是有序的。它可以提供一个管理功能或其它机制,以允许将一个或多个主题当作是无序的 。

服务端处理发送给有序主题的消息时,必须按照上面的规则将消息分发给每个订阅者。此外,它必须按照从客户端收到的顺序发送PUBLISH报文给消费者(对相同的主题和QoS)。

斜杠(‘/’ U+002F)用于分割主题的每个层级,为主题名提供一个分层结构.

数字标志(‘#’ U+0023)是用于匹配主题中任意层级的通配符。

加号 (‘+’ U+002B) 是只能用于单个主题层级匹配的通配符。

服务端不能将 $ 字符开头的主题名匹配通配符 (#或+) 开头的主题过滤器.

$SYS/ 被广泛用作包含服务器特定信息或控制接口的主题的前缀。

应用不能使用 $ 字符开头的主题。

订阅 “#” 的客户端不会收到任何发布到以 “$” 开头主题的消息。

订阅 “+/monitor/Clients” 的客户端不会收到任何发布到 “$SYS/monitor/Clients” 的消息。

订阅 “$SYS/#” 的客户端会收到发布到以 “$SYS/” 开头主题的消息。

订阅 “$SYS/monitor/+” 的客户端会收到发布到 “$SYS/monitor/Clients” 主题的消息。

如果客户端想同时接受以 “$SYS/” 开头主题的消息和不以 $ 开头主题的消息,它需要同

时订阅 “#” 和 ““$SYS/#”。

除非另有说明,如果服务端或客户端遇到了协议违规的行为,它必须关闭传输这个协议违规控制报文的网络连接.

MQTT方案通常部署在不安全的通信环境中。在这种情况下,协议实现通常需要提供这些机制:

——用户和设备身份认证

——服务端资源访问授权

——MQTT控制报文和内嵌应用数据的完整性校验

——MQTT控制报文和内嵌应用数据的隐私控制

作为传输层协议,MQTT仅关注消息传输,提供合适的安全功能是实现者的责任。使用TLS[RFC5246] 是比较普遍的选择。

广泛采用高级加密标准 [AES] 数据加密标准 [DES] 。

推荐使用为受限的低端设备特别优化过的轻量级加密国际标准 ISO 29192 [ISO29192] 。

如果MQTT在WebSocket [RFC6455] 连接上传输,必须满足下面的条件:

——MQTT控制报文必须使用WebSocket二进制数据帧发送。如果收到任何其它类型的数据帧,接收者必须关闭网络连接 。

——单个WebSocket数据帧可以包含多个或者部分MQTT报文。接收者不能假设MQTT控制报文按WebSocket帧边界对齐 。

——客户端必须将字符串 mqtt 包含在它提供的WebSocket子协议列表里 。

——服务端选择和返回的WebSocket子协议名必须是  。

——用于连接客户端和服务器的WebSocket URI对MQTT协议没有任何影响。

MQTT规范定义了MQTT客户端实现和MQTT服务端实现的一致性要求

MQTT实现可以同时是MQTT客户端和MQTT服务端。接受入站连接和建立到其它服务端的出站连接的服务端必须同时符合MQTT客户端和MQTT服务端的要求 。

为了与任何其它的一致性实现交互操作,一致性实现不能要求使用在本规范之外定义的任何扩展 。

附录:

控制报文类型

byte1:标志位flags:

‘贰’ 如何在 JMeter 中使用 MQTT 插件

JMeter 内置 HTTP/HTTPS、TCP 等支持多种协议,还具备插件扩展机制。

MQTT 协议作为物联网界的主流协议,虽然并非 JMeter 自带的协议类型,但在物联网测试场景中极为普遍。为了支持 MQTT 协议的规模测试,EMQ 映云科技开发了基于 JMeter 的 MQTT 协议开源测试插件: https://github.com/xmeter-net/mqtt-jmeter 。

经过几个版本的迭代,目前 JMeter MQTT 插件的最新版本为 2.0.2,支持连接、消息发布、消息订阅等多种采样器,并可通过组合构建更复杂的测试场景。

本文我们将具体介绍如何在 JMeter 中使用 MQTT 插件。

MQTT 插件的安装方式与其他 JMeter 第三方插件类似。

连接采样器模拟物联网设备,发起 MQTT 连接。

Server name or IP: 指向被测 MQTT 服务器地址

Port number: 以 EMQ X 为例,默认 TCP 连接的端口是 1883, SSL 连接则是 8883。具体的端口请参照服务器的具体配置。

MQTT version : 目前支持 MQTT 3.1及3.1.1版本。

Timeout: 连接超时设置,以秒为单位。

Protocols: 支持TCP、SSL、WS 和 WSS 方式连接 MQTT 服务器。当选择 SSL 或 WSS 加密通道连接时,可以选择单向或者双向认证(Dual)。如果希望进行双向认证,还需要指定相应的客户端证书(p12证书),以及对应的文件保护密码(Secret)。

User authentication: 如果 MQTT 服务器配置了用户认证,需要提供相应的用户名( User name )和密码( Password )。

ClientId: 虚拟用户的标识。如果勾选了“Add random suffix for ClientId”,将会在 ClientId 的基础上给每个虚拟用户再添加一个 uuid 串作为后缀,整个作为虚拟用户标识。

Keep alive(s): 心跳信号发送间隔。例如,300 表示客户端每隔 300 秒向服务器发出 ping 请求,以保持连接活跃。

Connect attempt max: 第一次连接过程中,尝试重连的最大次数。超过该次数则认为连接失败。如果希望一直尝试重连,可以设为 -1。

Reconnect attempt max: 后继连接过程中,尝试重连的最大次数。超过该次数则认为连接失败。如果希望一直尝试重连,可以设为 -1。

Clean session : 如果希望在连接之间保留会话状态,可以将该选项设为 false。如果不希望在新的连接中保留会话状态,则将该项设为true。

消息发布采样器复用连接采样器中建立的 MQTT 连接,向目标 MQTT 服务器发布消息。

QoS Level: 服务质量,取值为 0,1,2,分别代表 MQTT 协议规范里的至多一次(AT_MOST_ONCE),至少一次(AT_LEAST_ONCE),精确一次(EXACTLY_ONCE)

Retained messages : 如果希望使用“保留消息”,可将该选项设为 true,MQTT 服务器端将会存储插件发布的保留消息及其 QoS,并在相应 topic 上发生订阅时,直接将最后一条保留消息投递给订阅端,使得订阅端不必等待即可获取发布端的最新状态值。

Topic name: 发布消息所属的主题。

Add timestamp in payload: 如果勾选,发布的消息体开头会附带当前时间戳,配合消息订阅采样器的 Payload includes timestamp 选项,可以在消息接收端计算消息达到的延时。如果不勾选则只发送实际的消息体。

Payloads Message type: 目前支持三种消息类型

消息发布采样器复用连接采样器中建立的 MQTT 连接,从目标 MQTT 服务器上订阅消息。

QoS Level: 服务质量,含义与消息发布采样器相同。

Topic name(s): 订阅消息所属的主题。支持单个消息订阅采样器订阅多个主题,主题之间用逗号分隔。

Payload includes timestamp: 如果勾选,会从消息体开头处解析发送时间戳,配合消息发布采样器的 Add timestamp in payload 选项,可以用于计算消息的接收延时。如果不勾选则只解析实际的消息体。

Sample on : 采样方式,默认为" specified elapsed time(ms) ",即每隔指定的毫秒时间采样一次。也可以选择" number of received messages ",即每接收到指定的消息数采样一次。

Debug response: 如果勾选,消息内容会打印在 JMeter 的响应结果中。该选项主要用于调试目的,正式运行测试不建议勾选,以免影响测试效率。

断开连接采样器中建立的 MQTT 连接。

本文我们介绍了 JMeter MQTT 插件的各测试组件,在下期文章中我们将针对不同的测试场景详细介绍如何用 MQTT 插件来构建测试脚本

‘叁’ 5-Openwrt MQTT client使用

在mosquitto里面有个client目录,里面就是使用libmosquitto实现的客户端程序,封装成mosquitto_sub和mosquitto_pub命令行。

所以新建一个跟client同一级,自己的client,添加对应的文件

Makefile的内容

main.c的内容

myclient.h的内容

外层的mosquitto/src/Makefile里面添加myclient文件的编译

编译测试一切正常,接下去添加mqtt的内容

mqtt client里面最主要的就是几个回调函数,先调用lib_init,正常后,就这只各个callback,然后在callback里面做逻辑。

各回调函数的内容

逻辑应该也比较直观,当connect成功后,在回调函数里面订阅test1主题的内容,然后发布test2主题的内容。

收到内容就在 myclient_message_callback 回调里面打印处理。

正常情况我们都会让客户端的连接做一些账号密码的设置,避免别人攻击。

将allow_anonymous改成不允许匿名登陆,并制定pwfile。

vim /etc/mosquittoConf/mosquitto.conf

在ubuntu上面使用mosquitto_passwd生成密码

就会在pwfile文件下生成账号和加密的密码root/admin

这是后登陆的时候就需要-u root -P admin进行登陆

mosquitto提供了mosquitto_passwd命令来生成账号密码等,不过这个方式不喜欢,因为没办法定制化自己想要的账号密码加密方式,所以做了一些小改动。

在myclient里面加我们想要的加密方式,然后在mosquitto broker的源码里面添加对应的解密方式即可。

如下,账号为client_name,然后通过rsa和base64生成密码,myclient的试下调用 mosquitto_username_pw_set 函数。

然后在mosquitto broker里面添加解密,位于mosquitto/src/security.c文件的 mosquitto_unpwd_check 函数里面。

另一个加密方式就是SSL认证,给客户端提供相应的证书,和配置协议(mqtt or websockets)一样,在配置文件监听的端口下面可以添加ssl的配置选项,每个port都可以单独配置ssl的证书内同容。

如下:从端口7885连接进来的设备需要下面的证书要求

设备的认证有单向认证和双向认证两种:

单向认证,只需要提供ca证书

双向认证,需要ca,pem,key三个

按步骤一步一步执行,生成证书(里面也可以指定各参数,有效时间):

按上面的步骤可以生成如下文件

在服务器端需要放三个文件

如果是单向认证,客户端只需要一个文件

如果是双向认证,客户端只需要三个文件

查看证书的有效时间

‘肆’ TI CC3200的MQTT怎么使用ssl

MQTT连接建立的代码(SSL方式)

[java] view plain
public static void connect(Driver driver) {
ServerConfig serverConfig = UserMole.Instance.getServerConfig();
MqttConnectOptions conOpt = new MqttConnectOptions();
try {
SSLContext sslContext;
KeyStore ts = KeyStore.getInstance("BKS");
ts.load(context.getResources().openRawResource(R.raw.test_cert),
"123456".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(ts);
TrustManager[] tm = tmf.getTrustManagers();
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tm, null);

SocketFactory factory = sslContext.getSocketFactory();
conOpt.setSocketFactory(factory);
} catch (Exception e) {
e.printStackTrace();
}

[java] view plain
//paho库得
Iterator<Map.Entry> it = Connections
.getInstance(context).getConnections().entrySet().iterator();
while (it.hasNext()) {
MqttClientAndroidService detectClient = it.next().getValue()
.getClient();
try {
detectClient.disconnect();
} catch (MqttException e) {
e.printStackTrace();
}
it.remove();
}

// The basic client information
MqttClientAndroidService client;
client = Connections.getInstance(context).createClient(context,
serverConfig.getUri(), serverConfig.clientId);
Integer qos = Integer.parseInt(context.getResources().getString(
R.string.qos));
Boolean retained = Boolean.parseBoolean(context.getResources()
.getString(R.string.retained));

// connection options
int timeout = Integer.parseInt(context.getResources().getString(
(R.string.timeout)));
int keepalive = Integer.parseInt(context.getResources().getString(
R.string.keepalive));

Connection connection = new Connection(serverConfig.getClientHandle(),
serverConfig.clientId, serverConfig.server,
Integer.parseInt(serverConfig.port), context, client,
serverConfig.ssl);

// connection.registerChangeListener(changeListener);
// connect client

String[] actionArgs = new String[1];
actionArgs[0] = serverConfig.clientId;
connection.changeConnectionStatus(ConnectionStatus.CONNECTING);

boolean cleanSession = false;
conOpt.setCleanSession(cleanSession);
conOpt.setConnectionTimeout(timeout);
conOpt.setKeepAliveInterval(keepalive);
if (!TextUtils.isEmpty(serverConfig.user)) {
conOpt.setUserName(serverConfig.user);
}
if (!TextUtils.isEmpty(serverConfig.pwd)) {
conOpt.setPassword(serverConfig.pwd.toCharArray());
}
// conOpt.setPassword("1111".toCharArray());
final ActionListener callback = new ActionListener(context,
ActionListener.Action.CONNECT, driver.getMqttUtilsCallback(),
serverConfig.getClientHandle(), actionArgs);

boolean doConnect = true;

String message = ActivityConstants.message;
String topic = ActivityConstants.topic;

if ((!TextUtils.isEmpty(message) || !TextUtils.isEmpty(topic))) {
// need to make a message since last will is set
try {
conOpt.setWill(topic, message.getBytes(), qos.intValue(),
retained.booleanValue());
} catch (Exception e) {
e.printStackTrace();
doConnect = false;
callback.onFailure(null, e);
}
}
client.setCallback(new MqttCallbackHandler(context, serverConfig
.getClientHandle(), driver));

connection.addConnectionOptions(conOpt);
Connections.getInstance(context).addConnection(connection);
if (doConnect) {
try {
client.connect(conOpt, null, callback);
} catch (MqttException e) {
Log.e(TAG, "MqttException Occured", e);
}
}
}
发布(publish)代码
[java] view plain
public static void publish(String clientHandle, String topic,
JSONObject jsonObj, int qos) {
MqttClientAndroidService client = Connections.getInstance(context)
.getConnection(clientHandle).getClient();
if (!isConnected(context)) {
try {
client.connect();
} catch (MqttException e) {
e.printStackTrace();
}
Toast.makeText(context, "please try again", Toast.LENGTH_SHORT)
.show();
return;
}
if (topic == null) {
Toast.makeText(context, "can not get other's identity for now",
Toast.LENGTH_SHORT).show();
return;
}
String[] args = new String[2];
if (jsonObj.optLong("time") != 0) {
args[0] = String.valueOf(jsonObj.optLong("time"));
args[1] = topic;
}

Boolean retained = Boolean.parseBoolean(context.getResources()
.getString(R.string.retained));
try {
client.publish(topic, jsonObj.toString().getBytes(), qos, retained,
null, new ActionListener(context, Action.PUBLISH, null,
clientHandle, args));
} catch (MqttSecurityException e) {
Log.e(TAG,
"Failed to publish a messged from the client with the handle "
+ clientHandle, e);
} catch (MqttException e) {
Log.e(TAG,
"Failed to publish a messged from the client with the handle "
+ clientHandle, e);
}
}
订阅(subscribe)代码
[java] view plain
public static void subscribe(MqttUtilsCallback mqttUtilsCallback,
String clientHandle, String topic) {
MqttClientAndroidService client = Connections.getInstance(context)
.getConnection(clientHandle).getClient();
if (client == null || (client != null && !client.isConnected())) {
Toast.makeText(context, "please connect to server first",
Toast.LENGTH_SHORT).show();
return;
}

if (TextUtils.isEmpty(topic)) {
topic = "hello";
}
String[] topics = new String[1];
topics[0] = topic;
try {
client.subscribe(topic, 1, null, new ActionListener(context,
ActionListener.Action.SUBSCRIBE, mqttUtilsCallback,
clientHandle, topics));
} catch (MqttSecurityException e) {
Log.e(TAG, "Failed to subscribe to" + topic
+ " the client with the handle " + clientHandle, e);
} catch (MqttException e) {
Log.e(TAG, "Failed to subscribe to" + topic
+ " the client with the handle " + clientHandle, e);
}

‘伍’ 1-MQTT基础知识

由于物联网的环境是非常特别的,所以MQTT遵循以下设计原则:

MQTT 协议的中心是 MQTT 服务器或代理 (broker) ,支持发布程序和订阅程序进行访问,如下图所示

MQTT拥有14种不同的消息类型:

MQTT是通过主题对消息进行分类的,本质上就是一个UTF-8的字符串,不过可以通过反斜杠表示多个层级关系。主题并不需要创建,直接使用就是了。

主题还可以通过通配符进行过滤。其中,+可以过滤一个层级,而#只能出现在主题最后表示过滤任意级别的层级。

举个例子:

building-b/floor-5:代表B楼5层的设备。
+/floor-5:代表任何一个楼的5层的设备。
building-b/#:代表B楼所有的设备。
注意,MQTT允许使用通配符订阅主题,但是并不允许使用通配符广播。

WILL主题也叫遗嘱消息,是一个特殊的主题。

客户端连接Broker的时候,附带一个will主题和will主题对应的内容。

当客户端与Broker断开连接时,Broker将该WILL主题的内容发送给相关的订阅者的遗嘱消息,这样订阅者就知道该客户端已经离线了。

以下情况下会发送 Will Message:

注:在客户端正常调用 disconnect 方法之后并不会被发送。

为了满足不同的场景,MQTT支持三种不同级别的服务质量(Quality of Service,QoS)为不同场景提供消息可靠性:

级别2所提供的不重不丢很多情况下是最理想的,不过往返多次的确认一定对并发和延迟带来影响。

级别1提供的至少一次语义在日志处理这种场景下是完全OK的,所以像Kafka这类的系统利用这一特点减少确认从而大大提高了并发。

级别0适合鸡肋数据场景,基本就没怎么用了。

客户端在连接的时候可以设置clean session,如果设置成true说明在设备离线后broker不保存,设置成false说明在设备离线后broker保存消息,等上线的时候就发送给他。

客户端在连接Broker的时候,会指定心跳的时候。连接成功之后,客户端就按照这个心跳时间定时发送心跳数据给Broker,如果Broker在1.5T时间内没有收到客户端的心跳数据,则判定改设备已经离线,发送WILL主题广播告诉别人该设备已离线。

MQTT可以使用SSL加密方式传输,设备的认证有单向认证和双向认证两种:

MQTT除了有SSL加密之外,对于连接也有账号密码的授权,只要账号密码正确的才可以连接成功。

MQTT 协议和mosquitto: https://shaocheng.li/posts/2015/08/11/

物联网网关协议比较MQTT 和 Modbus:
https://software.intel.com/pt-br/node/628992?language=es

‘陆’ Paho Mqtt SSL连接时常见异常问题以及解决方案

在使用Mqtt的SSL方式连接时遇到了如下问题:

说明使用SSL连接配置中的TrustManager里的server验证失败,即服务端证书签名时用的host和现在签名的host不是同一个。如果是HTTPS,可以通过重写hostNameVerifyer的方法来解决问题,网上有很多攻略
对于Mqtt连接时遇到这个问题,应该检查是否pom.xml中依赖的版本有冲突。经实际验证,paho的版本使用 较新 版本时会遇到这个问题(无论证书是正确/错误,均优先报no name mathing错误,推测是较低版本的校验机制较弱)因此可以参考下面的搭配,使用较低版本跳过这个错误。因为暂未找到mqtt ssl连接时忽略host验证的方法

对于Mqtt连接时遇到这个问题,应该检查是否现在所使用的证书,和服务器端的证书不是同一个。这一次就栽在这个问题上了

pom.xml文件中的依赖:

‘柒’ android mqtt+ssl

你是不是用的最新版本的?可能是你的网络问题吧,我前段时间下载一直下不动,然后我去网吧下载的,用U盘拷回来解压到原来的安装位置就可以了

热点内容
androidsdk包含 发布:2024-05-04 00:45:54 浏览:206
android拷贝文件 发布:2024-05-04 00:38:28 浏览:775
存储冗余比 发布:2024-05-04 00:12:58 浏览:403
oracle数据库存储原理 发布:2024-05-04 00:10:40 浏览:522
未拆封玩客云3怎么搭建服务器 发布:2024-05-04 00:06:11 浏览:797
彻底删除编译安装的文件 发布:2024-05-04 00:05:33 浏览:55
编程机构数量 发布:2024-05-03 23:49:25 浏览:955
python源码编译安装 发布:2024-05-03 23:48:16 浏览:108
android手机市场 发布:2024-05-03 23:47:04 浏览:499
如何配置vlan并添加端口 发布:2024-05-03 23:37:53 浏览:726