4

I've finally gotten around to learning python's asyncio, and am creating a simple client-server. I can easily use open_connection and start_server and get everything talking. However, I've run into some unexpected behavior detecting when one side closes the connection unexpectedly.

On the client-side, I can easily detect the server closed the connection because any call to StreamReader.read being awaited either errors or returns nothing. However, a StreamWriter.write/await StreamWriter.drain call seems to succeed regardless of whether the other side still has the socket open or not, and this is what confuses me.

Client (in a coroutine which is called via run_until_complete:

_, writer = await asyncio.open_connection('127.0.0.1', 1234)
_ = input('Connected.  Type and press enter to attempt send.')

try:
    writer.write(b'A message')
    await writer.drain()
    print('Sent successfully.')

except:
    print(traceback.print_exc())
finally:
    writer.close()
    await writer.wait_closed()

Server (synchronous version, I've also tried an async server):

with socket.socket() as s:
    s.bind(('0.0.0.0', 1234))
    s.listen(1)

    con, addr = s.accept()
    with con:
        print(f'Connected to {addr[0]}:{addr[1]}')
        _ = input('Type and press Enter to close.')

If I start the server, connect the client, then have the server terminate the connection, then tell the client to write, I would expect based on the documentation and this StackOverflow post to receive an exception, but instead the client just prints Sent successfully. Tested with Python 3.7.6 and Python 3.8.1.

I'm aware that this won't matter in many actual applications, as you're likely to have a read-loop of some sort so you'd get know about the issue from a call to StreamReader.read. That much said, I'm at a loss as to why I'm unable to detect the failure on write?

martineau
  • 119,623
  • 25
  • 170
  • 301
Chris3606
  • 43
  • 4

1 Answers1

2

What you are trying to implement is incompatible with how TCP sockets work. Your drain() succeeds not because of an asyncio peculiarity, but because the underlying write() succeeds. OS-level writing succeeds because it is buffered by the kernel for efficiency.

If you want to detect that the client is still there, you will need to design a protocol that requires it to respond to the write, and detect eof while reading that response. But even so, you will need a timeout while reading, as there is no guarantee that the client's disconnect will always be "clean" and result in a shutdown signal.

user4815162342
  • 141,790
  • 18
  • 296
  • 355