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('客户端已断开连接')
关键调整说明
- 事件监听独立化:把处理客户端命令的
data事件监听移到了全局作用域,确保SocketIO启动时就完成注册,客户端发送事件能立即触发处理。 - 连接级资源隔离:用
session.sid(每个连接的唯一标识)来存储对应的PTY文件描述符,避免多用户使用时互相干扰。 - 事件循环不阻塞:用
socketio.start_background_task来运行PTY的监听逻辑,防止长时间操作卡住SocketIO的主线程。 - 资源自动清理:新增了断开连接的处理函数,及时关闭PTY文件描述符,避免资源泄漏。
额外小提示
- 如果是单用户使用场景,全局字典可以简化,但多用户场景下必须按会话隔离资源。
- 输出内容如果直接传字节,客户端可能需要额外处理转码,转成字符串后更通用。
备注:内容来源于stack exchange,提问作者Xandaaah
相关产品推荐
相关产品推荐

