如何通过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
相关产品推荐
相关产品推荐

