如何在异步上下文管理器中使用多Socket服务?连接被立即关闭问题
问题根源
你遇到的问题是因为async with块在调用asyncio.create_task()后立刻执行完毕了——create_task只会把协程交给事件调度器,不会阻塞当前代码。所以上下文管理器直接进入__aexit__方法,把刚建立的Socket连接给关了,导致后续的echo任务根本没法正常通信。
解决办法
这里给两个实用的修改方案,按需选择:
方案1:串行处理连接(简单直接)
如果你的服务器不需要同时处理多个连接,直接在async with块里等待任务完成就行,这样连接会保持到任务结束才关闭:
修改connection_listener方法:
async def connection_listener(self, server_socket): while True: async with ConnectedSocket(server_socket) as conn: task = asyncio.create_task(self.echo(conn)) self.tasks.append(task) await task # 等echo任务做完再退出上下文管理器
方案2:并发处理连接(适合多客户端场景)
如果要支持同时多个客户端连接,就得调整上下文管理器的逻辑,让连接的关闭由处理任务自己控制,而不是上下文管理器自动关闭:
第一步:修改ConnectedSocket类
class ConnectedSocket: def __init__(self, server_socket): self._connection = None self._server_socket = server_socket async def __aenter__(self): print("Entering context manager, waiting for connection") loop = asyncio.get_event_loop() conn, addr = await loop.sock_accept(self._server_socket) conn.setblocking(False) self._connection = conn print("Accepted a connection") return self # 返回整个实例,方便后续调用close方法 async def __aexit__(self, exc_type, exc_val, exc_tb): print("Exiting context manager") # 这里不再主动关闭连接,交给任务处理完后再关 def close(self): if self._connection: self._connection.close() print("Closed connection")
第二步:修改echo和connection_listener方法
async def connection_listener(self, server_socket): while True: async with ConnectedSocket(server_socket) as sock_wrapper: # 把包装后的实例传给echo任务 task = asyncio.create_task(self.echo(sock_wrapper)) self.tasks.append(task) async def echo(self, sock_wrapper: ConnectedSocket) -> None: conn = sock_wrapper._connection try: while data := await self.loop.sock_recv(conn, 1024): if data == b'exit\r\n': break print(data) finally: # 不管任务正常结束还是异常,都关闭连接 sock_wrapper.close()
测试结果
修改后重新运行服务器,用telnet测试:
Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. hello world hello world exit Connection closed by foreign host.
现在可以正常收发数据,直到发送exit指令后连接才会关闭,完美解决问题。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

