详细聊聊paho-mqtt 模块

# 安装paho-mqtt包
pip3 install paho-mqtt

paho-mqtt主要由三个模块组成:Client模块、Publish模块和Subscribe模块。Publish模块和Subscribe模块使用相对较少。

Client的基本使用流程

Client的基本使用流程如下:

创建客户端实例
使用 connect*() 函数之一连接到代理
调用 loop*() 函数之一来维护与代理的网络流量
使用 subscribe() 订阅主题并接收消息
使用 publish() 将消息发布到代理
使用 disconnect() 断开与代理的连接

Client的构建与重置

# 构建
Client(client_id="", clean_session=True, userdata=None, protocol=MQTTv311, transport="tcp")
# 重置
reinitialise(client_id="", clean_session=True, userdata=None)

参数说明
client_id:
连接到代理时使用的唯一客户端 ID 字符串。如果 client_id 为零长度或 None ,则将随机生成一个。在这种情况下,clean_session 参数必须为 True。

clean_session:
确定客户端类型的布尔值。如果为 True,代理将在断开连接时删除有关此客户端的所有信息。如果为 False,则客户端是持久客户端,并且在客户端断开连接时将保留订阅信息和排队消息。

注意,客户端永远不会在断开连接时丢弃自己的传出消息。调用 connect() 或 reconnect() 将导致消息被重新发送。使用 reinitialise() 将客户端重置为其原始状态。

userdata:
作为 userdata 参数传递给回调的任何类型的用户定义数据。稍后可能会使用 user_data_set() 函数对其进行更新。

protocol:
用于此客户端的 MQTT 协议版本。可以是 MQTTv31 或 MQTTv311

transport:
设置为“websockets”以通过 WebSockets 发送 MQTT。保留默认值“tcp”以使用原始 TCP。

# 构建一个Client
mqttc = mqtt.Client()
# 重置一个Client
mqttc.reinitialise()

# 连接 MQTT
mqttc.connect(host=IP, port=1883, keepalive=60, bind_address="")
# 重连
mqttc.reconnect()
# 断开连接
mqttc.disconnect()

PS: MQTT的本质是一个用以维护客户端与代理之间的长连接的、并且是低消耗的协议。所以,无论对于代理还是对于客户端,他们都需要清楚地知道二者之间的连接是否断开、而且是正常断开还是非正常断开(正常断开(客户端使用disconnect方法)则不需特别操作;非正常断开情况下,客户端需要尝试重新连接,而代理则需要发送遗嘱)。这一点贯穿与MQTT的协议设计与“连接”这一部分的内容。

参数说明
host:
远程代理的主机名或 IP 地址
port:
要连接的服务器主机的网络端口。 默认为 1883。请注意,基于 SSL/TLS 的 MQTT 的默认端口为 8883,因此如果您使用 tls_set() 或 tls_set_context(),则可能需要手动提供端口
keepalive:
与代理通信之间允许的最长间隔(以秒为单位)。 如果没有其他消息正在交换,这将控制客户端向代理发送 ping 消息的速率。

需要指出,MQTT协议规定,在 1.5倍的keepalive时间内,如果代理没有收到来自客户端的任何数据包,那么代理将认为它和这个客户端之间的连接已经断开;而如果客户端没有收到来自 代理的任何数据包,那么这个客户端会认为它和代理之间的连接已经断开。为维持正常的连接,如果代理与客户端之间没有其他数据传输,客户端会每隔keepalive时间向代理发送一次ping消息(由loop()来维护)。keepalive的缺省时间是60s。

bind_address:
假设存在多个接口,要将此客户端绑定到的本地网络接口的 IP 地址

网络回路控制

网络回路控制是客户端对传入与发出数据进行控制的背后驱动力,同时也根据客户端的keepalive设置发送ping消息(心跳报文),以刷新连接。如果不调用网络回路方法,则客户端不会处理传入的网络数据,并且可能无法及时发送传出的网络数据。其作用可由下图表示:
在这里插入图片描述

# 相关方法
运行这几个loop 方法后,回调函数就会被执行
loop(timeout=1.0, max_packets=1)

loop_start()
loop_stop(force=False)

loop_forever(timeout=1.0, max_packets=1, retry_first_connection=False)

loop_forever

