【python】MQTT通信

一、MQTT协议

MQTT协议几个概念解释一下,对MQTT协议熟悉的请跳过这部分

1、遗言消息

① 一旦连接到MQTT服务器,遗言消息就会被服务器托管,本客户端凡是非正常断开连接 服务器就会将本遗言发送给订阅该遗言消息的客户端,告知对方本客户端离线

② retain=False 新的client订阅是不会收到遗嘱消息的

2、服务质量(QoS)

  0:至多一次,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。

  1:至少一次,确保消息到达,但消息重复可能会发生。

  2:只有一次,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。

3、连接返回代码

0: 连接成功

1: 连接失败 - 不正确的协议版本

2: 连接失败 - 无效的客户端标识符

3: 连接失败 - 服务器不可用

4: 连接失败 - 错误的用户名或密码

4、消息主题

① 大小写敏感

② # 符号是用于匹配主题中任意层级的通配符

5、Client ID可以自定义,但要注意不能重复

6、MQTT只能传输字符串数据

二、下载MQTT服务器端EMQX

1、EMQX下载链接: 免费下载、试用 EMQ 产品

2、选择开源版(当然如果条件允许也可以选择企业版),选择对应系统

【python】MQTT通信_第1张图片

【python】MQTT通信_第2张图片

 3、下载完成,解压gzip文件,进入解压目录,进入命令行,输入

./bin/emqx start

【python】MQTT通信_第3张图片

【python】MQTT通信_第4张图片

三、下载MQTT服务器端MQTTX(也可以不下载)

1、下载链接: MQTT 桌面客户端 - MQTT X | EMQ

2、使用MQTTX客户端:

① 获取IP地址:打开命令行,输入systeminfo

【python】MQTT通信_第5张图片

②打开MQTTX客户端,新建连接

名称不能与已建的连接重名

Client ID是自动填写的,可以不用管

服务器地址填写第一步获取到的IP地址

其他信息暂时不用填,点击右上角的连接,可以看到返回已连接 

【python】MQTT通信_第6张图片

 【python】MQTT通信_第7张图片

四、Python代码

1、安装python MQTT第三方库

pip install paho.mqtt

2、简单的发布和订阅

        2.1 publish_basic.py

# publish_basic.py
import time
import json
import random
from paho.mqtt import client as mqtt_client

broker = '192.168.***.***'  # mqtt代理服务器地址
port = 1883
keepalive = 60     # 与代理通信之间允许的最长时间段(以秒为单位)
topic = "test/send"  # 消息主题
client_id = f'python-mqtt-send-{random.randint(0, 1000)}'  # 客户端id不能重复

def connect_mqtt():
    '''连接mqtt代理服务器'''
    def on_connect(client, userdata, flags, rc):
        '''连接回调函数'''
        if rc == 0:
            print("Connected to MQTT successfully!")

        else:
            print("Failed to connect, return code {0}".format(rc))
    # 连接mqtt代理服务器,并获取连接引用
    client = mqtt_client.Client(client_id)
    client.on_connect = on_connect
    client.connect(broker, port, keepalive)
    return client

def publish(client):
    '''发布消息'''
    while True:
        '''每隔4秒发布一次信息'''
        time.sleep(4)
        # mqtt只能传输字符串数据
        info = {
            'msg1': 'Hello kristina',
            'msg2': 'msg {0}'.format(random.randint(0, 1000))
        }
        msg = json.dumps(info)
        # 默认retain=False,一个Topic只能有一个retained消息,后设置的会覆盖前面的消息
        result = client.publish(topic=topic, payload=msg, qos=0, retain=True)
        # 删除retained消息
        # result = client.publish(topic=topic, payload=None, qos=0, retain=True)
        if result[0] == 0:
            print("Send {0} to topic {1}".format(msg, topic))
        else:
            print("Failed to send message {0} to topic {1}".format(msg, topic))

def run():
    '''运行发布者'''
    client = connect_mqtt()
    # 运行一个线程来自动调用loop()处理网络事件, 非阻塞
    client.loop_start()
    publish(client)

if __name__ == '__main__':
    run()

        2.2 subscribe_basic.py

# subscribe_basic.py
import random
from paho.mqtt import client as mqtt_client
import json
from utils_logger import get_logger

broker = '192.168.***.***'  # mqtt代理服务器地址
port = 1883
keepalive = 60     # 与代理通信之间允许的最长时间段(以秒为单位)
topic = "test/subcribe"  
client_id = f'python-mqtt-subscribe-{random.randint(0, 1000)}'  # 可自定义,但要注意客户端id不能重复

def connect_mqtt():
    '''连接mqtt代理服务器'''
    def on_connect(client, userdata, flags, rc):
        '''连接回调函数'''
        # 响应状态码为0表示连接成功
        if rc == 0:
            print("Connected to MQTT successfully!")
        else:
            print("Failed to connect, return code {0}".format(rc))

    client = mqtt_client.Client(client_id)
    client.on_connect = on_connect
    client.connect(broker, port, keepalive)
    return client

def subscribe(client: mqtt_client):
    '''订阅主题并接收消息'''
    def on_message(client, userdata, msg):
        '''订阅消息回调函数'''
        data = json.loads(msg.payload)  # data = 字典  #payload = json数据
        print("Received message from topic {0} ".format(msg.topic))
        print("The message have {0} information".format(len(data)))
        print("The information is '{0}'".format(data))

    # 订阅指定消息主题
    client.subscribe(topic=topic, qos=0)
    # 将回调函数指派给客户端实例
    client.on_message = on_message

