物联网MQTT协议详解:最全面、最简洁的讲解

一、MQTT的背景和简介

MQTT是一个基于发布/订阅范式的“轻量级”消息协议,它工作在TCP/IP协议族上,并为低带宽、高延迟或不稳定的网络环境提供了可靠的消息传输服务。MQTT的最初设计是为了满足石油和天然气管道的远程监控需求,但随着时间的推移,其应用领域已经扩展到了包括智能家居、工业自动化、智慧城市在内的多个领域。

二、MQTT是什么

MQTT协议全称是(Message Queuing Telemetry Transport),即消息队列遥测传输协议。是一种基于发布/订阅(Publish/Subscribe)模式的轻量级通讯协议,并且该协议构建于TCP/IP协议之上,我们知道TCP协议本身就具有高可靠性的特点,因此基于其上的MQTT协议同样也是具有高可靠、低开销的特点,之所以低开销,是以为MQTT协议传输的最小的报文也只有两个字节。

实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。

三、一些专业术语

网络连接   MQTT 使用的底层传输协议(TCP)基础设施。客户端使用它连接服务端。它提供有序的、可靠的、双向字节流传输。

应用消息   MQTT 协议通过网络传输应用数据。应用消息通过 MQTT 传输时,它们有关联的服务质量(QoS) 和主题(Topic) 。

客户端       使用 MQTT 的程序或设备。客户端总是通过网络连接到服务端。它可以

                    1.发布应用消息给其它相关的客户端。

                    2.订阅以请求接受相关的应用消息。

                    3.取消订阅以移除接受应用消息的请求。

                    4.从服务端断开连接。

服务端       一个程序或设备,作为发送消息的客户端和请求订阅的客户端之间的中介。服务端

                    1.接受来自客户端的网络连接。

                    2.接受客户端发布的应用消息。

                    3.处理客户端的订阅和取消订阅请求。

                    4.转发应用消息给符合条件的已订阅客户端。

订阅

  订阅包含一个主题过滤器(Topic Filter)和一个最大的服务质量(QoS)等级。订阅与单个会话(Session)   关联。会话可以包含多于一个的订阅。会话的每个订阅都有一个不同的主题过滤器。

主题名

附加在应用消息上的一个标签,服务端已知且与订阅匹配。服务端发送应用消息的一个副本给每一个匹配的客户端订阅。

主题过滤器

订阅中包含的一个表达式,用于表示相关的一个或多个主题。主题过滤器可以使用通配符。

会话

客户端和服务端之间的状态交互。一些会话持续时长与网络连接一样,另一些可以在客户端和服务端的多个连续网络连接间扩展。

控制报文

通过网络连接发送的信息数据包。MQTT规范定义了十四种不同类型的控制报文,其中一个(PUBLISH报文) 用于传输应用消息。

四、消息质量

MQTT消息质量有三个等级,QoS 0,QoS 1和 QoS 2。

QoS 0:最多分发一次。消息的传递完全依赖底层的TCP/IP网络,协议里没有定义应答和重试,消息要么只会到达服务端一次,要么根本没有到达。消息可能会丢失。例如,这个等级可用于环境传感器数据,单次的数据丢失没关系,因为不久之后会再次发送。

QoS 1:至少分发一次。服务器的消息接收由PUBACK消息进行确认,如果通信链路或发送设备异常,或者指定时间内没有收到确认消息,发送端会重发这条在消息头中设置了DUP位的消息。(保证交付功能)

QoS 2:只分发一次。这是最高级别的消息传递,消息丢失和重复都是不可接受的,使用这个服务质量等级会有额外的开销。例如,这个等级可用在一个计费系统中,这里如果消息重复或丢失会导致不正确的收费。

