下面是一个简单的回显服务器。但是,如果客户端在10秒钟内未发送任何内容,则我想关闭连接。
import asyncio
async def process(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
print("awaiting for data")
line = await reader.readline()
print(f"received {line}")
writer.write(line)
print(f"sent {line}")
await writer.drain()
print(f"Drained")
async def timeout(task: asyncio.Task, duration):
print("timeout started")
await asyncio.sleep(duration)
print("client unresponsive, cancelling")
task.cancel()
print("task cancelled")
async def new_session(reader, writer):
print("new session started")
task = asyncio.create_task(process(reader, writer))
timer = asyncio.create_task(timeout(task, 10))
await task
print("task complete")
timer.cancel()
print("timer cancelled")
writer.close()
print("writer closed")
async def a_main():
server = await asyncio.start_server(new_session, port=8088)
await server.serve_forever()
if __name__ == '__main__':
asyncio.run(a_main())
如果客户端发送一条消息,它可以正常工作。但是在另一种情况下,当客户端保持沉默时,它将无法正常工作
当客户发送消息时:
new session started
awaiting for data
timeout started
received b'slkdfjsdlkfj\r\n'
sent b'slkdfjsdlkfj\r\n'
Drained
task complete
timer cancelled
writer closed
打开连接后客户端保持静音状态
new session started
awaiting for data
timeout started
client unresponsive, cancelling
task cancelled
没有task complete
,timer cancelled
,writer closed
。
更新
找出了问题,看起来任务实际上已经取消了,但是异常被忽略了,通过捕获CancelledError
async def new_session(reader, writer):
print("new session started")
task = asyncio.create_task(process(reader, writer))
timer = asyncio.create_task(timeout(task, 10))
try:
await task
except asyncio.CancelledError:
print(f"Task took too long and was cancelled by timer")
print("task complete")
timer.cancel()
print("timer cancelled")
writer.close()
print("writer closed")
第二部分仍然存在。有没有更好的方法来实现超时?
Update2
使用wait_for
完成代码。不再需要超时代码。在下面检查接受的solution:
async def new_session(reader, writer):
print("new session started")
try:
await asyncio.wait_for(process(reader, writer), timeout=5)
except asyncio.TimeoutError as te:
print(f'time is up!{te}')
finally:
writer.close()
print("writer closed")
答案 0 :(得分:2)
建立连接时,我使用以下代码。我建议为您的代码类似地使用wait_for。
fut = asyncio.open_connection( self.host, self.port, loop=self.loop )
try:
r, w = await asyncio.wait_for(fut, timeout=self.connection_timeout)
except asyncio.TimeoutError:
pass
答案 1 :(得分:0)
是否有更好的方法来实现超时?
您可以使用asyncio.wait_for
代替timeout
。它具有相似的语义,但是已经带有asyncio。另外,您可以等待将来返回以检测是否发生了超时。