wialon / gmqtt

Python MQTT v5.0 async client
MIT License
396 stars 52 forks source link

Handle unexpected errors while reconnecting #119

Closed blaxter closed 3 years ago

blaxter commented 3 years ago

When we reconnect we are doing an explicit disconnect, this can fail and if we don't handle possible exceptions we won't reconnect (even when it's possible to do so)

A way to reproduce this easily is killing the connection with ss -K (linux only)

A standalone script that I used to reproduce this:

import asyncio
import signal
import time

from gmqtt import Client as MQTTClient

import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

STOP = asyncio.Event()

def on_connect(client, flags, rc, properties):
    print('Connected')
    client.subscribe('TEST/#', qos=0)

def on_disconnect(client, packet, exc=None):
    print('Disconnected')

def on_subscribe(client, mid, qos, properties):
    print('SUBSCRIBED')

    client.publish('TEST/TIME', str(time.time()), qos=1)

def on_message(client, topic, payload, qos, properties):
    print('RECV MSG:', payload)

def ask_exit(*args):
    STOP.set()

async def main(broker_host):
    client = MQTTClient("foobar")
    client.set_config({'reconnect_retries': -1, 'reconnect_delay': 0})

    client.on_connect = on_connect
    client.on_message = on_message
    client.on_disconnect = on_disconnect
    client.on_subscribe = on_subscribe

    await client.connect(broker_host)

    await STOP.wait()
    await client.disconnect()

if __name__ == '__main__':
    loop = asyncio.get_event_loop()

    host = 'test.mosquitto.org'

    loop.add_signal_handler(signal.SIGINT, ask_exit)
    loop.add_signal_handler(signal.SIGTERM, ask_exit)

    loop.run_until_complete(main(host))

Execution without this patch:

$ python gmqtt_disconnect_error.py 
Connected
SUBSCRIBED
RECV MSG: b'1605609671.003748'
# To force a network error, I execute in a different shell: sudo ss -K dst `sudo netstat -putan | grep 1883 | grep python | awk '{print $5}'`
[EXC: CONN LOST]
ConnectionAbortedError: [Errno 103] Software caused connection abort
Disconnected
[TRYING WRITE TO CLOSED SOCKET]
[EXC OCCURED] in reconnect future [Errno 103] Software caused connection abort

Execution with this patch:

$ python gmqtt_disconnect_error.py 
Connected
SUBSCRIBED
RECV MSG: b'1605609903.1251647'
# Forced network error
[EXC: CONN LOST]
ConnectionAbortedError: [Errno 103] Software caused connection abort
Disconnected
[TRYING WRITE TO CLOSED SOCKET]
Connected
SUBSCRIBED
RECV MSG: b'1605609905.9536421'

Notice we have successfully reconnected.

Mixser commented 3 years ago

Thanks for PR. We have updated the lib with your fix (v0.6.9).