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

如何通过Ctrl-C正常终止使用asyncio和websockets的Python进程?

解决asyncio+websockets进程无法通过Ctrl-C终止的问题

运行以下Python代码时,偶尔Ctrl-C无法终止进程,只能用Ctrl-Z将进程转入后台,但WebSocket服务器占用的7878端口不会被释放。需要实现通过Ctrl-C正常终止进程:

async def handle_messages(websocket):
    async for message in websocket:
        await websocket.send(message)

async def start_server():
    async with serve(handle_messages, "localhost", 7878):
        await asyncio.Future()  # run forever

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

问题原因

代码中await asyncio.Future()会创建一个永远不会完成的Future,完全阻塞事件循环,导致异步信号处理逻辑(比如Ctrl-C触发的KeyboardInterrupt)无法被及时执行,进而无法正常终止进程。

解决方案

方案一:用Event替代永久Future,添加信号处理

通过asyncio.Event()等待终止信号,并为SIGINT(Ctrl-C)注册处理函数,让事件循环能响应终止指令:

import asyncio
from websockets import serve

async def handle_messages(websocket):
    async for message in websocket:
        await websocket.send(message)

async def start_server():
    stop_event = asyncio.Event()
    
    def trigger_stop():
        stop_event.set()
    
    # 为当前事件循环添加SIGINT信号处理
    loop = asyncio.get_running_loop()
    loop.add_signal_handler(asyncio.SIGINT, trigger_stop)
    
    async with serve(handle_messages, "localhost", 7878):
        await stop_event.wait()

if __name__ == '__main__':
    try:
        asyncio.run(start_server())
    except KeyboardInterrupt:
        print("服务器已正常终止")

方案二:用循环sleep替代永久Future

通过定期await asyncio.sleep()让事件循环有间隙处理信号,避免完全阻塞:

async def start_server():
    async with serve(handle_messages, "localhost", 7878):
        # 每秒让出一次事件循环,确保信号能被处理
        while True:
            await asyncio.sleep(1)

两种方案都能让事件循环响应Ctrl-C信号,正常终止进程并释放端口。

临时端口清理命令(Mac)

如果遇到端口被占用的情况,可通过以下命令强制终止占用7878端口的进程:

kill -9 $(lsof -ti:7878)

内容的提问来源于stack exchange,提问作者BAR

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 19:12:53