[Python] How to gracefully stop an asyncio server in python 3.8

Discussão em 'Python' iniciado por Stack, Fevereiro 27, 2025.

  1. Stack

    Stack Membro Participativo

    As part of learning python and asyncio I have a simple TCP client/server architecture using asyncio (I have reasons why I need to use that) where I want the server to completely exit when it receives the string 'quit' from the client. The server, stored in asyncio_server.py, looks like this:

    import socket
    import asyncio

    class Server:
    def __init__(self, host, port):
    self.host = host
    self.port = port

    async def handle_client(self, reader, writer):
    # Callback from asyncio.start_server() when
    # a client tries to establish a connection
    addr = writer.get_extra_info('peername')
    print(f'Accepted connection from {addr!r}')

    request = None

    try:
    while request != 'quit':
    request = (await reader.read(255)).decode('utf8')
    print(f"Received {request!r} from {addr!r}")

    response = 'ok'

    writer.write(response.encode('utf8'))
    await writer.drain()

    print(f'Sent {response!r} to {addr!r}')
    print('----------')

    except Exception as e:
    print(f"Error handling client {addr!r}: {e}")
    finally:
    print(f"Connection closed by {addr!r}")

    writer.close()
    await writer.wait_closed()
    print(f'Closed the connection with {addr!r}')

    asyncio.get_event_loop().stop() # <<< WHAT SHOULD THIS BE?

    async def start(self):
    server = await asyncio.start_server(self.handle_client, self.host, self.port)

    async with server:
    print(f"Serving on {self.host}:{self.port}")
    await server.serve_forever()

    async def main():
    server = Server(socket.gethostname(), 5000)
    await server.start()

    if __name__ == '__main__':
    asyncio.run(main())


    and when the client sends quit the connection is closed and the server exits but always with the error message:

    Traceback (most recent call last):
    File "asyncio_server.py", line 54, in <module>
    asyncio.run(main())
    File "C:\Python38-32\lib\asyncio\runners.py", line 44, in run
    return loop.run_until_complete(main)
    File "C:\Python38-32\lib\asyncio\base_events.py", line 614, in run_until_complete
    raise RuntimeError('Event loop stopped before Future completed.')
    RuntimeError: Event loop stopped before Future completed.


    What do I need to do instead of or in addition to calling asyncio.get_event_loop().stop() and/or server.serve_forever() to have the server exit gracefully with no error messages?

    I've tried every alternative I can find with google, including calling cancel() on the server, using separate loop construct in main(), trying alternatives to stop(), alternatives to run_forever(), etc., etc. and cannot figure out what I'm supposed to do to gracefully stop the server and exit the program without error messages when it receives a quit message from the client.

    I'm using python 3.8.10 and cannot upgrade to a newer version due to managed environment constraints. I'm also calling python from git bash in case that matters.


    Additional Information:

    The client code, stored in asyncio_client.py`, is below in case that's useful.

    import socket
    import asyncio

    class Client:
    def __init__(self, host, port):
    self.host = host
    self.port = port

    async def handle_server(self, reader, writer):
    addr = writer.get_extra_info('peername')
    print(f'Connected to from {addr!r}')

    request = input('Enter Request: ')

    while request.lower().strip() != 'quit':
    writer.write(request.encode())
    await writer.drain()
    print(f'Sent {request!r} to {addr!r}')

    response = await reader.read(1024)
    print(f'Received {response.decode()!r} from {addr!r}')
    print('----------')

    request = input('Enter Request: ')

    writer.write(request.encode())
    await writer.drain()
    print(f'Sent {request!r} to {addr!r}')

    writer.close()
    await writer.wait_closed()
    print(f'Closed the connection with {addr!r}')

    async def start(self):
    reader, writer = await asyncio.open_connection(host=self.host, port=self.port)

    await self.handle_server(reader, writer)

    async def main():
    client = Client(socket.gethostname(), 5000)
    await client.start()

    if __name__ == '__main__':
    asyncio.run(main())

    Continue reading...

Compartilhe esta Página