# loop_forever()
loop_forever()持续阻塞进程(不需外部循环),连接将维持到客户端调用disconnect()。timeout 和 max_packets 参数已过时,应保持未设置。retry_first_connection=True 使其重试第一次连接。警告:这可能会导致客户端不断连接到不存在的主机而不提示失败的情况。由于主线程被阻塞,其他操作无法在主线程执行,包括disconnect()在内的其他操作都需要通过回调函数来执行。
class MQTT(object):
    mqtt_broker = '10.0.0.113'
    mqtt_port = 1883


    # sub_topic_status = "agv/publicMsg/status/#"  # 该主题可监听车的状态,如急停
    sub_topic_task_status = "agv/publicMsg/status/#"
    # client_id = f'python-mqtt-{random.randint(0, 1000)}'

    down_message = {'start': (None, None), 'middle': (None, None), 'end': (None, None)}


    def __init__(self):
        self.run_sub()

    def connect_mqtt_sub(self):
        def on_connect(client, userdata, flags, rc):
            if rc == 0:
                print("Connected to MQTT Broker sub!")  # 可用logger替换
            else:
                print("Failed to connect, return code %d\n", rc)  # 可用logger替换

        client = mqtt_client.Client()
        client.on_connect = on_connect
        client.connect(self.mqtt_broker, self.mqtt_port)
        return client

    # 7、监听mqtt的消息,将到达不同工位点的agv信息存入到相对应的队列中
    def sub(self, client: mqtt_client):
        '''同一个主题调用一次'''

        client.subscribe(self.sub_topic_task_status)
        client.message_callback_add(self.sub_topic_task_status, self.handle_down_line)


    def handle_down_line(self,  mosq, obj, msg):
        receive_data = msg.payload.decode()  # 接收sub传来的信息
        action_data = json.loads(receive_data)  # 转换成json
        print('here', action_data)


    def run_sub(self):
        client = self.connect_mqtt_sub()
        self.sub(client)
        client.loop_forever()  # 这是网络循环的阻塞形式,直到客户端调用disconnect()时才会返回。它会自动处理重新连接。
     

loop()

# loop() 方法
一是循环调用loop()阻塞进程:timeout参数定义了loop()阻塞进程的超时时间,
而max_packets 参数已过时,应保持未设置。timeout 不得超过客户端的 keepalive 值,
否则客户端将被代理定期断开连接。
class MQTT(object):
    mqtt_broker = '10.0.0.113'
    mqtt_port = 1883


    # sub_topic_status = "agv/publicMsg/status/#"  # 该主题可监听车的状态,如急停
    sub_topic_task_status = "agv/publicMsg/status/#"
    # client_id = f'python-mqtt-{random.randint(0, 1000)}'

    down_message = {'start': (None, None), 'middle': (None, None), 'end': (None, None)}


    def __init__(self):
        self.run_sub()

    def connect_mqtt_sub(self):
        def on_connect(client, userdata, flags, rc):
            if rc == 0:
                print("Connected to MQTT Broker sub!")  # 可用logger替换
            else:
                print("Failed to connect, return code %d\n", rc)  # 可用logger替换

        client = mqtt_client.Client()
        client.on_connect = on_connect
        client.connect(self.mqtt_broker, self.mqtt_port)
        return client

    # 7、监听mqtt的消息,将到达不同工位点的agv信息存入到相对应的队列中
    def sub(self, client: mqtt_client):
        '''同一个主题调用一次'''

        client.subscribe(self.sub_topic_task_status)
        client.message_callback_add(self.sub_topic_task_status, self.handle_down_line)


    def handle_down_line(self,  mosq, obj, msg):
        receive_data = msg.payload.decode()  # 接收sub传来的信息
        action_data = json.loads(receive_data)  # 转换成json
        print('here', action_data)


    def run_sub(self):
        client = self.connect_mqtt_sub()
        self.sub(client)
        
        while True:
            time.sleep(1)
            client.loop(timeout=1.0)

loop_start() / loop_stop()

# loop_start() / loop_stop()
loop_start()和loop_stop()创建和停止后台线程,以自动调用loop()。这种方式释放了主线程。 loop_start()可以在connect*() 之前或之后调用。 此调用还处理与代理的重新连接。 调用 loop_stop() 停止后台线程。 force 参数当前被忽略。