eg.目前流行的共享单车智能锁,智能锁可以定时使用QoS level 0质量消息请求服务器,发送单车的当前位置,如果服务器没收到也没关系,反正过一段时间又会再发送一次。之后用户可以通过App查询周围单车位置,找到单车后需要进行解锁,这时候可以使用QoS level 1质量消息,手机App不断的发送解锁消息给单车锁,确保有一次消息能达到以解锁单车。最后用户用完单车后,需要提交付款表单,可以使用QoS level 2质量消息,这样确保只传递一次数据,否则用户就会多付钱了。

五、MQTT的报文结构

MQTT 控制报文的结构   :固定报头+可变报头+有效负荷

固定报头:第一个字节高4位是MQTT控制报文的类型 + 第一个字节低4位用于指定控制报文类型的标志位(只在PUBLISH发布消息时用到) + 剩余长度1~4个字节(剩余长度字段最大4个字节)

可变报头:可变报头的内容根据报文类型的不同而不同。

有效负荷:某些 MQTT 控制报文在报文的最后部分包含一个有效载荷,例如对于 PUBLISH 来说有效载荷就是应用消息。
可能包含有效载荷的控制报文有:CONNECT,PUBLISH,SUBSCRIBE,SUBACK,UNSUBSCRIBE

六、服务端代码

using HslCommunication.MQTT;  //调用胡工提前写好的库

        private MqttServer mqttServer = null;

        public void Start()

        {

            mqttServer = new MqttServer();

            mqttServer.ServerStart(6666);

            mqttServer.ClientVerification += OnConnect;

            mqttServer.OnClientDisConnected += OnClientOffline;

            mqttServer.OnClientConnected += OnClientOnline;

            mqttServer.OnClientApplicationMessageReceive += OnClientReceive;

        }

        /// <summary>

        /// 客户端信息的字符串

        /// </summary>

        /// <param name="session"></param>

        /// <returns></returns>

        private string ClientInfoString(MqttSession session)

        {

            return string.Format("ID:{0}, 用户名:{1} ,Ip:{2}", session.ClientId, session.UserName,      session.EndPoint);

        }

七、客户端代码

using uPLibrary.Networking.M2Mqtt;

       using uPLibrary.Networking.M2Mqtt.Messages;

       private void btnConnect_Click(object sender, EventArgs e)

        {

            try

            {

                if (this.mqttClient != null)

                {

                    List<string> lst = new List<string>();

                    foreach (SubscribeModel model in this.lstSubscribeTopic.Items)

                    {

                        lst.Add(model.Topic);

                    }

                    this.lstSubscribeTopic.Items.Clear();

                    if (lst.Count > 0)

                    {

                        ushort us = this.mqttClient.Unsubscribe(lst.ToArray());

                    }

                    this.mqttClient.Disconnect();

                }

                this.mqttClient = new MqttClient(this.txtServer.Text,6666, secure: false, null, MqttSslProtocols.None);

                this.mqttClient.ConnectionClosed += MqttClient_ConnectionClosed;

                this.mqttClient.MqttMsgPublished += MqttClient_MqttMsgPublished;

                this.mqttClient.MqttMsgPublishReceived += MqttClient_MqttMsgPublishReceived;

                this.mqttClient.MqttMsgSubscribed += MqttClient_MqttMsgSubscribed;

                this.mqttClient.MqttMsgUnsubscribed += MqttClient_MqttMsgUnsubscribed;

                byte success = this.mqttClient.Connect(this.txtClientId.Text);

                if (success != 0 && this.mqttClient.IsConnected)

                {

                    MessageBox.Show("连接失败!");

                    return;

                }

                this.btnConnect.Text = "断开连接";

                this.rtbRecive.AppendText($"内容:连接MQTT服务器成功\r\n");

            }

            catch (Exception ex)

            {

                MessageBox.Show("连接失败!"+ ex.ToString());

            }

        }

作者:道传科技上位机

物联沃分享整理
物联沃-IOTWORD物联网 » 物联网MQTT协议详解:最全面、最简洁的讲解

发表回复