起因,想仿写个文件传一张照片跟一段参数
上网一查,发现哦豁,更新了,虽然仿写的文件paho-mqtt版本好像没这么近
但是不妨碍我看看怎么个事,顺便学下
paho在v2.0中更新了回调参数,24/10/30文档发布时间蛮近的
Eclipse Paho™ MQTT Python 客户端 — Eclipse paho-mqtt 文档
我完全不会啊,先抄个例程改改参数运行
import time
import paho.mqtt.client as mqtt
# 配置 MQTT 代理信息
broker = 'xxx.xxx.xxx.xxx' # 服务器地址
port = 1883 # 默认 MQTT 端口
topic = 'test' # 消息主题
qos = 1 # 服务质量等级
def on_publish(client, userdata, mid, reason_code, properties):
"""消息发布时执行的回调函数。"""
try:
userdata.remove(mid)
except KeyError:
print("on_publish() 被调用时,mid 不在 unacked_publish 中")
# 潜在竞争条件的解释
print("检查消息确认中的竞争条件。")
# 初始化未确认发布的集合
unacked_publish = set()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_publish = on_publish
# 将未确认发布集合存储在用户数据中
mqttc.user_data_set(unacked_publish)
# 连接到指定的 MQTT 代理
mqttc.connect(broker, port)
mqttc.loop_start()
# 发布消息并跟踪其消息 ID
msg_info1 = mqttc.publish(topic, "my message", qos=qos)
unacked_publish.add(msg_info1.mid)
msg_info2 = mqttc.publish(topic, "my message2", qos=qos)
unacked_publish.add(msg_info2.mid)
# 等待所有消息被发布(被确认)
while len(unacked_publish) > 0:
time.sleep(0.1) # 轮询延迟
# 确保安全等待所有消息的确认
msg_info1.wait_for_publish()
msg_info2.wait_for_publish()
# 清理:断开与代理的连接并停止循环
mqttc.disconnect()
mqttc.loop_stop()
代码说明
- MQTT 配置:确保将
'xxx.xxx.xxx.xxx'
替换为您 MQTT 代理的实际 IP 地址或主机名。 - 服务质量 (QoS):QoS 级别允许您指定消息传递的保证。较高的 QoS 可能导致延迟增加,但能确保更可靠的消息传递。
- 错误处理:错误处理用于管理未确认的消息,并处理使用线程时可能的竞争条件。
- 安全关闭:在发布消息后,脚本等待确认,然后再断开连接,以确保所有消息都已被确认
这个参数我只改了
broker | port | topic | qos |
---|---|---|---|
mqtt服务器地址 | 服务端口,也就是TCP Port,这里是默认的1883 | 主题 | 传输安全性 简单说就是等级越高越安全,相应的过程会更复杂,消耗资源更多。 备注:实际传输最大的qos等级是收信息那方说了算 |
没有main函数,是因为python是顺次从上往下执行的,可自己加。
代码各部分解释:
- 库导入:
import time
import paho.mqtt.client as mqtt
- 导入 `time` 模块以处理时间延迟,以及导入 `paho.mqtt.client` 库以使用 MQTT 客户端功能。
- MQTT 代理信息配置:
broker = 'xxx.xxx.xxx.xxx' # 服务器地址
port = 1883 # 默认 MQTT 端口
topic = 'test' # 消息主题
qos = 1 # 服务质量等级
- 设置 MQTT 代理的地址、端口、消息主题和服务质量等级(QoS)。这些参数用于后续的连接和消息发布。
- 消息发布回调函数:
def on_publish(client, userdata, mid, reason_code, properties):
"""消息发布时执行的回调函数。"""
try:
userdata.remove(mid)
except KeyError:
print("on_publish() 被调用时,mid 不在 unacked_publish 中")
print("检查消息确认中的竞争条件。")
- 定义了一个回调函数 `on_publish`,该函数在消息成功发布后执行。它会从未确认发布的集合中移除消息 ID(mid)。如果移除时发生 `KeyError`,则打印错误信息。
- 初始化未确认发布集合:
unacked_publish = set()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_publish = on_publish
- 创建一个空集合 `unacked_publish` 用于存储未确认的消息 ID。然后初始化 MQTT 客户端,并指定 `on_publish` 回调函数。
- 存储未确认发布集合:
mqttc.user_data_set(unacked_publish)
- 将未确认发布的集合存储在用户数据中,以便在回调函数中访问。
- 连接到 MQTT 代理:
mqttc.connect(broker, port)
mqttc.loop_start()
- 连接到指定的 MQTT 代理并启动网络循环以接收和处理网络事件。
- 发布消息并跟踪消息 ID:
msg_info1 = mqttc.publish(topic, "my message", qos=qos)
unacked_publish.add(msg_info1.mid)
msg_info2 = mqttc.publish(topic, "my message2", qos=qos)
unacked_publish.add(msg_info2.mid)
- 发布两条消息,并将它们的消息 ID 添加到未确认发布集合中。
- 等待消息被确认:
while len(unacked_publish) > 0:
time.sleep(0.1) # 轮询延迟
- 通过轮询方式等待所有未确认消息被确认,期间每 0.1 秒检查一次未确认集合。
- 确保安全等待所有消息确认:
msg_info1.wait_for_publish()
msg_info2.wait_for_publish()
- 确保所有消息已成功发布并得到确认。
- 清理功能:
mqttc.disconnect()
mqttc.loop_stop()
- 断开与 MQTT 代理的连接并停止网络循环,保证资源得到妥善释放。
结构总结:
代码结构分为多个部分,主要包括基础设置、MQTT 功能实现、消息发布和后续处理。整体逻辑如下:
- 基础配置:设置 MQTT 代理信息和回调函数。
- 客户端初始化:创建 MQTT 客户端对象,存储未确认消息的集合,并建立网络连接。
- 消息发布:发布消息并跟踪其状态。
- 确认处理:实现等待机制以确保已发布的消息被正确确认。
- 资源管理:保证在程序结束时正确清理和释放资源。
在我写到这里的时候,突然觉得mqtt好像送报,别人给你递过来报纸,你一看封面,霍新闻,丢到一边。
又给你一张,你一看2块钱(qos),但你最高只要一块钱的,送报的说都送到这了,不可能再拿回去,就只能认可这份报只有一块钱(qos降级)的价值。
简单说,就是不合口味不要,太高级了就给你降级
import paho.mqtt.client as mqtt
# 订阅回调函数
def on_subscribe(client, userdata, mid, reason_code_list, properties):
if reason_code_list[0].is_failure:
print(f"Broker rejected your subscription: {reason_code_list[0]}")
else:
print(f"Broker granted the following QoS: {reason_code_list[0].value}")
# 取消订阅回调函数
def on_unsubscribe(client, userdata, mid, reason_code_list, properties):
if len(reason_code_list) == 0 or not reason_code_list[0].is_failure:
print("Unsubscribe succeeded (success in MQTTv3 if SUBACK is received)")
else:
print(f"Broker replied with failure: {reason_code_list[0]}")
client.disconnect()
# 消息处理回调函数
def on_message(client, userdata, message):
userdata.append(message.payload.decode())
print(f"Message received: {message.payload.decode()}")
# 可以根据需要增加处理逻辑
if len(userdata) >= 10: # 最多处理10条消息
client.unsubscribe(topic)
# 连接回调函数
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Failed to connect: {reason_code}. `loop_forever()` will retry connection")
else:
client.subscribe(topic, qos) # 订阅指定的主题和 QoS
# 设置参数
broker = "xxx.xxx.xxx.xxx"
port = 1883
topic = "test"
qos = 2
# 创建 MQTT 客户端
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_connect = on_connect
mqttc.on_message = on_message
mqttc.on_subscribe = on_subscribe
mqttc.on_unsubscribe = on_unsubscribe
# 设置用户数据
userdata = []
mqttc.user_data_set(userdata)
# 连接到 MQTT 代理
mqttc.connect(broker, port)
mqttc.loop_forever()
# 打印接收到的消息
print(f"Received the following messages: {mqttc.user_data_get()}")
代码说明
- 回调函数:
on_subscribe
: 处理订阅结果。on_unsubscribe
: 处理取消订阅结果并断开连接。on_message
: 处理接收到的消息,最多处理 10 条消息后取消订阅。on_connect
: 在连接成功时自动订阅指定的主题。
- 用户数据:
- 使用
userdata
列表保存接收到的消息。
- 使用
- 连接与循环:
- 使用
mqttc.connect
连接到 MQTT 代理。 - 使用
mqttc.loop_forever()
开始循环等待消息。
- 使用
代码各部分解释:
- 库导入:
import paho.mqtt.client as mqtt
- 导入 `paho.mqtt.client` 库以使用 MQTT 客户端的功能。
- 订阅回调函数:
def on_subscribe(client, userdata, mid, reason_code_list, properties):
if reason_code_list[0].is_failure:
print(f"Broker rejected your subscription: {reason_code_list[0]}")
else:
print(f"Broker granted the following QoS: {reason_code_list[0].value}")
- 该函数在成功或失败订阅主题时被调用。如果订阅失败,则打印错误信息;成功时输出所获得的 QoS。
- 取消订阅回调函数:
def on_unsubscribe(client, userdata, mid, reason_code_list, properties):
if len(reason_code_list) == 0 or not reason_code_list[0].is_failure:
print("Unsubscribe succeeded (success in MQTTv3 if SUBACK is received)")
else:
print(f"Broker replied with failure: {reason_code_list[0]}")
client.disconnect()
- 该函数在取消订阅时被调用。根据 `reason_code_list` 检查取消订阅是否成功,并在成功后关闭与 MQTT 代理的连接。
- 消息处理回调函数:
def on_message(client, userdata, message):
userdata.append(message.payload.decode())
print(f"Message received: {message.payload.decode()}")
if len(userdata) >= 10: # 最多处理10条消息
client.unsubscribe(topic)
- 该函数处理接收到的消息。消息被解码并添加到 `userdata` 列表中。如果接收到的消息达到 10 条,则取消对主题的订阅。
- 连接回调函数:
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Failed to connect: {reason_code}. `loop_forever()` will retry connection")
else:
client.subscribe(topic, qos) # 订阅指定的主题和 QoS
- 在成功连接到 MQTT 代理时调用。失败时输出错误信息;成功时订阅指定的主题和 QoS。
- 设置参数:
broker = "xxx.xxx.xxx.xxx"
port = 1883
topic = "test"
qos = 2
- 设置 MQTT 代理的地址、端口、要订阅的主题和服务质量(QoS)等级。
- 创建 MQTT 客户端:
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_connect = on_connect
mqttc.on_message = on_message
mqttc.on_subscribe = on_subscribe
mqttc.on_unsubscribe = on_unsubscribe
- 初始化 MQTT 客户端并设置各类回调函数以处理连接、消息、订阅和取消订阅事件。
- 设置用户数据:
userdata = []
mqttc.user_data_set(userdata)
- 创建一个空列表 `userdata` 用于存储接收到的消息,然后将其作为用户数据放入 MQTT 客户端。
- 连接到 MQTT 代理:
mqttc.connect(broker, port)
mqttc.loop_forever()
- 建立与 MQTT 代理的连接,并开始无限循环以处理网络事件和回调。
- 打印接收到的消息:
print(f"Received the following messages: {mqttc.user_data_get()}")
- 代码的这个部分应该实际上在 `loop_forever()` 循环外部执行,因此接收到的消息将不会显示在循环结束后。通常它在 `loop_forever()` 中不直接打印,因为该调用是阻塞性的。
结构总结:
代码整体分为以下几个主要部分:
- 基础设置:配置 MQTT 代理的信息、主题、QoS 和用户数据结构。
- 回调函数定义:实现连接、订阅、消息接收和取消订阅的回调函数,用于处理相应的事件。
- MQTT 客户端创建:初始化客户端并设置回调函数,使其能够响应不同的 MQTT 事件。
- 连接与消息处理:连接到 MQTT 代理并开始处理消息;使用无限循环处理接收到的消息。
- 取消订阅和资源管理:在处理到一定数量的消息后自动取消订阅并断开连接。
这种结构支持 MQTT 消息的异步处理,确保消息能够被及时接收和处理,并在符合条件时自动取消订阅,优化了资源的管理和使用。