def run():
    # 运行订阅者
    client = connect_mqtt()
    subscribe(client)
    #  运行一个线程来自动调用loop()处理网络事件, 阻塞模式
    client.loop_forever()# 保持 loop()调用


if __name__ == '__main__':
    run()

3、进阶版发布和订阅(添加写入log,遗言主题以及上线下线消息)

        3.1 publish.py

# publish.py
import time
import json
import random
from paho.mqtt import client as mqtt_client
from utils_logger import get_logger


broker = '192.168.***.***'  # mqtt代理服务器地址
port = 1883
keepalive = 60     # 与代理通信之间允许的最长时间段(以秒为单位)
topic = "test/send"  # 消息主题
will_topic = "test/will"  # 遗言消息主题
client_id = f'python-mqtt-send-{random.randint(0, 1000)}'  # 客户端id不能重复
# 遗言消息内容
will_msg = {
            "ID": f"{client_id}",
            "stat": "Offline"
        }
# 上线消息内容
online_msg = {
            "ID": f"{client_id}",
            "stat": "online"
        }
logfolder = 'logs'
logfile = f'mqtt_publish.log'
publish_logger = get_logger(logfolder, logfile)

def connect_mqtt():
    '''连接mqtt代理服务器'''
    def on_connect(client, userdata, flags, rc):
        '''连接回调函数'''
        if rc == 0:
            # 上线后清除retained消息
            result = client.publish(topic=topic, payload=json.dumps(online_msg), qos=0, retain=False)
            publish_logger.info("Connected to MQTT successfully!")
            if result[0] == 0:
                publish_logger.info("Send {0} to topic {1}".format(online_msg, topic))
            else:
                publish_logger.error("Failed to send message {0} to topic {1}".format(online_msg, topic))

        else:
            publish_logger.error("Failed to connect, return code {0}".format(rc))
    # 连接mqtt代理服务器,并获取连接引用
    client = mqtt_client.Client(client_id)
    client.will_set(topic=will_topic, payload=json.dumps(will_msg), qos=0, retain=False)
    client.on_connect = on_connect
    client.connect(broker, port, keepalive)
    return client

def publish(client):
    '''发布消息'''
    while True:
        '''每隔4秒发布一次信息'''
        time.sleep(4)
        # mqtt只能传输字符串数据
        info = {
            'msg1': 'Hello kristina',
            'msg2': 'msg {0}'.format(random.randint(0, 1000))
        }
        msg = json.dumps(info)
        # 默认retain=False,一个Topic只能有一个retained消息,后设置的会覆盖前面的消息
        result = client.publish(topic=topic, payload=msg, qos=0, retain=True)
        # 删除retained消息
        # result = client.publish(topic=topic, payload=None, qos=0, retain=True)
        if result[0] == 0:
            publish_logger.info("Send {0} to topic {1}".format(msg, topic))
        else:
            publish_logger.error("Failed to send message {0} to topic {1}".format(msg, topic))

def run():
    '''运行发布者'''
    client = connect_mqtt()
    # 运行一个线程来自动调用loop()处理网络事件, 非阻塞
    client.loop_start()
    publish(client)

if __name__ == '__main__':
    run()

        3.2 subscribe.py

# subcribe.py
import random
from paho.mqtt import client as mqtt_client
import json
import datetime
from utils_logger import get_logger
# systeminfo
broker = '192.168.***.***'  # mqtt代理服务器地址
port = 1883
keepalive = 60     # 与代理通信之间允许的最长时间段(以秒为单位)
topic = "test/subcribe"  

logfolder = 'logs'
logfile = f'mqtt_subscribe.log'
subcribe_logger = get_logger(logfolder, logfile)
client_id = f'python-mqtt-subscribe-{random.randint(0, 1000)}'  # 可自定义,但要注意客户端id不能重复

def connect_mqtt():
    '''连接mqtt代理服务器'''
    def on_connect(client, userdata, flags, rc):
        '''连接回调函数'''
        # 响应状态码为0表示连接成功
        if rc == 0:
            subcribe_logger.info("Connected to MQTT successfully!")
        else:
            subcribe_logger.error("Failed to connect, return code {0}".format(rc))

    client = mqtt_client.Client(client_id)
    client.on_connect = on_connect
    client.connect(broker, port, keepalive)
    return client

def subscribe(client: mqtt_client):
    '''订阅主题并接收消息'''
    def on_message(client, userdata, msg):
        '''订阅消息回调函数'''
        data = json.loads(msg.payload)  # data = 字典  #payload = json数据
        subcribe_logger.info("Received message from topic {0} ".format(msg.topic)) # msg.payload.decode()
        subcribe_logger.info("The message have {0} information".format(len(data)))
        subcribe_logger.info("The information is '{0}'".format(data))

    # 订阅指定消息主题
    client.subscribe(topic=topic, qos=0)
    # 将回调函数指派给客户端实例
    client.on_message = on_message

def run():
    # 运行订阅者
    client = connect_mqtt()
    subscribe(client)
    #  运行一个线程来自动调用loop()处理网络事件, 阻塞模式
    client.loop_forever() # 保持 loop()调用


if __name__ == '__main__':
    run()

        3.3 效果图

【python】MQTT通信_第8张图片

你可能感兴趣的:(WorkTask,服务器,运维,python)