MQTT之初体验

初识mqtt

MQTT 是一种基于 发布/订阅(publish/subscribe) 模式的“轻量级”通信协议,该协议创建在TCP/IP协议上。MQTT最大优势在于,能够以极少的代码和有限的宽带为远程链接设备提供实时可靠的消息服务。做为一种低开销、低宽带占用的即时通信协议,使其在物联网、小型设备、移动开发等方面有比较普遍的应用。 MQTT是一个基于客户端-服务器的消息发布/订阅传输协议,MQTT协议中有三种身份:发布者(Publish)代理(Broker)(服务器)订阅者(Subscribe),形式以下:
html

image

设计规范

  • 精简,不添加无关紧要的功能。
  • 发布/订阅(Pub/Sub)模式,方便消息在传感器之间传递。
  • 容许用户动态建立主题,零运维成本。
  • 把输入量下降到最低以提升传输效率。
  • 把低宽带、高延迟、不稳定的网络因素考虑在内。
  • 支持连续的会话控制。
  • 理解客户端计算能力可能很低。
  • 提供服务质量管理
  • 假设数据不可知不强求传输数据的类型与格式保持灵活性。

主要特性

  • 使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合。
  • 对负载内容屏蔽的消息传输
  • 使用tcp/ip提供网络链接
  • 有三种消息发布质量
    • 至多一次:消息发布彻底依赖底层TCP/IP网络。会发生消息丢失或重复
    • 至少一次:确保消息到达,但消息重复可能会发生。
    • 只有一次:确保消息到达一次
  • 小型传输,开销很小(固定长度的头部字节是2字节),协议交换最小化,以下降网络流量。
  • 使用Last WillTestament特性通知有关各方客户端异常中断的机制
    • Last Will:即遗言机制,用于通知同一主题下的去他设备发送遗言的设备已经断开链接。
    • Testament:遗嘱机制,功能相似Last Will

MQTT协议原理

MQTT协议实现方式

实现MQTT协议须要客户端和服务器端通信完成,在通信过程当中MQTT协议中有三种身份:发布者(Publish)代理(Broker)(服务器)订阅者(Subscribe)。其中消息的发布者和订阅者都是客户端,消息的代理是服务器,消息发布者能够同时是订阅者。 MQTT传输的消息分为:java

  • Topic(主题):能够理解为接头暗号,订阅者订阅后(即口号对上后),就会收到该主题的消息内容(payload)
  • payload(负载):能够理解为消息内容,是指订阅者具体须要的内容/

网路传输与应用消息

MQTT会构建镀层网络传输:他将创建客户端到服务器的链接,提供二者之间的一个有序的、无损的、基于字节流的双向传输。当应用数据经过MQTT网络发送时,MQTT会把与之相关的服务质量(Qos)和主题名(Topic)相关联。python

MQTT客户端

一个使用MQTT协议的应用程序或者设备,他老是创建到服务器的网络链接。客户端能够:bash

  • 发布其余客户端可能会订阅的信息
  • 订阅其余客户端信息
  • 退订或删除应用程序的消息
  • 断开与服务器链接

MQTT服务器

MQTT服务器以称为消息代理(Broker),能够是一个程序或一台设备他是位于消息发布者和订阅者之间,它能够:服务器

  • 接受来自客户端的链接
  • 接受客户端发布的应用信息
  • 处理来自客户端的订阅退订请求
  • 向订阅的客户端转发应用程序消息

MQTT协议中的订阅、主题、会话

订阅(Subscription)

订阅包含主题筛选器(Topic Filter)和最大服务质量(Qos)。订阅会与一个会话(Session)关联。一个会话能够包含多个订阅。每一个会话中的每一个订阅都有一个不一样的主题筛选器。网络

会话(Session)

每一个客户端与服务器创建链接后就是一个会话,客户端和服务器之间有状态交互。会话存在与一个网络之间,也可能在客户端和服务器之间跨越多个连续的网络链接。运维

主题名(Topic Name)

链接到一个应用程序消息的标签,该标签与服务器的订阅相匹配。服务器会将消息发送给订阅者所匹配标签的每一个客户端。tcp

主题筛选器(Topic Filter)

一个对主题名通配符筛选器,在订阅表达式中使用,表示订阅所匹配到的多个主题。函数

负载(Payload)

消息订阅者所具体接受的内容。oop

MQTT协议中的方法

MQTT协议中定义了一些方法(也被称为动做),用于来表示对肯定资源所进行操做,这个资源能够表明预先存在的数据或动态生成数据,这取决于服务器的实现。一般来讲,资源指服务器上的文件或输出。主要方法有:

  • Connect:与服务器创建链接,链接成功后有个回调函数(以下是python代码)
# 连接mqtt服务器函数
    def on_mqtt_connect(self):
        self.client.connect(self.MQTTHOST, self.MQTTPORT, 60)
        # 开始监听
        self.client.loop_start() 

# 连接完成后的回调函数
    def on_connect(self, client, userdata, flags, rc):
        logging.info("+++ Connected with result code {} +++".format(str(rc)))
        self.client.subscribe(self.topic_from_base)
复制代码
  • Disconnect: 等待MQTT客户端完成所作的工做,并与服务器断开TCP/IP会话。
  • Subscribe:等待完成订阅。(python代码以下)
