pythonsocketspython-asynciopython-3.12

my python asyncio server won't stop even after all clients are disconnected


My asyncio server won't shut down if a client has connected even if the client closes the socket. Why won't it stop? If a client does not connect within 10 seconds it shuts down and the program closes.

My procedure:

Result:

I found that adding writer.close() in the clientHandler solved the issue but why? Where is this documented/explained?

# server
from os import path as ospath
import asyncio
import socket as Socket
import logger as log

logger = log.getLogger(fileName=ospath.join('log', 'server.log'), initialize=True)

class Client():
    def __init__(self, socket:Socket=None, addr:tuple=None) -> None:
        self.socket = socket
        self.addr = addr
        self.connected = False

class App():
    def __init__(self) -> None:
        self.quit = False
        self.clients = []

async def clientHandler(reader, writer):
    logger.info('client handler starting')

async def main():
    logger.info("server app starting")
    app = App()
    
    # start socket server
    ipaddr = Socket.gethostbyname(Socket.gethostname())
    port = 32843
    logger.info(f'starting socket server ({ipaddr}:{port})')
    server = await asyncio.start_server(clientHandler, ipaddr, port=32843)

    logger.info('waiting for client to connect...')
    await asyncio.sleep(10)
    logger.info('closing server')
    server.close()

    await server.wait_closed()
    logger.info('server closed')

if __name__ == '__main__':
    asyncio.run(main())
# client
import socket

dest_ip = socket.gethostbyname(socket.gethostname())
dest_port = 32843
encoder = "utf-8"

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((dest_ip, dest_port))

client_socket.shutdown(socket.SHUT_RDWR)
client_socket.close()

Solution

  • The client closes the socket, but the TCP connection remains half-closed, because the server does not close its end. Thus the server cannot consider the client disconnected.

    A half-closed connection happens when one side sends EOF. The other side can continue to send data before it sends an EOF too.

    You need to add writer.close() to the clientHandler() to fix it.


    Side note: server.wait_closed() was slightly broken before Python 3.12.1 and it is probably not possible to reproduce the issue on older versions.