You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python 3.8中优雅停止asyncio服务器

如何在Python 3.8中优雅停止asyncio服务器

我之前在Python3.8环境下也碰到过一模一样的问题,直接调用loop.stop()确实会导致那个讨厌的RuntimeError——因为asyncio.run()期望整个异步流程正常结束,而不是被强制终止事件循环。下面是亲测有效的解决方案:

问题根源

你当前代码里的asyncio.get_event_loop().stop()会直接终止事件循环,但此时server.serve_forever()这个异步任务还没完成,asyncio.run()会一直等待main()函数执行完毕,事件循环提前停止就触发了那个报错。

解决方案:优雅关闭服务器实例

核心思路是不要硬停事件循环,而是通过关闭服务器实例让整个异步流程自然收尾,具体修改步骤如下:

  1. 在Server类中新增属性保存服务器实例,这样在处理客户端的方法里能访问到它:
class Server:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.server = None  # 新增:用于保存服务器实例
  1. 在start()方法中,将创建的服务器实例赋值给self.server:
async def start(self):
    self.server = await asyncio.start_server(self.handle_client, self.host, self.port)

    async with self.server:
        print(f"Serving on {self.host}:{self.port}")
        await self.server.serve_forever()
  1. 修改handle_client中的停止逻辑,替换掉asyncio.get_event_loop().stop(),改为优雅关闭服务器:
# 替换原来的loop.stop()代码
if self.server:
    await self.server.close()
    await self.server.wait_closed()

另外还要注意一个细节:客户端发送的quit可能带有换行符,所以在读取请求时最好用strip()处理一下,避免因为多余的空白字符导致判断失效。

修改后的完整服务器代码

import socket
import asyncio

class Server:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.server = None  # 保存服务器实例

    async def handle_client(self, reader, writer):
        addr = writer.get_extra_info('peername')
        print(f'Accepted connection from {addr!r}')

        request = None

        try:
            while request != 'quit':
                # 用strip()去除可能的换行/空格,避免判断失效
                request = (await reader.read(255)).decode('utf8').strip()
                print(f"Received {request!r} from {addr!r}")

                # 针对quit请求返回特定响应
                if request == 'quit':
                    response = 'server shutting down'
                else:
                    response = 'ok'

                writer.write(response.encode('utf8'))
                await writer.drain()

                print(f'Sent {response!r} to {addr!r}')
                print('----------')

        except Exception as e:
            print(f"Error handling client {addr!r}: {e}")
        finally:
            print(f"Connection closed by {addr!r}")

        # 关闭客户端连接
        writer.close()
        await writer.wait_closed()
        print(f'Closed the connection with {addr!r}')

        # 优雅关闭服务器
        if self.server:
            await self.server.close()
            await self.server.wait_closed()

    async def start(self):
        self.server = await asyncio.start_server(self.handle_client, self.host, self.port)

        async with self.server:
            print(f"Serving on {self.host}:{self.port}")
            await self.server.serve_forever()

async def main():
    server = Server(socket.gethostname(), 5000)
    await server.start()

if __name__ == '__main__':
    asyncio.run(main())

为什么这样能正常工作?

当收到quit请求后,我们先给客户端返回响应,关闭客户端连接,然后调用server.close()停止接受新连接,server.wait_closed()等待服务器资源清理完成。之后server.serve_forever()会自动退出,async with块会完成服务器的上下文清理,start()方法正常结束,main()函数执行完毕,asyncio.run()就会优雅退出,不会再抛出错误。

备注:内容来源于stack exchange,提问作者Ed Morton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 11:53:16