一套用 Python 实现的轻量远程桌面控制方案:
- 被控端(客户端):简洁稳定,跨平台(Windows / Linux / macOS),负责屏幕采集、输入注入、命令执行与文件管理。
- 中继服务器:托管现代化后台管理界面,纳管在线设备、在管理台与客户端之间透传数据。
- 管理台(Web):现代化、自包含(无外部 CDN)的控制台,支持远程观看 / 控制、终端命令、文件管理。
支持两种连接形态:
- 中继模式:客户端连接在线服务器,管理台通过网页统一查看和控制所有在线设备。
- 直连模式:客户端自带 WebSocket 服务,管理台用
IP:端口 + 授权 token直接连接,无需服务器。
| 功能 | 说明 |
|---|---|
| 在线设备纳管 | 服务器维护设备列表(主机名 / OS / 用户 / 架构 / 核数) |
| 远程观看 | 实时 JPEG 帧推流(二进制 WebSocket 帧,省去 base64 约 33% 开销),关键帧 + 区块增量(delta) 传输:客户端保留上一帧、按 64×64 区块比对差异,仅把变化区块编码发送,前端在底图上贴变化区域;静止画面自动跳帧。带宽从「整帧×fps」降到「变化区×fps」,全屏高画质高帧率下延迟与带宽占用大幅下降;按网络质量自动调帧率 / 质量 / 分辨率;控制台可实时调画质、全屏自动拉满 |
| 远程控制 | 鼠标移动 / 点击 / 右键 / 滚轮、键盘输入(含组合键) |
| 终端 | 在被控端执行 shell 命令并实时回显输出(按系统编码解码,中文不乱码) |
| 文件管理 | 浏览 / 上传 / 下载 / 删除 / 重命名;默认开放整台机器所有磁盘(指定 --root 则限制在该目录内),大文件以二进制分片流式传输 |
| 双连接模式 | 中继服务器 / 客户端直连,同一套协议 |
从登录到远程控制、终端命令、文件管理——全流程截图:
后台管理台需登录后才能访问(账号密码在启动服务时指定或随机生成)。
登录后的主界面:左侧在线设备列表 + 直连入口,右侧为操作区(未选设备时显示使用指引)。顶部可查看/复制授权 Token、一键下载客户端配置文件。
点击设备的「控制」按钮进入远程桌面画面。支持鼠标移动 / 点击 / 右键 / 滚轮、键盘输入(含组合键),可切换「控制」与「观看」模式。会话工具栏提供画质调节 / 全屏 / 断连等操作。
在被控端执行 shell 命令并实时回显输出(按系统编码解码,中文不乱码)。Windows 使用 ConPTY (winpty),Linux/macOS 使用 pty,体验接近 SSH。
浏览被控端磁盘目录结构,支持上传 / 下载 / 删除 / 重命名文件。大文件以二进制分片流式传输,带进度条显示。默认开放整台机器所有磁盘(可通过 --root 限制访问目录)。
会话工具栏「画质」面板可实时调整分辨率、JPEG 压缩质量、帧率,并可开启「自动适应网络」(被控端根据 QoS 上报自动调参)。全屏时自动拉满至原始分辨率 + 高画质 + 高帧率。
py_rdesk/
├── requirements.txt
├── common/
│ ├── protocol.py # WebSocket 消息协议
│ └── crypto.py # token 授权 / 校验
├── client/
│ ├── device.py # 被控端核心(采集/输入/命令/文件)
│ ├── relay.py # 中继模式客户端
│ ├── direct.py # 直连模式客户端(自带服务)
│ └── main.py # 客户端入口
├── server/
│ ├── main.py # FastAPI 中继服务器 + 静态托管
│ ├── devices.py # 设备注册表
│ └── static/ # 后台管理界面 (index.html / styles.css / app.js)
├── scripts/
│ └── build_client.py # 客户端跨平台打包脚本(PyInstaller onefile)
└── README.md
建议使用虚拟环境(Python 3.10+):
cd py_rdesk
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt注:被控端依赖
mss/pyautogui/Pillow需要图形环境。 Linux 上pyautogui需要 X11(sudo apt install scrot python3-tk等)。 服务器本身不需要图形库。
# 固定授权 token + 后台登录密码(生产必填)
python -m server.main --host 0.0.0.0 --port 8000 \
--token YOUR_STRONG_TOKEN --admin-user admin --admin-pass YOUR_LOGIN_PASSWORD未指定 --token / --admin-pass 时会各生成一次性随机值(重启失效),并在启动横幅里打印:
================================================================
[py_rdesk] 服务已启动,后台需登录后访问:
登录用户名: admin
登录密码 : 8f3Kd2pQx (随机生成,重启后失效)
授权 Token: RkrHlW6cX_... (随机生成,重启后失效)
================================================================
浏览器打开 http://<服务器IP>:8000 → 先用上面的用户名 / 密码登录,登录后即可在侧边栏
「服务信息」看到并复制授权 token,或一键下载即用的客户端配置文件(见下)。
python -m client.main relay --server ws://<服务器IP>:8000 --token YOUR_STRONG_TOKEN客户端会连上服务器并出现在后台「在线设备」列表中。
在被控机上:
python -m client.main direct --port 9000 --token YOUR_STRONG_TOKEN在后台管理台左侧「直连设备」填入被控机 IP、端口 9000 与同一 token,点击连接即可。
(确保网络可达该端口。)
在后台点击设备的「远程控制」:
- 顶部切换 控制 / 观看 模式。控制模式下在画面区域操作鼠标键盘即作用于被控端。
- 「终端」标签执行命令;「文件」标签浏览、上传、下载、删除、重命名文件。
被控端通用参数(relay / direct 均可加):
| 参数 | 默认 | 说明 |
|---|---|---|
--root |
整台机器(所有磁盘) | 文件管理允许访问的根目录;不指定则开放整盘(Windows 列出 C:/D:/… 各盘,Linux 从 / 起),指定后限制在该目录内(越界拒绝) |
--fps |
15 | 屏幕推流帧率 (1-30) |
--max-width |
1600 | 屏幕帧最大宽度(像素),0 表示不缩放(原始分辨率);用于控制带宽 |
--quality |
80 | JPEG 压缩质量 (1-100) |
--no-adapt |
关闭 | 关闭按网络质量自适应(固定使用上面的帧率 / 质量 / 分辨率) |
--allow-sleep |
关闭 | 允许系统空闲时自动锁屏 / 休眠(默认会阻止锁屏,见下) |
控制台侧也能实时调画质:会话工具栏点「画质」可自定义分辨率 / 质量 / 帧率, 并勾选「自动适应网络」。进入全屏会自动以原始分辨率 + 高画质 + 高帧率运行(关闭自适应), 退出全屏恢复为面板设定。这些设置通过
set_params消息下发到被控端。
示例:
# 仅允许访问 /data 目录
python -m client.main direct --port 9000 --token SECRET --fps 15 --max-width 1600 --quality 60 --root /data
# 开放整盘(默认):可浏览 / 下载目标机器任意磁盘文件
python -m client.main direct --port 9000 --token SECRET
⚠️ 必须以管理员(高完整性)运行,否则控制可能失效。 Windows 的 UIPI(用户界面特权隔离) 会丢弃低完整性进程用SendInput合成的键鼠事件。 当任务管理器 / 注册表等「以管理员运行」的窗口置顶时,普通权限的客户端注入的输入会被系统静默吞掉, 表现为「能看不能操作」,且重连无效(连接没断,是输入被吃了)。
- 源码运行:
client/main.py检测到非管理员会自动以 runas 重新拉起自身(触发一次 UAC)。- 打包产物:
build_client.py默认给 exe 加--uac-admin清单,启动即请求管理员权限。- 无桌面 / 服务场景不想弹 UAC:源码加
--no-elevate,或打包时加--no-uac-admin(此时需自行以管理员拉起)。
- 自动重连 + 会话续接:被控端断线后
relay/direct模式都会在数秒内自动重连;且增加心跳保活(有观看端时每 10s 发_hb,30s 内无回包即判定连接已死并主动重连),解决静默断网时async for一直 hang「卡着不重连」的问题。服务端中继在设备重连后自动恢复推流(仅移除设备、保留观看端),管理台不再需要手动重连。 - 前端自动重连:浏览器管理台 ws 断开(非手动点「断开」)后,2.5s 自动重连并自动恢复会话。
- 键盘组合键(Ctrl+S / Win+S 等):改为把每个键(含 Ctrl/Alt/Shift/Win 修饰键)的
down/up单独下发,由被控端维持修饰键按住状态来合成组合键。Win 键对应浏览器metaKey(映射为win),失焦时自动释放所有修饰键,避免卡在 Ctrl/Win 状态。(注:单独按 Win 键开开始菜单受浏览器/系统限制,通常抓不到 keydown;但 Win+其它键 的组合可正常触发。)
- 程序同目录(frozen exe / 脚本所在目录,回退到 cwd)放一个 JSON 配置文件,直接双击运行即自动按配置启动,不用每次敲命令行参数。
- 配置文件名(按此顺序查找):
py_rdesk-client.json→py_rdesk.json→config.json。 - 字段(均可选):
mode(relay/direct,必填)、server/token(relay)、host/port/token(direct)、root、fps、max_width、quality、no_adapt、no_reconnect、no_elevate。可用--config 路径显式指定。 - 命令行参数优先级高于配置文件:例如配置写了 server,但启动时又带了
--server,以命令行为准。 - 示例见仓库根
py_rdesk-client.example.json(复制改名即可)。
- Ctrl+C / 关闭终端不再抛一堆 traceback:被控端安装 SIGINT/SIGTERM 处理(Windows 用
signal.signal+call_soon_threadsafe回退,因为 Windows 事件循环不支持loop.add_signal_handler),收到信号即关 ws、取消后台任务、松开修饰键,干净退出。 - 服务端主动让客户端退出:在管理台会话工具栏点「退出客户端」(会二次确认),向被控端下发
{type:"exit"},被控端收到后优雅退出进程。中继/直连通用——服务端ws_view已透明转发该消息,无需改服务器。relay模式退出后不会陷在无限重连。
autostart子命令,独立于relay/direct:py_rdesk-client autostart --enable:开启。自动把当前配置文件的绝对路径烧进命令(--config),开机时无论 cwd 是哪里都能稳定找到配置;并强制--no-elevate(不弹 UAC)。- UAC 清单处理(关键):若 exe 是带
--uac-admin清单(requireAdministrator)的构建,注册表HKCU\Run在「无交互登录」时无法静默提权、进程直接起不来。因此--enable(即使你写--method registry)会自动改用计划任务/RL HIGHEST(静默以最高权限运行,无 UAC 弹窗)。非 UAC 构建(源码运行 /--no-uac-admin打包)才走注册表。 --method task(仅 Windows):显式用schtasks建「最高权限 / 登录即运行」计划任务(建任务本身需管理员权限)。py_rdesk-client autostart --disable:关闭(注册表与计划任务都会清理)。py_rdesk-client autostart --status:查看当前状态。
- 前置条件:自启动前必须先有真实配置文件(不能是
py_rdesk-client.example.json模板)。复制模板改名py_rdesk-client.json并填好mode/server/token,放到 exe 同目录(或项目根)即可。--enable时若找不到配置会明确警告,否则开机只会「静默秒退」让人误以为没生效。 - 这样「配置文件 + 自启动」组合,就能实现开机自动连上服务器,无需人工干预。
- 必须登录才能访问后台:中继服务器给管理台加了账号密码鉴权。未登录时访问任意页面会被 302 跳到
/login,访问任意/api/*接口返回 401。这样即使部署到公网,也不会「谁拿到链接都能看/操作」。- 登录状态用 HMAC-SHA256 签名的会话 Cookie(
httponly+samesite=lax,有效期 7 天),服务端不存 session,签名密钥每次启动随机生成(重启即让所有旧登录失效)。
- 登录状态用 HMAC-SHA256 签名的会话 Cookie(
- 账号密码来源:
- 用户名:
--admin-user,默认环境变量PY_RDESK_USER,再默认admin。 - 密码:
--admin-pass,默认环境变量PY_RDESK_PASS,都没设则每次启动随机生成并打印在启动横幅里。 - 启动横幅(
flush输出,服务方式运行也能在日志看到)示例:[py_rdesk] 服务已启动,后台需登录后访问: 登录用户名: admin 登录密码 : k3Ff9Q2a7 (随机生成,重启后失效) 授权 Token: RkrHlW6c... (随机生成,重启后失效)
- 用户名:
- Token 展示 + 一键复制:登录后侧边栏「服务信息」区直接显示本次服务的授权 Token,点「复制」即可拷到剪贴板,省去去命令行翻横幅。
- 一键下载可直接运行的客户端配置:点「⬇ 下载客户端配置文件」会下载一个
py_rdesk-client.json,其中server字段按当前访问地址自动推导(含反代场景:读X-Forwarded-Proto,HTTPS 反代自动给出wss://),token自动填入本次服务 Token,另附默认fps/max_width/quality等。把它放到被控端 exe 同目录双击即连,无需手填服务器地址和 Token。
现象:被控端锁屏后,远程观看端「看得到锁屏画面、却点不到那个『确定』」——这不是 bug,是 Windows 安全边界:锁屏运行在隔离的「安全桌面(Winlogon)」里,普通用户态进程(本项目用的 pyautogui/SendInput)无法把输入送达安全桌面。这跟 UAC 提权弹窗无法被远程点击是同一原因;没有内核驱动(TeamViewer / ToDesk 那种虚拟输入设备)或 Winlogon 钩子,纯 Python 工具做不到解锁锁屏。
因此事后点解锁无解,只能从源头不让它锁屏:
- 客户端已内置「连接期间阻止锁屏」:被控端一旦进入推流(
active)状态,会持续调用SetThreadExecutionState声明「系统 / 显示器需保持工作」,空闲计时器不会把机器锁掉。默认开启;若你确实想让机器能休眠,加--allow-sleep(或配置文件"allow_sleep": true)关闭该保护。 - 系统策略(彻底方案,需在被控机上做):仓库提供
scripts/disable_lock_screen.reg,双击导入后重启。它会把「交互式登录:计算机不活动限制」设为 0(永不因空闲自动锁屏)、并关闭锁屏界面显示。再配合下面两条,远程机器就永远停在桌面:- 自动登录:运行
control userpasswords2→ 取消「要使用本计算机,用户必须输入用户名和密码」→ 确定(无密码账户留空即可)。这样重启后自动回到桌面。 - 关闭休眠(避免机器自己睡死):
powercfg -change -standby-timeout-ac 0与powercfg -change -hibernate-timeout-ac 0。
- 自动登录:运行
- 注意:以上只防「空闲自动锁屏」。用户手动按 Win+L 属于安全注意力序列,用户态拦不了——无密码常驻远程的机器请避免使用 Win+L。
- 后台必须登录:管理台页面与所有
/api/*接口都需登录(会话 Cookie 用 HMAC 签名,7 天有效,密钥每次启动随机)。公网部署也不会「拿到链接就能看/操作」。账号用--admin-user/PY_RDESK_USER,密码用--admin-pass/PY_RDESK_PASS;不设密码则每次启动随机生成并打印在启动横幅。生产环境建议显式设置强密码。 - 所有 WebSocket 连接必须携带
?token=,与服务器/客户端的--token一致,否则被拒绝。(可在登录后的后台「服务信息」区一键复制 Token,或直接下载填好的客户端配置文件。) - 文件管理限制在
--root目录内,防止越权访问。 - 当前为明文 WebSocket;公网部署建议在前面加 TLS 反向代理(Nginx / Caddy),
并将
ws://升级为wss://(管理台会自动根据页面协议选择 ws/wss;下载的客户端配置也会自动用wss://)。 - 生产环境请使用强随机 token,并通过环境变量
PY_RDESK_TOKEN注入。
目标机器没有 Python 环境(如干净的虚拟机)时,可用内置脚本把被控端打包成单文件可执行程序:
# 在目标平台本机上运行(PyInstaller 不支持交叉编译)
python scripts/build_client.py- Windows 上产出
dist/py_rdesk-client.exe,Linux/macOS 上产出dist/py_rdesk-client。 - 脚本会自动创建隔离 venv、安装依赖(含懒加载的
mss/pyautogui/Pillow),再调用 PyInstaller 打包。 - 运行产物与源码完全一致,参数相同:
# Windows 虚拟机示例
py_rdesk-client.exe direct --port 9000 --token YOUR_STRONG_TOKEN
py_rdesk-client.exe relay --server ws://<服务器IP>:8000 --token YOUR_STRONG_TOKEN常用参数:
| 参数 | 说明 |
|---|---|
--name |
指定产出文件名(默认按平台命名) |
--onedir |
打包为单目录而非单文件(调试体积用) |
--skip-install |
跳过依赖安装(venv 已就绪时加速) |
--no-uac-admin |
打包的 exe 不请求管理员权限(仅运行时自动提权,适合无桌面/服务场景) |
--dry-run |
仅打印将执行的命令,不真正打包 |
说明:PyInstaller 无法跨平台编译,需在 Windows 上打
.exe、在 Linux 上打 ELF、在 macOS 上打 Mach-O。 各平台产物需在该平台运行(依赖系统图形/输入接口)。
客户端与管理台使用同一套 JSON 消息协议,服务器只做透传:
- 客户端 → 管理台:
info/frame/shell_out/file_list/file_chunk/file_done/ack/error/adapt - 管理台 → 客户端:
input/shell_exec/file_list_req/file_get/file_put/file_del/file_rename/qos - 服务器 ↔ 客户端:
start/stop(控制推流开关)
二进制帧(WebSocket 二进制消息,服务器透传):屏幕帧与文件分片走二进制,省去 base64 开销、避免大文件 atob 内存爆炸。二进制消息以 1 字节标签开头:
-
0x00+ JPEG 字节 = 一帧屏幕画面 -
0x01+ cid长度(2字节大端) + cid + 数据 = 一段文件下载分片(前端按 cid 归位,流式拼成 Blob) -
frame(JSON 文本)仅在分辨率 / 质量变化时下发元信息(meta:true,含w/h/q/fps)。 -
qos:观看端每秒上报实际收到的recv_fps/frame_bytes;被控端据此自动调帧率 / 质量 / 分辨率(带迟滞,避免抖动),并通过adapt回传当前参数,管理台在会话栏显示。画面静止时(跳帧)不会误判为网络拥塞。
输入坐标统一使用归一化值 [0,1],由被控端映射到真实屏幕分辨率,兼容不同观看端尺寸。
- 当前为单观看端 / 单设备会话;可扩展为多观看端广播、会话鉴权细化。
- 屏幕编码使用 JPEG 序列,已支持按网络质量自动调帧率 / 质量 / 分辨率(观看端测
recv_fps回传,被控端带迟滞调节)。 若需更低带宽,可在有桌面 / 硬件编码器的机器上接 H.264 / WebRTC 桥接(frame.codec已预留),作为高性能分支,当前默认仍走自适应 JPEG。 - 输入注入依赖
pyautogui,部分无头/Wayland 环境需额外适配。





