profile
viewpoint

Ask questionsSIGINT handler does not disconnect client

When attempting to disconnect client, it only disconnects once, but then reconnects.

Connection log:

DEBUG:asyncio:Using selector: KqueueSelector
INFO:engineio.client:Attempting WebSocket connection to wss://myproject.com/api/ws/?transport=websocket&EIO=3
INFO:engineio.client:WebSocket connection accepted with {'sid': '7a2f537ad5cc49e79c5f05fa3644c923', 'upgrades': [], 'pingTimeout': 60000, 'pingInterval': 25000}
INFO:socketio.client:Engine.IO connection established
INFO:engineio.client:Sending packet PING data None
INFO:engineio.client:Received packet MESSAGE data 0
INFO:socketio.client:Namespace / is connected
INFO:engineio.client:Received packet PONG data None

After sending SIGINT:

INFO:engineio.client:Sending packet PING data None
ERROR:engineio.client:packet queue is empty, aborting
INFO:engineio.client:Exiting write loop task
INFO:engineio.client:Unexpected error receiving packet: "", aborting
INFO:engineio.client:Waiting for write loop task to end
INFO:engineio.client:Waiting for ping loop task to end
INFO:engineio.client:PONG response has not been received, aborting
INFO:engineio.client:Exiting ping task
INFO:socketio.client:Engine.IO connection dropped
WARNING:project:Disconnected
INFO:engineio.client:Exiting read loop task
INFO:socketio.client:Connection failed, new attempt in 0.91 seconds
INFO:engineio.client:Attempting WebSocket connection to wss://myproject.com/api/ws/?transport=websocket&EIO=3
INFO:engineio.client:WebSocket connection accepted with {'sid': '3c5ebff507024dadbb293e9d1adfcad7', 'upgrades': [], 'pingTimeout': 60000, 'pingInterval': 25000}
INFO:socketio.client:Engine.IO connection established
INFO:socketio.client:Reconnection successful
INFO:engineio.client:Sending packet PING data None
INFO:engineio.client:Received packet MESSAGE data 0
INFO:socketio.client:Namespace / is connected
INFO:engineio.client:Received packet PONG data None

After another SIGINT:

^CERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<AsyncClient.disconnect() running at /Users/asnelzin/dev/project/venv/lib/python3.7/site-packages/engineio/asyncio_client.py:106>>
/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/base_events.py:604: RuntimeWarning: coroutine 'AsyncClient.disconnect' was never awaited
  self._ready.clear()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Traceback (most recent call last):
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/base_events.py", line 566, in run_until_complete
    self.run_forever()
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/base_events.py", line 534, in run_forever
    self._run_once()
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/base_events.py", line 1735, in _run_once
    event_list = self._selector.select(timeout)
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/selectors.py", line 558, in select
    kev_list = self._selector.control(None, max_ev, timeout)
  File "/Users/asnelzin/dev/project/venv/lib/python3.7/site-packages/engineio/client.py", line 43, in signal_handler
    return original_signal_handler(sig, frame)
  File "/Users/asnelzin/dev/project/venv/lib/python3.7/site-packages/socketio/client.py", line 26, in signal_handler
    return original_signal_handler(sig, frame)
KeyboardInterrupt

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/Users/asnelzin/dev/project/project/__main__.py", line 93, in <module>
    asyncio.run(main(sys.argv[1:]))
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/runners.py", line 46, in run
    _cancel_all_tasks(loop)
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/runners.py", line 62, in _cancel_all_tasks
    tasks.gather(*to_cancel, loop=loop, return_exceptions=True))
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/base_events.py", line 566, in run_until_complete
    self.run_forever()
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/base_events.py", line 534, in run_forever
    self._run_once()
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/base_events.py", line 1735, in _run_once
    event_list = self._selector.select(timeout)
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/selectors.py", line 558, in select
    kev_list = self._selector.control(None, max_ev, timeout)
  File "/Users/asnelzin/dev/project/venv/lib/python3.7/site-packages/engineio/client.py", line 43, in signal_handler
    return original_signal_handler(sig, frame)
  File "/Users/asnelzin/dev/project/venv/lib/python3.7/site-packages/socketio/client.py", line 26, in signal_handler
    return original_signal_handler(sig, frame)
KeyboardInterrupt
ERROR:asyncio:Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x10c7e0310>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<AsyncClient._ping_loop() running at /Users/asnelzin/dev/project/venv/lib/python3.7/site-packages/engineio/asyncio_client.py:446> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10c7ed790>()]>>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<AsyncClient._write_loop() running at /Users/asnelzin/dev/project/venv/lib/python3.7/site-packages/engineio/asyncio_client.py:551> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10c7eda50>()]>>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<AsyncClient._read_loop_websocket() running at /Users/asnelzin/dev/project/venv/lib/python3.7/site-packages/engineio/asyncio_client.py:500> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10c0af5d0>()]> cb=[<TaskWakeupMethWrapper object at 0x10c7ed9d0>()]>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<Queue.get() running at /Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/queues.py:159> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10c7ed2d0>()]> cb=[_release_waiter(<Future pendi...10c7eda50>()]>)() at /Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/tasks.py:392]>
Exception ignored in: <coroutine object Queue.get at 0x10c806710>
Traceback (most recent call last):
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/queues.py", line 161, in get
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/base_events.py", line 683, in call_soon
  File "/Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/base_events.py", line 475, in _check_closed
RuntimeError: Event loop is closed
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<Event.wait() running at /Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/locks.py:293> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10c7ed7d0>()]> cb=[_release_waiter(<Future pendi...10c7ed790>()]>)() at /Users/asnelzin/.pyenv/versions/3.7.4/lib/python3.7/asyncio/tasks.py:392]>
miguelgrinberg/python-socketio

Answer questions chrisdlangton

Zombie maker;

import atexit
import logging
import json
import socketio


log = logging.getLogger(__name__)
sio = socketio.Client()
atexit.register(sio.disconnect)

@sio.event
def connect():
    log.info("connected")

@sio.event
def connect_error():
    log.info("connection failed")

@sio.event
def disconnect():
    log.info("disconnected")

def not_the_real_send_event(event: str, data: dict, host: str):
    if not sio.connected:
        sio.connect(host)
    sio.emit(event, json.dumps(data, sort_keys=True, default=str))
 
if __name__ == "__main__":
    # Do stuff that will use not_the_real_send_event()
    log.info(f'Finished')
    sys.exit(0)

This caused output of connected and expected script outputs, finally i get a Finished which should be the end - however the process remains active. You can force external connectivity to be lost (by cycling the socket server or taking down the interface) which causes the zombie to produce output disconnected followed by connected when a connectivity is re-established. these occur after the exit;

connected
# do stuff that has outputs
Finished
disconnected
connected
disconnected

The fix was simply removing atexit

import logging
import json
import socketio
import signal


log = logging.getLogger(__name__)
sio = socketio.Client()

@sio.event
def connect():
    log.info("connected")

@sio.event
def connect_error():
    log.info("connection failed")

@sio.event
def disconnect():
    log.info("disconnected")

def not_the_real_send_event(event: str, data: dict, host: str):
    if not sio.connected:
        sio.connect(host)
    sio.emit(event, json.dumps(data, sort_keys=True, default=str))

def signal_handler(signum, stack_frame):
    message = f'Signal handler called with signal {signum}'
    log.warning(message)
    log.debug(stack_frame)
    sio.disconnect()
    sys.exit(0)

signal.signal(signal.SIGQUIT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGTSTP, signal_handler) # ctrl+z
signal.signal(signal.SIGINT, signal_handler) # ctrl+c
 
if __name__ == "__main__":
    # Do stuff that will use not_the_real_send_event()
    sio.disconnect()
    log.info(f'Finished')
    sys.exit(0)

However if there is an unhanded signal, and no sio.disconnect() call, we are a zombie again.

useful!

Related questions

Strange behaviours when pingInterval > pingTimeout hot 2
Invalid async_mode specified hot 2
How to terminate a disconnected client? hot 2
Is it possible to: Retrieve responses that do not have an event name? hot 1
ImportError: cannot import name 'Namespace' from 'socketio' (/usr/lib/python3.7/site-packages/socketio/__init__.py) hot 1
AttributeError: 'module' object has no attribute 'Server' hot 1
python-socketio can not connect to flask server hot 1
Python Server & Node Client , On Authentication Fail client receives 1 fix error in any scenario. hot 1
No disconnect event triggered after receiving no pong hot 1
How to resolve multiple CORS values in socketio requests, Sanic? hot 1
source:https://uonfu.com/
Github User Rank List