Client will not quit when ctrl+c on server terminal

Hi community,

I comment out these two lines in the client.py to simulate the situation that there is no data to write() from a client right now, but the client should still read().

# writer.write(f'hello server from {message} {seq}\n'.encode())
# await writer.drain()

The problem is when I Ctrl+C on server terminal, the client will not quit with these two lines commented out now. How can make it quit like before?

# python3 client.py <ip> <port> <client_tag>

import asyncio
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(
    os.path.abspath(__file__))))
from logging2.logging2 import *


async def handle_conn(reader, writer, message):
    seq = 0

    while True:
        seq += 1

        try:
            # writer.write(f'hello server from {message} {seq}\n'.encode())
            # await writer.drain()

            data = await asyncio.wait_for(reader.read(100),
                                          timeout=0.01)
            if data:
                print(f'{data.decode()}', end='')

        except asyncio.TimeoutError as e:
            # INFO(f'{e}')
            continue
        except KeyboardInterrupt as e:
            INFO(f'{e}')
            break

        # await asyncio.sleep(1) # test only

    writer.close()


async def main():
    ip = sys.argv[1]
    port = sys.argv[2]
    tag = sys.argv[3]
    INFO(f'connecting to {ip}:{port}')

    reader, writer = await asyncio.open_connection(ip, port)
    await handle_conn(reader, writer, tag)


try:
    logging2_init()
    asyncio.run(main())
except KeyboardInterrupt as e:
    INFO(f'{e}')
    pass
except ConnectionResetError as e:
    INFO(f'{e}')
    pass
except ConnectionRefusedError as e:
    INFO(f'{e}')
    pass
except BrokenPipeError as e:
    INFO(f'{e}')
    pass


# python3 server.py <port>

import asyncio
import signal
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(
    os.path.abspath(__file__))))
from logging2.logging2 import *


running = True


async def handle_conn(reader, writer):
    seq = 0
    addr = writer.get_extra_info('peername')
    INFO(f'accepting connection from {addr}')

    while running:
        seq += 1

        try:
            writer.write(f'hello client {seq}\n'.encode())
            await writer.drain()

            data = await asyncio.wait_for(reader.read(100),
                                          timeout=0.01)

            if data:
                print(f'{data.decode()}', end='')

        except asyncio.TimeoutError as e:
            # INFO(f'{e}')
            continue
        except ConnectionResetError as e:
            INFO(f'{e}')
            break
        except BrokenPipeError as e:
            INFO(f'{e}')
            break
        except KeyboardInterrupt as e:
            INFO(f'{e}')
            break
        except RuntimeError as e:
            INFO(f'{e}')
            break

        # await asyncio.sleep(1) # test only

    writer.close()


async def main():
    port = sys.argv[1]
    server = await asyncio.start_server(handle_conn, '', port)

    addrs = ', '.join(str(sock.getsockname())
                      for sock in server.sockets)
    INFO(f'serving on {addrs}')

    async with server:
        await server.serve_forever()


def handle_sigint(signum, frame):
    global running
    running = False

    for task in asyncio.all_tasks():
        task.cancel()

    asyncio.get_event_loop().stop()
    signal.default_int_handler(signum, frame)


try:
    logging2_init()
    signal.signal(signal.SIGINT, handle_sigint)
    asyncio.run(main())
except RuntimeError as e:
    INFO(f'{e}')
    pass
except KeyboardInterrupt as e:
    INFO(f'{e}')
    pass