Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unhandled ConnectionRefusedError #58

Open
d21d3q opened this issue Sep 2, 2019 · 9 comments
Open

Unhandled ConnectionRefusedError #58

d21d3q opened this issue Sep 2, 2019 · 9 comments

Comments

@d21d3q
Copy link

d21d3q commented Sep 2, 2019

If you run properties example with host that is not running broker (eg localhost), program will crash

Traceback (most recent call last):
  File "properties.py", line 98, in <module>
    loop.run_until_complete(main('localhost', port, None))
  File "/usr/lib64/python3.7/asyncio/base_events.py", line 579, in run_until_complete
    return future.result()
  File "properties.py", line 51, in main
    await sub_client.connect(broker_host, broker_port)
  File "/home/d21d3q/workspace/gmqtt/venv/lib/python3.7/site-packages/gmqtt-0.4.0-py3.7.egg/gmqtt/client.py", line 144, in connect
  File "/home/d21d3q/workspace/gmqtt/venv/lib/python3.7/site-packages/gmqtt-0.4.0-py3.7.egg/gmqtt/client.py", line 160, in _create_connection
  File "/home/d21d3q/workspace/gmqtt/venv/lib/python3.7/site-packages/gmqtt-0.4.0-py3.7.egg/gmqtt/mqtt/connection.py", line 26, in create_connection
  File "/usr/lib64/python3.7/asyncio/base_events.py", line 954, in create_connection
    raise exceptions[0]
  File "/usr/lib64/python3.7/asyncio/base_events.py", line 941, in create_connection
    await self.sock_connect(sock, address)
  File "/usr/lib64/python3.7/asyncio/selector_events.py", line 464, in sock_connect
    return await fut
  File "/usr/lib64/python3.7/asyncio/selector_events.py", line 494, in _sock_connect_cb
    raise OSError(err, f'Connect call failed {address}')
ConnectionRefusedError: [Errno 111] Connect call failed ('127.0.0.1', 1883)
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<Client._resend_qos_messages() running at /home/d21d3q/workspace/gmqtt/venv/lib/python3.7/site-packages/gmqtt-0.4.0-py3.7.egg/gmqtt/client.py:96> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f238afa0f10>()]>>

Expected behavior would be to try to reconnect normally.

workaround:

    try:
        await pub_client.connect(broker_host, broker_port)
    except ConnectionRefusedError:
        asyncio.ensure_future(pub_client.reconnect(delay=True))
@Lenka42
Copy link
Collaborator

Lenka42 commented Sep 2, 2019

Actually this is expected behavior. Reconnects are good when you client is working, but connection at the moment becomes broken and after some time your client automatically reconnects. If the first connection attempt was unsuccessful, it probably means that configuration is bad, host, port or authorization method should be fixed.

@d21d3q
Copy link
Author

d21d3q commented Sep 2, 2019

Hmmm, my use case is to embed mqtt client in IoT device and let it go into the wild, and report some data.
In such case I would except from program (device), to warn me about bad configuration, but keep retrying to connect without crashing. I am configuring device once (asserting that it is working), and letting him live its own life (like gps trackers :) ).
But there are other factors that might fail and device should not care about them. Imagine that your device reboots during such conditions

  • broker goes offline (crash/update) - it is temporary unavailable, but connect fails due to ConnectionRefusedError
  • network goes down - temporarily, but connect fails due to socket.gaierror
  • somebody deletes (mqtt) user credentails on server, and later restore them,

So if device reboots and goes through connect while one of above edge cases occur, then program would crash.
However when all exceptions on connect are handled and reconnect is manually rescheduled then it works (as suggesed).
That is why I am suggesting that connect should reconnect automatically but notify rest of program about connection failure through some callback.

@Lenka42
Copy link
Collaborator

Lenka42 commented Dec 2, 2019

Hey @d21d3q
I added kwarg raise_exc=True to client.connect method. Together with unlimitted reconnects this should work in your case. LEt me know if it helps 🙂

@skewty
Copy link

skewty commented Dec 21, 2019

@d21d3q does this resolve your problem? Can this issue be closed?

@d21d3q
Copy link
Author

d21d3q commented Dec 21, 2019

I will have a look at it on Monday. Thank you.

@mfrlin
Copy link

mfrlin commented Jan 13, 2020

No, this is not the correct way of handling this. If you take a look at the reconnect function you will see that it handles OSError and connect function does not. This is why the client explodes when broker is not online and you try to connect.

Workaround is something like this:

client = MQTTClient("client_id")
while True:
    try:
        await client.connect(broker_host)
    except OSError:
        print(f'Can not connect to the broker at {broker_host}')
        await asyncio.wait([asyncio.sleep(5), STOP.wait()], return_when=asyncio.FIRST_COMPLETED)
        if STOP.is_set():
            sys.exit()
    else:
        break

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

@NICOLASGON
Copy link

http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033

If a server sends a CONNACK packet containing a non-zero return code it MUST then close the Network Connection [MQTT-3.2.2-5]

gmqtt doesn't implement the MQTT standard for this particular point.

@Lenka42
Copy link
Collaborator

Lenka42 commented Feb 17, 2020

@NICOLASGON Specification tells server must close the connection after it sends bad CONNACK package. gmqtt is about client relaization and docs do not specify any required actions after receiving bad connack code.

@mfanara
Copy link

mfanara commented Feb 25, 2022

@Lenka42 - Using the current master branch (as of 25 FEB 2022), what is the correct answer?

This works:

try:
await lLocalMRClient.connect("localhost", 1884, keepalive=60)
except ConnectionRefusedError:
asyncio.ensure_future(lLocalMRClient.reconnect(delay=True))

as does:

try:
await lLocalMRClient.connect("localhost", 1884, keepalive=60, raise_exc=True)
except:
asyncio.ensure_future(lLocalMRClient.reconnect(delay=True))

And I see above the suggestion to catch OSError

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants