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

Flask-SocketIO+PTY虚拟控制台:发送命令时recv处理函数未被调用

Flask-SocketIO+PTY虚拟控制台:发送命令时recv处理函数未被调用

我来帮你分析下问题所在,你的代码里有个关键的逻辑错误直接导致了recv函数没被调用:

你把@socketio.on('data')的事件处理器嵌套在了write_handler函数里面,而write_handler只有在pty.spawn内部触发写操作时才会被调用——这就相当于SocketIO的data事件监听根本没被正确注册到全局的事件系统里,自然当客户端发送命令时,recv函数完全接收不到触发信号。

给你整理了修正后的代码,我会同步解释关键的调整点:

import os
import pty
from flask import session
from flask_socketio import SocketIO, emit, disconnect

# 假设你已经初始化好了SocketIO实例
# socketio = SocketIO(app)

# 用字典存储每个连接对应的PTY主文件描述符,以会话ID作为唯一标识
connection_pty_fds = {}

@socketio.on('connect')
def test_connect(auth: str):
    print('客户端已连接', auth)

    def read_handler(fd: int):
        # 读取子进程的输出并发送给当前客户端
        output_data = os.read(fd, 1024)
        if output_data:
            emit('data', output_data.decode())  # 转成字符串更易处理,也可以直接传字节
            # 如果需要把输出广播给所有客户端,就打开下面的注释
            # emit('data', output_data.decode(), broadcast=True)

    # 创建PTY并启动子进程
    master_fd, slave_fd = pty.openpty()
    pid = os.fork()

    if pid == 0:
        # 子进程环境:将PTY从端设为标准输入、输出、错误输出
        os.dup2(slave_fd, 0)
        os.dup2(slave_fd, 1)
        os.dup2(slave_fd, 2)
        # 关闭子进程不需要的主端文件描述符
        os.close(master_fd)
        os.close(slave_fd)
        # 启动shell
        os.execlp('/bin/sh', '/bin/sh')
    else:
        # 主进程环境:关闭从端文件描述符
        os.close(slave_fd)
        # 把当前连接的PTY主FD存入字典,方便后续写入命令
        connection_pty_fds[session.sid] = master_fd
        # 用后台任务运行PTY监听,避免阻塞SocketIO的事件循环
        socketio.start_background_task(target=pty.spawn, args=('/bin/sh', read_handler, None))

@socketio.on('data')
def recv_client_command(data: str):
    # 根据当前会话ID获取对应的PTY主FD
    master_fd = connection_pty_fds.get(session.sid)
    if master_fd:
        # 将客户端发送的命令写入PTY,传递给子进程
        os.write(master_fd, data.encode())
        # 可选:如果需要在客户端回显输入的命令,打开下面的注释
        # emit('data', data)

@socketio.on('disconnect')
def clean_up_on_disconnect():
    # 客户端断开连接时,清理PTY资源
    master_fd = connection_pty_fds.pop(session.sid, None)
    if master_fd:
        os.close(master_fd)
    print('客户端已断开连接')

关键调整说明

  1. 事件监听独立化:把处理客户端命令的data事件监听移到了全局作用域,确保SocketIO启动时就完成注册,客户端发送事件能立即触发处理。
  2. 连接级资源隔离:用session.sid(每个连接的唯一标识)来存储对应的PTY文件描述符,避免多用户使用时互相干扰。
  3. 事件循环不阻塞:用socketio.start_background_task来运行PTY的监听逻辑,防止长时间操作卡住SocketIO的主线程。
  4. 资源自动清理:新增了断开连接的处理函数,及时关闭PTY文件描述符,避免资源泄漏。

额外小提示

  • 如果是单用户使用场景,全局字典可以简化,但多用户场景下必须按会话隔离资源。
  • 输出内容如果直接传字节,客户端可能需要额外处理转码,转成字符串后更通用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 17:44:50