'Python asyncio: Cancel streams server and all clients

I have a streams server that handles multiple independent clients. When I shut it down I want to notify all clients that the server has shut down.

I figured out how to close the server to new connections, but not how to cancel the specific handlers waiting for client data.

So far the only solution I found is to cancel all tasks in the loop, but this doesn't work for me as I have other tasks that must finish their jobs first.

Does asyncio provide some interface for this or do I have to keep track of all connections myself and cancel them once the server shuts down? I would prefer if the connection handler catches an exception when it calls await reader.readuntil() and not in the middle of execution, but this is not required.

Right now the client looses connection without warning. With this it cannot tell if it was a network issue or if the server shut down.

import asyncio
import signal

server = None
shutdown = False

async def important_task():
    while not shutdown:
        await asyncio.sleep(10)
        print("I am important")

async def handle_conn(reader,writer):
    print("Got connection")
    try:
        while True:
            text = await reader.readuntil(b'\n')
            # Do stuff
            writer.write( text ) # Echo example
            await writer.drain()
    except serverShutdownException: # How do I cause something like this?
        writer.write(b"Goodbye")
        await writer.drain()
    finally:
        writer.close()
        await writer.wait_closed()

def handle_sig(num,frame):
    global shutdown
    print(f"Caught {num}")
    server.close()
    shutdown = True

async def serve():
    global server
    server = await asyncio.start_server(handle_conn,"127.0.0.1",8080)
    try:
        await server.serve_forever()
    except asyncio.CancelledError:
        pass
    await server.wait_closed()
    # wait for all handlers to be done?

def main():
    signal.signal(signal.SIGINT, handle_sig)
    loop = asyncio.get_event_loop()
    t1 = loop.create_task(serve())
    t2 = loop.create_task(important_task())
    loop.run_until_complete(asyncio.gather(t1,t2))

main()


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source