class MQTT(object):
    mqtt_broker = '10.0.0.113'
    mqtt_port = 1883


    # sub_topic_status = "agv/publicMsg/status/#"  # 该主题可监听车的状态,如急停
    sub_topic_task_status = "agv/publicMsg/status/#"
    # client_id = f'python-mqtt-{random.randint(0, 1000)}'

    down_message = {'start': (None, None), 'middle': (None, None), 'end': (None, None)}


    def __init__(self):
        self.run_sub()

    def connect_mqtt_sub(self):
        def on_connect(client, userdata, flags, rc):
            if rc == 0:
                print("Connected to MQTT Broker sub!")  # 可用logger替换
            else:
                print("Failed to connect, return code %d\n", rc)  # 可用logger替换

        client = mqtt_client.Client()
        client.on_connect = on_connect
        client.connect(self.mqtt_broker, self.mqtt_port)
        return client

    # 7、监听mqtt的消息,将到达不同工位点的agv信息存入到相对应的队列中
    def sub(self, client: mqtt_client):
        '''同一个主题调用一次'''

        client.subscribe(self.sub_topic_task_status)
        client.message_callback_add(self.sub_topic_task_status, self.handle_down_line)


    def handle_down_line(self,  mosq, obj, msg):
        receive_data = msg.payload.decode()  # 接收sub传来的信息
        action_data = json.loads(receive_data)  # 转换成json
        print('here', action_data)


    def run_sub(self):
        client = self.connect_mqtt_sub()
        self.sub(client)
        client.loop_start()  # 开启

        n = 1
        while True:
            time.sleep(1)

            n += 1
            if n >= 5:
                client.loop_stop()  # 关闭

订阅/取消订阅

使客户端订阅到一个或多个主题,或从相应主题退订。

mqttc.subscribe(("my/topic", 1))
mqttc.subscribe([("my/topic", 0), ("another/topic", 2)])
mqttc.unsubscribe("my/topic")
mqttc.unsubscribe(["my/topic", "another/topic"])

发布

发布会使得消息被发送到代理,然后再由代理发送到订阅匹配主题的任何客户端。

mqttc.publish(topic="my/topic", payload=None, qos=0, retain=True)

MQTT中的QoS等级

MQTT设计了一套保证消息稳定传输的机制,包括消息应答、存储和重传。在这套机制下,提供了三种不同层次QoS(Quality of Service):

QoS0,At most once,至多一次;
QoS1,At least once,至少一次;
QoS2,Exactly once,确保只有一次。
QoS 是消息的发送方(Sender)和接受方(Receiver)之间达成的一个协议:

QoS0 代表,Sender 发送的一条消息,Receiver 最多能收到一次,也就是说 Sender 尽力向 Receiver 发送消息,如果发送失败,也就算了;
QoS1 代表,Sender 发送的一条消息,Receiver 至少能收到一次,也就是说 Sender 向 Receiver 发送消息,如果发送失败,会继续重试,直到 Receiver 收到消息为止,但是因为重传的原因,Receiver 有可能会收到重复的消息;
QoS2 代表,Sender 发送的一条消息,Receiver 确保能收到而且只收到一次,也就是说 Sender 尽力向 Receiver 发送消息,如果发送失败,会继续重试,直到 Receiver 收到消息为止,同时保证 Receiver 不会因为消息重传而收到重复的消息。

类的封装

import paho.mqtt.client as mqtt_client


topic = "agv/publicMsg/status/#"
class MqttRoad(object):

    def __init__(self, mqtt_host, mqtt_port, mqtt_keepalive):
        super(MqttRoad, self).__init__()
        client = mqtt_client.Client()
        client.on_connect = self.on_connect
        client.on_message = self.on_message
        client.on_publish = self.on_publish
        client.on_subscribe = self.on_subscribe
        client.connect(mqtt_host, mqtt_port, mqtt_keepalive)  # 600为keepalive的时间间隔

        client.subscribe(topic)

        client.loop_forever()  # 保持连接

    def on_connect(self, client, userdata, flags, rc):
        print("Connected with result code: " + str(rc))
        # 订阅
        client.subscribe("mqtt11")

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

    #   订阅回调
    def on_subscribe(self, client, userdata, mid, granted_qos):
        print("On Subscribed: qos = %d" % granted_qos)
        pass

    #   取消订阅回调
    def on_unsubscribe(self, client, userdata, mid):
        # print("取消订阅")
        print("On unSubscribed: qos = %d" % mid)
        pass

    #   发布消息回调
    def on_publish(self, client, userdata, mid):
        # print("发布消息")
        print("On onPublish: qos = %d" % mid)
        pass

    #   断开链接回调
    def on_disconnect(self, client, userdata, rc):
        # print("断开链接")
        print("Unexpected disconnection rc = " + str(rc))
        pass


if __name__ == '__main__':
    MqttRoad("10.0.0.113", 1883, 600)
    

参考链接

参考链接
以上内容的参考链接
以上内容的参考链接
Q0s说明
参考链接
参考链接
参考链接
参考链接
参考链接
Python paho-mqtt消息队列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值