eclipse / paho.mqtt.python

paho.mqtt.python
Other
2.19k stars 722 forks source link

Subscription could be lost if loop is started before connecting to server. (inconsistiency in docs) #835

Open pktiuk opened 5 months ago

pktiuk commented 5 months ago

Prerequisites

Note: You may remove this section prior to submitting your report.

A small team of volunteers monitors issues. Please help us to help you by making it simple to understand and, if possible, replicate your issue. Prior to reporting a bug please:

Bug Description

According do docs:

It is acceptable to firstly launch event loop before connecting.

Connect to a remote broker. This is a blocking call that establishes the underlying connection and transmits a CONNECT packet. Note that the connection status will not be updated until a CONNACK is received and processed (this requires a running network loop, see loop_start, loop_forever, loop…).

But according to examples (like client_sub ) the order is opposite.

mqttc.on_subscribe = on_subscribe
# Uncomment to enable debug messages
# mqttc.on_log = on_log
mqttc.connect("mqtt.eclipseprojects.io", 1883, 60)
mqttc.subscribe("$SYS/#")

When I tried to start loop before connecting I had problems with connection and with subscription.

Reproduction

  1. Run simple MQTT broker
    docker run -d --name emqx -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 8883:8883 -p 18083:18083  emqx:5.0.20
  2. Run code
    
    import time
    import paho.mqtt.client as mqtt

def on_connect(mqttc, obj, flags, reason_code, properties): print("reason_code: " + str(reason_code))

def on_message(mqttc, obj, msg): print(msg.topic + " " + str(msg.qos) + " " + str(msg.payload))

def on_subscribe(mqttc, obj, mid, reason_code_list, properties): print("Subscribed: " + str(mid) + " " + str(reason_code_list))

mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2) mqttc.on_message = on_message mqttc.on_connect = on_connect mqttc.on_subscribe = on_subscribe mqttc.loop_start() ### LOOP STARTED BEFORE CONNECTION mqttc.connect("localhost", 1883, 60) print("Is connected: ", mqttc.is_connected()) mqttc.subscribe("/#")

while True: time.sleep(1)

3. Send something to on thread `/logs` using tool like MQTTX
4.  Subscription does not work (messages are not printed)

$ python3 ./example.py Is connected: False reason_code: Success


Replacing order in this code
```py
mqttc.connect("localhost", 1883, 60)
mqttc.loop_start() ### LOOP STARTED AFTER CONNECTION

Fixes the issue:

$ python3 ./example.py 
Is connected:  False
reason_code: Success
Subscribed: 1 [ReasonCode(Suback, 'Granted QoS 0')]
/logs 0 b'{\n  "msg": "xxxxxxxxx"\n}'

Environment

Logs

PierreF commented 5 months ago

I reproduce your issue and this should either be fixed in code (preferred) or documented.

I've updated the title, since starting the loop before work and the connection works (cf your reason_code: Success message) but the subscribe() is indeed lost. You should be able to see that subscribe() is lost because it return an error (but no example show error checking).

Regardless of this bug, it might be preferable to subscribe in the on_connect callback to be sure your subscription is kept in case of reconnection (I'm not sure the broker had to persist them, especially when clean_session is True - the default). e.g.

def on_connect(mqttc, obj, flags, reason_code, properties):
    print("reason_code: " + str(reason_code))
    mqttc.subscribe("/#")