我终于开始学习python的asyncio
,并正在创建一个简单的客户端服务器。我可以轻松地使用open_connection
和start_server
并开始谈论一切。但是,当一侧意外关闭连接时,我遇到了一些意外行为。
在客户端,我可以很容易地检测到服务器已关闭连接,因为等待StreamReader.read
的任何调用都出错或什么都不返回。但是,无论对方是否仍打开套接字,StreamWriter.write
/ await StreamWriter.drain
调用似乎都能成功,这使我感到困惑。
客户(在通过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()
服务器(同步版本,我也尝试过异步服务器):
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.')
如果我启动服务器,连接客户端,然后让服务器终止连接,然后告诉客户端写,我希望根据文档和this StackOverflow post会收到异常,但是客户端只能打印Sent successfully.
经过Python 3.7.6和Python 3.8.1测试。
我知道这在许多实际应用中都无关紧要,因为您可能会有某种读取循环,因此您可以通过调用StreamReader.read
来了解该问题。这么多话,我迷茫了为什么无法检测到write
上的故障?
答案 0 :(得分:1)
您要实现的内容与TCP套接字的工作方式不兼容。您的drain()
成功不是因为异步特性,而是因为基础的write()
成功。操作系统级别的写入成功是因为它被内核缓冲以提高效率。
如果要检测到客户端仍在,则需要设计一个协议,要求该客户端对写入进行响应,并在读取该响应时检测eof。但是即使如此,您仍需要在读取时超时,因为无法保证客户端的断开连接始终是“干净的”并导致关闭信号。