在前篇文章(http://t.csdnimg.cn/IamPz)中,介绍了发布/订阅架构和MQTT如何据此交换信息,其中的关键概念是:
- 发布/订阅架构触耦了负责发布信息的客户端(发布者)和负责接收信息的客户端(订阅端)。
- MQTT使用主题确定哪个客户端(订阅者)应该接收信息。主题是一个层级性的字符串,可以用来过滤和路由消息。
接下来,将深入MQTT的世界,介绍MQTT代理(broker)和MQTT的连接。
MQTT客户端和MQTT代理(broker)
MQTT中的两个关键概念是客户端和代理(broker)。一个MQTT客户端可以是任何一个可以通过网络连接到MQTT代理(broker)的运行了MQTT库的设备。发布者和订阅者标签是指发布或订阅消息的客户端。另外,MQTT代理(broker),负责接收、过滤消息,并将消息发送给订阅消息的客户端。代理(broker)同样负责客户端认证和授权,并通过持久会话保存所有客户端的会话数据。
什么是MQTT客户端
在IoT场景中,一个MQTT客户端通常代理一个发布者或订阅者。一个发布者客户端发送信息,而订阅者客户端接收信息。但是,一个MQTT客户端可既是发布者又是订阅者。
一个MQTT客户端可以是任意设备,从一个微控制器到一个大的服务器,运行着MQTT库且通过网络连接到MQTT代理(broker)。
一个MQTT库是一个实现了MQTT协议的软件模块或包,为设备或程序提供一个接口和MQTT通信。这些库可以更轻松的将MQTT支持添加到应用程序和设备中,而无需从头开始实现协议。
MQTT客户端库对很多编辑语言和平台都可用,比如安卓、C、C++、C#、Go、iOS、Java、JavaScript等。
MQTT客户端可以是运行用于测试目的的图形界面MQTT客户端的典型计算机。MQTT被设计为在TCP/IP协议之上工作,因此任何使用TCP/IP并实现MQTT协议的设备都是MQTT客户端。MQTT协议的客户端实现简单明了,非常适合小型设备。
什么是MQTT代理(broker)
MQTT代理(broker)是发布/订阅消息传递系统中的中心枢纽,它接收来自发布者的消息并将其分发给订阅者。在管理MQTT客户端之间的通信流和确保消息可靠的分发上,它扮演着重要的角色。
MQTT代理(broker)的功能包括以下方面:
- 处理大量的并发连接:依据实现的不同,代理可以处理数百万个MQTT客户端并发连接。它通过弥合不同设备、网络和软件系统之间的差距来实现它们之间的通信。
- 过滤和路由消息:代理(broker)依赖订阅主题过滤消息和确定哪个客户端接收消息。
- 身份验证和授权:依据客户端提供的凭据,代理(broker)负责身份验证和授权。代理是可扩展的,便于自定义身份验证、授权和集成到后端系统中。除了身份验证和授权,代理可提供其他安全功能,比如消息传输加密和接入控制清单。
- 可扩展性、集成和监控:一个MQTT客户端必须可扩展来处理大量的消息、客户端,能够集成到后端系统,容易监控且具有抗故障能力。为了满足这些要求,MQTT代理(broker)必须使用最先进的事件驱动网络处理,一个开放的扩展系统和标准监视提供程序。代理(broker)也可以提供其他先进的功能来管理、监控MQTT系统,比如消息过滤、消息持久化和实时分析。
另外,一个MQTT代理(broker)支持集群,允许多个代理(实例)一同工作来处理大量的客户端和消息。
上面已经讨论了MQTT代理(broker)和其责任,接下来更进一步的了解MQTT客户端是如何建立和代理(broker)的连接。
MQTT客户端和代理(broker)如何建立连接
MQTT协议的一个关键功能是其在IoT设备之间交换消息的高效和轻量级方法。这种通信的基础是MQTT连接,它使设备能够安全可靠的与MQTT代理(broker)交换数据。在这一部分中,我们会探究MQTT连接建立的过程和涉及到的不同参数。通过理解MQTT连接如何工作,就可以优化你的IoT部署,以获得更好的性能、安全性和可扩展性。
MQTT协议是基于TCP/IP,意味着客户端和代理(broker)必须有TCP/IP栈。
MQTT的连接永远在一个客户端和一个代理(broker)之间,客户端之间决不会直接相连。为了初始化连接,客户端发送“CONNECT”信息到代理(broker),代理会使用“CONNACK”消息和状态代码进行响应。一旦连接建立,代理(broker)会保持这个连接直到客户端发送一条断开连接指令或连接中断。
本部分会探究通过NAT的MQTT连接,以及MQTT客户端如何通过向代理发送CONECT消息来初始化连接。我们会深入了解MQTT CONNECT命令消息的细节,聚集一些关键选项,包括客户端ID(ClientId)、清除会话、用户名/密码、遗嘱消息和保活等。另外,我们会讨论代理对CONNECT的响应消息,即一条CONNACK消息,该消息包含两个数据条目:会话存在标志和连接返回代码。
通过NAT的MQTT连接
在许多情况下,MQTT客户端位于使用网络地址转换(NAT)将专用网络地址(例如192.168.x.x或10.0.x.x)转换为面向公共地址的路由后面,正如前面提到的,MQTT客户端通过发送CONNECT消息到代理(broker)来启动连接。由于代理(broker)有一个公共地址且保持连接打开以启用消息的双向发送和接收(初始化连接后),位于NAT路由器后面的MQTT客户端不会遇到任何困难。
对于那些不知道的人,NAT是一种常见的网络技术,路由器用来允许私有网络内的设备通过一个公共IP连接到互联网。NAT的工作原理是将私有网络上设备的IP地址转换为路由器的公共IP地址,反之亦然。
在MQTT中,位于NAT路由器后面的客户端依然可以和MQTT代理(broker)通信,因为代理(broker)有一个公共地址,能够和客户端通过NAT连接。但是,通过NAT,一些潜在的问题可能出现,比如配置端口转发或打开防火墙端口以允许流量进来到达代理。另外,一些NAT实现可能在并发连接建立上存在限制,会影响MQTT系统的扩展性。
现在我们理解了位于NAT之后的MQTT客户端和MQTT代理(broker)如何建立连接,接下来更进一步连接MQTT CONNECT命令和它的内容。
MQTT客户端如何通过CONNECT消息初始化连接
现在让我们检查下MQTT CONNECT命令消息,也就是客户端发送到代理(broker)来初始化连接的。如果此消息格式不正确,或者打开网络套接字和发送CONNECT消息之间间隔过长,那么代理(broker)将终止连接以阻止可能降低代理速度的恶意客户端。除了MQTT3.1.1规范中指定的其他详细外,正常的MQTT3客户端还会发送以下内容。让我们关注一个关键选项。
虽然MQTT库的用户可能会发现CONNECT消息中的某些信息有用,但某些细节可能与库的实现者更相关。为了对消息中包含的所有信息有一个完整的理解,参照 MQTT 3.1.1 specification.
让我们看下MQTT CONNECT包中的一些元素,比如ClinetId、Clean Session、Username/Password、Will Message、Keep Alive等。
MQTT CONNECT包中的ClientId是什么?
ClientId是一个唯一的标识,用来区分每一个连接到代理(broker)的MQTT客户端、使代理(broker)跟踪客户端的当前状态。为保证唯一,ClientId应特定于每个客户端和代理。MQTT3.1.1允许在代理不需要维护任何状态的情况下使用空的ClientId。但是,这个连接必须将Clean Session标志设置为true,否则代理将拒绝连接。
MQTT CONNECT包中的CleanSession是什么?
CleanSession标记指示客户端是否要与代理(broker)建立持久会话。当CleanSession被设置为false(CleanSession = false)时,代理将存储客户端所有订阅以及订阅QoS级别为1或2的客户端的所有丢失消息。相反,当CleanSessioin被设置为true(CleanSession = true)时,代理(broker)不会为该客户端存储任何信息,并丢弃任何持久会话中的任何先前状态。
MQTT CONNECT包中的Username/Password是什么?
MQTT提供选项包括一个用户名和密码,用来客户端的认证和授权。但是,要注意的是,明文发送这些信息存在风险。为避免这个风险,我们强烈建议使用加密和哈希(比如通过TLS)来保护凭证。我们还建议在传输敏感数据时使用安全传输层。
另外,一些代理(broker)提供SSL证书认证,完全无需用户名和密码凭据。采取这些预防措施可确保你的MQTT通信保持安全并免受潜在的安全威胁。
MQTT CONNECT包中的Will Message是什么?
MQTT的遗嘱功能(LWT)包括遗嘱消息(will message),当一个客户端意外断开时通知其他客户端。客户端可以在CONNECT消息中将此消息指定为MQTT消息和主题。但客户端突然断开时,代理(broker)将代表客户端发送LWT消息。在后续章节中会对此进行详细的介绍。
MQTT CONNECT包中的Keep Alive是什么?
MQTT的keep alive功能允许客户端指定一个以秒为单位的时间间隔,当连接建立时,以此间隔和代理(broker)通信。这些时间间隔决定了代理(broker)和客户端能够通信的最长周期,而不用发送消息。为了保证连接活跃状态,客户端规律发送PING请求,代理(broker)返回一个PING响应。这种方面允许双方确认对方是否依旧可用。后续章节会对此进行详细的介绍。
当一个MQTT3.1.1客户端连接到代理(broker),keep alive时间间隔非常重要。
MQTT代理响应CONNACK消息
当代理(broker)收到CONNECT消息时,它有义务使用CONNACK消息进行响应。
CONNACK消息包含两个数据条目:
- 当前会话标志
- 连接返回状态码
CONNACK消息中的sessionPresent标志是什么?
sessionPresent标志通知客户端上一个会话是否在代理上仍然可用。如果客户端请求了一个clean会话,则该标志将始终为false,表示没有上一个会话。
但是,如果客户端请求恢复前一个会话,那么如果代理(broker)仍存储会话信息,则该标志将为true。该标志帮助客户端确定是否需要重新订阅主题,或者代理(broker)是否具有上一个会话的订阅。
CONNACK消息中的returnCode标志是什么?
returnCode是一个状态码,通知客户端连接尝试是否成功。这个状态码能够指示多种异常,比如不合法的凭据或不支持协议版本:
Return Code | Return Code Response |
---|---|
0 | Connection accepted |
1 | Connection refused, unacceptable protocol version |
2 | Connection refused, identifier rejected |
3 | Connection refused, server unavailable |
4 | Connection refused, bad user name or password |
5 | Connection refused, not authorized |
这些状态码的更多详细解释,参见the MQTT specification.
注意连接的returnCode很重要,因为它可以帮助诊断连接问题。比如,如果returnCode指明认证失败,客户端可以使用正确的凭证重新连接。了解sessionPresent标志和连接returnCode对于成功的MQTT连接至关重要。
MQTT如何保持一个连接
如果你想知道MQTT如何在没有发送消息的情况下如何保持连接,或者如何确定连接何时丢失,不要担心,后续文章会更详细的介绍。
结论
总结下,对于任何有兴趣使用MQTT协议的人来说,理解MQTT客户端和代理(broker)以及连接建立的过程都非常重要。MQTT客户端库使向应用程序和设备添加MQTT支持变得容易,而无需从头实现协议。MQTT代理(broker)负责接收、过滤、发送消息到订阅的客户端,以及处理客户端认证和授权。
有了这些知识,就可以使用MQTT构建可扩展且高效的物联网系统。接下来的文章中,我们会讨论在MQTT中发布、订阅和取消订阅的更多高级内容。