4. QoS 0消息传递
任务目标
使用MQTT客户端将消息的QoS级别设置为0进行发布,并订阅同一主题接收消息。通过对比发送与接收的消息数量,观察在尽力而为(Best Effort)传递模式下可能出现的消息丢失现象,从而理解QoS 0的语义。
环境与依赖
本案例使用Python的paho-mqtt客户端库,安装命令:
pip install paho-mqtt
需要一个可用的MQTT Broker(如Mosquitto),本地默认地址为localhost:1883。
关键完整代码
以下代码实现了一个完整的QoS 0消息传递演示:发布端连续发送20条消息,订阅端接收并计数。为了模拟消息丢失,发布端在发送过程中主动断开连接一次(模拟网络抖动),随后重连继续发送。
import paho.mqtt.client as mqtt
import time
BROKER = "localhost"
PORT = 1883
TOPIC = "qos0/demo"
TOTAL_MSGS = 20
DISCONNECT_AT = 8 # 发送第8条后断开,模拟网络中断
received_count = 0
publish_count = 0
def on_connect(client, userdata, flags, rc, properties=None):
if rc == 0:
client.subscribe(TOPIC, qos=0)
print(f"[SUB] 已连接并订阅 {TOPIC} (QoS 0)")
else:
print(f"[SUB] 连接失败,rc={rc}")
def on_message(client, userdata, msg):
global received_count
received_count += 1
print(f"[SUB] 收到: {msg.payload.decode()} (当前累计: {received_count})")
# 订阅端
sub_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
sub_client.on_connect = on_connect
sub_client.on_message = on_message
sub_client.connect(BROKER, PORT, 60)
sub_client.loop_start()
# 发布端
pub_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
pub_client.connect(BROKER, PORT, 60)
pub_client.loop_start()
time.sleep(1) # 等待订阅建立
print("[PUB] 开始发送消息 (QoS 0)")
for i in range(1, TOTAL_MSGS + 1):
if i == DISCONNECT_AT:
print("[PUB] 模拟网络中断:主动断开连接")
pub_client.disconnect()
pub_client.loop_stop()
time.sleep(2) # 中断期间消息丢失
pub_client.connect(BROKER, PORT, 60)
pub_client.loop_start()
time.sleep(1)
msg = f"msg-{i}"
pub_client.publish(TOPIC, msg, qos=0)
publish_count += 1
print(f"[PUB] 已发送: {msg}")
time.sleep(0.2)
time.sleep(3) # 等待剩余消息到达
sub_client.loop_stop()
pub_client.loop_stop()
sub_client.disconnect()
pub_client.disconnect()
print("\n===== 结果统计 =====")
print(f"发布端发送总数: {publish_count}")
print(f"订阅端接收总数: {received_count}")
print(f"丢失消息数: {publish_count - received_count}")
print("结论: QoS 0 为尽力而为传递,消息可能丢失。")
运行与验证
- 确保MQTT Broker已启动(如
mosquitto -v)。 - 将上述代码保存为
qos0_demo.py并运行:python qos0_demo.py - 观察输出:程序会打印每条消息的发送与接收记录,以及最终统计。
通过判定标准:
- 程序正常结束,无异常报错。
- 输出中
发布端发送总数为20。 - 由于第8条消息发送前主动断开了连接,
订阅端接收总数应小于20(通常为19或更少,取决于断开时机),且丢失消息数大于0。 - 若接收数等于发送数(即未发生丢失),可适当增加
DISCONNECT_AT后的断开时长(如从2秒改为5秒),或增大TOTAL_MSGS并增加一次额外断开,以更明显地观察丢失。
常见问题
- 为什么接收数可能大于0但小于20?:QoS 0不提供确认与重传机制,Broker收到消息后直接转发给当前订阅者。若发布端与Broker之间的连接中断,中断期间发送的消息会丢失;若订阅端与Broker之间的连接中断,则转发失败且不重试。
- 如何让丢失更明显?:增加断开时长,或在断开期间调用
publish()多次(消息在客户端本地即被丢弃,因为无连接可发)。 - QoS 0与QoS 1/2的本质区别?:QoS 0无应答、无存储、无重发,最高吞吐但可能丢失;QoS 1保证至少一次(可能重复),QoS 2保证恰好一次。