Skip to content

Repository files navigation

py_rdesk · 跨平台远程桌面控制系统

一套用 Python 实现的轻量远程桌面控制方案:

  • 被控端(客户端):简洁稳定,跨平台(Windows / Linux / macOS),负责屏幕采集、输入注入、命令执行与文件管理。
  • 中继服务器:托管现代化后台管理界面,纳管在线设备、在管理台与客户端之间透传数据。
  • 管理台(Web):现代化、自包含(无外部 CDN)的控制台,支持远程观看 / 控制、终端命令、文件管理。

支持两种连接形态:

  1. 中继模式:客户端连接在线服务器,管理台通过网页统一查看和控制所有在线设备。
  2. 直连模式:客户端自带 WebSocket 服务,管理台用 IP:端口 + 授权 token 直接连接,无需服务器。

功能一览

功能 说明
在线设备纳管 服务器维护设备列表(主机名 / OS / 用户 / 架构 / 核数)
远程观看 实时 JPEG 帧推流(二进制 WebSocket 帧,省去 base64 约 33% 开销),关键帧 + 区块增量(delta) 传输:客户端保留上一帧、按 64×64 区块比对差异,仅把变化区块编码发送,前端在底图上贴变化区域;静止画面自动跳帧。带宽从「整帧×fps」降到「变化区×fps」,全屏高画质高帧率下延迟与带宽占用大幅下降;按网络质量自动调帧率 / 质量 / 分辨率;控制台可实时调画质、全屏自动拉满
远程控制 鼠标移动 / 点击 / 右键 / 滚轮、键盘输入(含组合键)
终端 在被控端执行 shell 命令并实时回显输出(按系统编码解码,中文不乱码)
文件管理 浏览 / 上传 / 下载 / 删除 / 重命名;默认开放整台机器所有磁盘(指定 --root 则限制在该目录内),大文件以二进制分片流式传输
双连接模式 中继服务器 / 客户端直连,同一套协议

效果预览

从登录到远程控制、终端命令、文件管理——全流程截图:

1. 登录

后台管理台需登录后才能访问(账号密码在启动服务时指定或随机生成)。

登录

2. 仪表盘

登录后的主界面:左侧在线设备列表 + 直连入口,右侧为操作区(未选设备时显示使用指引)。顶部可查看/复制授权 Token、一键下载客户端配置文件。

仪表盘

3. 远程控制(屏幕)

点击设备的「控制」按钮进入远程桌面画面。支持鼠标移动 / 点击 / 右键 / 滚轮、键盘输入(含组合键),可切换「控制」与「观看」模式。会话工具栏提供画质调节 / 全屏 / 断连等操作。

远程控制

4. 终端

在被控端执行 shell 命令并实时回显输出(按系统编码解码,中文不乱码)。Windows 使用 ConPTY (winpty),Linux/macOS 使用 pty,体验接近 SSH。

终端

5. 文件管理

浏览被控端磁盘目录结构,支持上传 / 下载 / 删除 / 重命名文件。大文件以二进制分片流式传输,带进度条显示。默认开放整台机器所有磁盘(可通过 --root 限制访问目录)。

文件管理

6. 画质设置

会话工具栏「画质」面板可实时调整分辨率、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 等)。 服务器本身不需要图形库。


快速开始

1) 启动中继服务器

# 固定授权 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,或一键下载即用的客户端配置文件(见下)。

2) 启动被控端(中继模式)

python -m client.main relay --server ws://<服务器IP>:8000 --token YOUR_STRONG_TOKEN

客户端会连上服务器并出现在后台「在线设备」列表中。

3) 直连模式(不依赖服务器)

在被控机上:

python -m client.main direct --port 9000 --token YOUR_STRONG_TOKEN

在后台管理台左侧「直连设备」填入被控机 IP、端口 9000 与同一 token,点击连接即可。 (确保网络可达该端口。)

4) 开始控制

在后台点击设备的「远程控制」:

  • 顶部切换 控制 / 观看 模式。控制模式下在画面区域操作鼠标键盘即作用于被控端。
  • 「终端」标签执行命令;「文件」标签浏览、上传、下载、删除、重命名文件。

配置参数

被控端通用参数(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.jsonpy_rdesk.jsonconfig.json
  • 字段(均可选):moderelay/direct,必填)、server/token(relay)、host/port/token(direct)、rootfpsmax_widthqualityno_adaptno_reconnectno_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 签名的会话 Cookiehttponly + samesite=lax,有效期 7 天),服务端不存 session,签名密钥每次启动随机生成(重启即让所有旧登录失效)。
  • 账号密码来源
    • 用户名:--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 0powercfg -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 环境需额外适配。

About

一套用 Python 实现的轻量远程桌面控制方案

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages