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

Python中执行异步函数后如何正确关闭WebSocket连接?

问题分析

你的核心问题是同步阻塞操作(示例里的input()、实际项目里的Textual应用)卡住了asyncio的主事件循环,导致后续的WebSocket关闭流程无法正常执行:

  • 同步函数(如input())会完全占用事件循环所在的线程,让所有异步任务(包括WebSocket的心跳、关闭握手)都无法被调度。
  • 当执行到await websocket.close()时,事件循环已经被阻塞了很长时间,远端可能已经主动断开连接,或者本地的关闭请求无法完成握手,最终导致超时。

解决方案

针对示例代码:替换同步输入为异步执行

把阻塞的input()放到单独线程中运行,避免占用asyncio事件循环,使用asyncio.to_thread()实现:

import asyncio
import websockets

async def connect_and_send(ip, port):
    uri = f"ws://{ip}:{port}"
    websocket = await websockets.connect(uri)
    await websocket.send(r"C:\Volgistics\Vlogger;Boi.txt;;None")
    print("Message sent.")

    await second_function(websocket)

    print("Closing the connection...")
    await websocket.close()

async def second_function(websocket):
    # 将同步input放到线程执行,不阻塞事件循环
    await asyncio.to_thread(input, "Press Enter to send additional message...")
    await websocket.send(r"C:\Volgistics\Vlogger;Boi.txt;second;None")
    print("Additional message sent.")

ip = "192.168.3.181"
port = 8888

asyncio.run(connect_and_send(ip, port))

针对实际项目中的Textual应用

Textual默认会启动自己的事件循环,直接调用run()会阻塞asyncio主循环,推荐两种兼容方式:

方式1:让Textual共享asyncio事件循环(推荐)

Textual 0.19.0+支持run_async()方法,可在当前asyncio循环中运行应用,避免阻塞:

from textual.app import App

async def second_function(websocket):
    app = YourTextualApp()  # 替换为你的Textual应用类
    # 在asyncio循环中异步运行Textual
    await app.run_async()
    # 应用退出后继续执行WebSocket操作
    await websocket.send("Textual app exited")

方式2:单独线程运行Textual,用队列通信

如果必须使用同步的run()方法,将Textual放到单独线程,通过asyncio.Queue实现线程与asyncio循环的安全通信:

import threading
from textual.app import App
import asyncio

async def second_function(websocket):
    exit_signal = asyncio.Queue()

    def run_textual():
        app = YourTextualApp()  # 替换为你的Textual应用类
        app.run()
        # 应用退出后向asyncio循环发送信号
        asyncio.run_coroutine_threadsafe(exit_signal.put("done"), asyncio.get_running_loop())

    # 启动线程运行Textual
    thread = threading.Thread(target=run_textual)
    thread.start()

    # 等待Textual退出信号
    await exit_signal.get()
    thread.join()

    # 继续执行WebSocket操作
    await websocket.send("Textual app exited")

关键注意事项
  • 永远不要在异步函数中直接调用同步阻塞函数(如input()、time.sleep()),必须用asyncio.to_thread()或异步替代方案。
  • WebSocket的关闭是异步握手过程,需要事件循环正常运行才能完成,阻塞会导致握手超时。
  • 跨线程操作asyncio对象时,必须用asyncio.run_coroutine_threadsafe()保证线程安全,禁止直接在非事件循环线程中调用异步方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 23:33:12