如何在异步服务器中实现超时?

时间:2018-12-27 20:09:38

标签: python python-3.x python-asyncio session-timeout

下面是一个简单的回显服务器。但是,如果客户端在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 completetimer cancelledwriter closed

  1. 上面的代码有什么问题?
  2. 是否有更好的方法来实现超时?

更新

找出了问题,看起来任务实际上已经取消了,但是异常被忽略了,通过捕获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")

2 个答案:

答案 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。另外,您可以等待将来返回以检测是否发生了超时。