# 订阅函数
    def subscribe(self):
        self.client.subscribe(self.topic_from_base, 1)
        # 消息到来处理函数
        self.client.on_message = self.on_message
        
    # 接收到信息后的回调函数, (client:客户端信息,userdata:用户信息,msg:消息体)
    def on_message(self, client, userdata, msg):
        pass
复制代码
  • UnSubscribe:等待服务器取消客户端的一个或多个订阅。
  • Publish:MQTT客户端发送消息请求,发送完成后返回应用程序线程。(python代码以下)
# 数据发送函数
    def publish(self, index, fuc):
        # mqtt发送
        self.client.publish(
            self.topic_to_base, #主题
            to_base_data    # 消息题
        )
复制代码

MQTT协议数据包结构

在MQTT协议中,一个MQTT数据包由:固定头(Fixed header)、可变头(Variable header)、消息题(Payload)三部分组成。MQTT数据包结构以下:

  • 固定头(Fixed header): 存在与全部MQTT数据包中,表示数据包类型及数据包的分组类标识。
  • 可变头(Variable header):存在与部分MQTT数据包中,数据包类型决定了可变头是否存在及其具内容。
  • 消息体(Payload):存在于部分MQTT数据包中,表示客户端收到的具体内容。
MQTT固定头

固定头存在与全部MQTT数据包中,使用两个字节看,共十六位 其结构以下:

image

MQTT数据包类型

位置:Byte 1中的4-7位。

使用四位二进制,可表明十六种类型消息:

image

除去0和15位置属于保留待用,共十四种消息时间类型。

标志位

位置:Byte 1 中的0-3

  • DUP :保证消息可靠传输,默认为0,只占用一个字节,表示第一次发送。不能用于检测消息重复发送等。只适用于客户端或服务器端尝试重发PUBLISH, PUBREL, SUBSCRIBE 或 UNSUBSCRIBE消息,注意须要知足条件:当QoS > 0消息须要回复确认 此时,在可变头部须要包含消息ID,当值为1时,表示当前效益先前已经被传送过。
  • Qos(服务质量):
    image
  • RETAIN:发布保留标识,表示服务器要保留此次推送的信息,若是有新的订阅者出现,就把这消息推送给它,若是设有那么推送至当前订阅者后释放
剩余长度

位置:Byte 2

固定头的第二字节用来保存变长头部和消息体的总大小的,但不是直接保存的。这一字节是能够扩展,其保存机制,前7位用于保存长度,后一部用作标识。当最后一位为1时,表示长度不足,须要使用二个字节继续保存。例如:计算出后面的大小为0

MQTT可变头

MQTT数据包中包含一个可变头,它驻位于固定的头和负载之间。不少类型数据包中都包括一个2字节的数据包标识字段,这些类型的包有:PUBLISH (QoS > 0)、PUBACK、PUBREC、PUBREL、PUBCOMP、SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK。可变头部内容字节长度 + Playload/负荷字节长度 = 剩余长度,这个是须要牢记的可变头的内容因数据包类型而不一样,较常的应用是做为包的标识:

image

消息体(Payloa)

Payload消息体位MQTT数据包的第三部分,包含CONNECT、SUBSCRIBE、SUBACK、UNSUBSCRIBE四种类型的消息:

  • CONNECT:消息体内容主要是:客户端的ClientID、订阅的Topic、Message以及用户名和密码。
  • SUBSCRIBE:消息体内容是一系列的要订阅的主题以及QoS。
  • SUBACK:消息体内容是服务器对于SUBSCRIBE所申请的主题及QoS进行确认和回复。
  • UNSUBSCRIBE:消息体内容是要订阅的主题。

python 简单测试代码

# -*- coding: utf-8 -*-

import paho.mqtt.client as mqtt
import time

class Mqtt(object):
    def __init__(self):
        self.MQTTHOST = "********"
        self.MOTTPORT = "********"
        self.client_id = time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))
        self.client = mqtt.Client(self.client_id)
        self.client.username_pw_set()

        # 设置连接上服务器后回调函数
        self.client.on_connect = self.on_connect

        # 设置接收到服务器消息后回调函数
        self.client.on_message = self.on_message

        # 链接服务器,维持心跳为60秒
        self.client.connect(self.MQTTHOST, self.MOTTPORT, 60)
        self.client.loop_forever()

    def on_connect(self,client, userdata, flag, rc):
        print("Connected with result code " + str(rc))
        client.subscribe("test")

    def on_publish(self, topic, payload, qos):
        self.client.publish(topic, payload, qos)
        print(topic+"消息发送成功。。。。。")
        return 1

    def on_message(self, client, userdata, msg):
        print(msg.topic+ "" +msg.payload.decode("utf-8"))
        return 1

    def on_subscribe(self, topic):
        self.client.subscribe(topic, 1)
        self.client.on_message = self.on_message
        return 1

if __name__ == "__main__":
    mqtt = Mqtt()
    mqtt.on_publish("test", "helloWord",1)
复制代码
参考:

https://www.jianshu.com/p/5c42cb0ed1e9
http://www.runoob.com/w3cnote/mqtt-intro.html
http://www.blogjava.net/yongboy/archive/2014/02/07/409587.html

新手上车,请多指教,若有问题,请邮件联系:young5678@qq.com

相关文章
相关标签/搜索