积累沉淀

待山花烂漫,化茧成蝶

RemoteCtrl项目

前言:RemoteCtrl 是一个基于 C++20 协程的远程控制软件,支持文件管理、远程桌面监控、远程终端控制等功能。客户端使用 Qt5 GUI,服务端使用 coronet 异步 I/O 协程库。


RemoteCtrl

RemoteCtrl- Github||Gitee

功能特色

  • 文件管理:远程浏览文件目录树,支持文件下载(断点续传)、删除、远程打开
  • 远程桌面监控:实时屏幕共享(30 FPS),鼠标事件转发,支持锁屏/解锁
  • 远程终端控制:Lock/Unlock 远程机器的键盘鼠标
  • 守护进程模式:支持开机自启、后台静默运行(-DBACKGROUND=ON
  • 跨平台通信:自定义二进制协议,CRC32C 校验,支持大文件传输
  • 告别编码问题:全链路 UTF-8,中文文件名无乱码

架构设计

1
2
3
4
5
6
┌────────────────────────────┐      TCP       ┌─────────────────────────┐
│ RemoteCtrlApp (Client) │ ◄─────────────► │ RemoteCtrlServer │
│ Qt5 Widgets · C++20 │ 自定义二进制协议 │ C++20 · coronet 协程 │
│ coronet 协程(独立I/O线程)│ │ Qt5::Core (文件操作) │
│ Windows 10+ (x64) │ │ Windows / Linux │
└────────────────────────────┘ └─────────────────────────┘

客户端线程模型

1
2
3
4
5
6
7
┌──────────────────────────┐    mutex 队列     ┌───────────────────────┐
│ Qt 主线程 (UI 事件循环) │ ◄──────────────► │ coronet I/O 线程 │
│ MainWindow │ Packet in/out │ writerLoop (发送) │
│ MonitorWindow │ │ readerLoop (接收) │
│ DownloadDialog │ │ io_context 事件循环 │
│ 每 50ms 轮询接收队列 │ │ │
└──────────────────────────┘ └───────────────────────┘

服务端线程模型

1
2
3
4
5
6
7
8
9
10
11
┌──────────────────────────────────────────────┐
│ io_context (单线程事件循环) │
│ │
│ ┌──────────────┐ ┌───────────────────┐ │
│ │ acceptLoop │ │ SessionHandler │ │
│ │ (接收连接) │ │ (每个客户端) │ │
│ └──────────────┘ └───────────────────┘ │
│ ├─ readLoop (接收) │
│ ├─ 命令分发 │
│ └─ screenStream (截图) │
└──────────────────────────────────────────────┘

技术选型

组件 技术 说明
异步 I/O coronet C++20 协程库,跨平台 IOCP/epoll/io_uring,零虚表开销
GUI Qt 5.12 Widgets 稳定的桌面 UI 框架
构建系统 CMake 3.20+ 支持 MSVC,自动管理依赖
编译器 MSVC 2022 C++20 标准
文件操作 Qt5::Core (QFile/QDir) 统一 UTF-8 编码
JPEG 编码 WIC (Windows Imaging Component) 硬件加速图像编码
日志 自研轮转文件日志 写入 %%TEMP%%/RemoteCtrlServer/,10MB 轮转

核心代码逻辑

通信协议

自定义二进制协议,所有操作通过同一套包格式完成:

1
2
3
4
5
┌─────────────────────────────────────────────────────────┐
│ Header (16B) │ Command (4B) │ Payload Length (8B) │ CRC32C (4B) │
│ magic=0x524D │ cmd ID │ payload size │ 校验和 │
│ 5443 ("RMTC") │ │ │ │
└─────────────────────────────────────────────────────────┘
  • 校验:CRC32C(SSE4.2 硬件加速,32-bit 兼容回退)
  • 字符串:长度前缀 [uint16_t len][UTF-8 data]
  • 命令集:Ping/Pong、ListDrives、ListDir、DeleteFile、DownloadReq/Start/Resp/Complete/Cancel、OpenFile、ScreenFrame、MouseEvent、LockScreen 等

数据通信核心流程

连接与文件浏览

1
2
3
4
5
6
7
Client                      Server
│ │
│──── TCP Connect ────────► │
│──── LIST_DRIVES ────────► │
│◄──── DRIVE_LIST ───────── │ ← 枚举磁盘 (GetLogicalDriveStrings)
│──── LIST_DIR ("C:/") ───► │
│◄──── DIR_ENTRIES ──────── │ ← 目录/文件分离,子目录加树,文件显示在右侧

文件下载(断点续传)

1
2
3
4
5
6
7
8
9
10
11
12
Client                      Server
│ │
│──── DOWNLOAD_REQ ───────► │ path + offset
│◄──── DOWNLOAD_START ───── │ file_size + name
│◄──── DOWNLOAD_RESP ────── │ 64KB 数据块 × N
│◄──── DOWNLOAD_COMPLETE ── │ 传输完成
│ │
│ (断线重连后) │
│──── DOWNLOAD_REQ ───────► │ offset = 已接收字节数
│◄──── DOWNLOAD_START ───── │ file_size + name
│◄──── DOWNLOAD_RESP ────── │ 从偏移处继续
│◄──── DOWNLOAD_COMPLETE ── │

屏幕共享

1
2
3
4
5
6
7
8
9
Client                      Server
│ │
│──── SCREEN_START ───────► │
│◄──── SCREEN_FRAME ─────── │ JPEG 编码的桌面截图, ~20 FPS
│◄──── SCREEN_FRAME ─────── │ 持续推送
│──── SCREEN_STOP ────────► │
│ │
│ (鼠标事件转发) │
│──── MOUSE_EVENT ────────► │ SendInput 模拟鼠标

服务端截图流水线

1
GDI BitBlt → 32-bit BGRA → BGR(24-bit) 转换 → WIC JPEG 编码 → 数据包发送

错误处理机制

所有错误通过 CmdError(0xFFFF)单一通道 + ErrorCode 枚举:

错误码 场景 客户端表现
FileNotFound (1) 文件不存在 Warning 提示
PermissionDenied (2) 权限不足 Warning 提示
FileBusy (8) 文件被占用 Warning 提示
ReadError (4) 读取失败 Warning 提示

错误响应自动转发到对应的对话框(下载/打开),用户确认后自动关闭。


构建说明

环境要求

  • Visual Studio 2022
  • CMake 3.20+
  • Qt 5.12.12 (msvc2017_64)
  • Git

编译

1
2
3
4
5
6
7
# 普通模式(控制台 + 文件日志)
cmake -B build
cmake --build build --config Release

# 守护进程模式(后台运行,无控制台,开机自启)
cmake -B build -DBACKGROUND=ON
cmake --build build --config Release

运行

1
2
3
4
5
6
7
8
9
10
11
# 服务端
build/server/Release/RemoteCtrlServer.exe [port]

# 客户端 默认端口 8888
build/client/Release/RemoteCtrlApp.exe

# 守护进程模式 - 安装开机自启并启动 不指定port:默认端口 8888
build/server/Release/RemoteCtrlServer.exe [port] --install

# 守护进程模式 - 卸载开机自启 不指定port:默认端口 8888
build/server/Release/RemoteCtrlServer.exe [port] --uninstall

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
RemoteCtrl/
├── CMakeLists.txt # 顶级构建配置
├── cmake/
│ └── FindCoronet.cmake # find_package → add_subdirectory 回退
├── common/ # 共享库 (remotectrl_common)
│ ├── Protocol.hpp # 命令枚举、错误码、常量
│ ├── Checksum.hpp/.cpp # CRC32C 校验
│ ├── Packet.hpp/.cpp # 包序列化/反序列化
│ └── Log.hpp # 统一日志模块(轮转文件输出)
├── server/ # RemoteCtrlServer
│ ├── RemoteCtrlServer.cpp # 入口 + daemon + autostart
│ ├── ServerApp.hpp/.cpp # acceptor 循环
│ ├── SessionHandler.hpp/.cpp # 协程读写 + 命令分发
│ ├── FileManager.hpp/.cpp # Qt 文件操作
│ ├── ScreenCapture.hpp/.cpp # GDI 截图 + WIC JPEG
│ └── InputForwarder.hpp/.cpp # 鼠标转发 + 锁屏
└── client/ # RemoteCtrlApp (Qt5)
├── RemoteCtrlApp.cpp
├── MainWindow.hpp/.cpp # 主界面 + 包路由分发
├── ClientNetwork.hpp/.cpp # coronet 收发分离 + Qt 桥接
├── MonitorWindow.hpp/.cpp # 远程桌面 + 鼠标转发
├── DownloadDialog.hpp/.cpp # 下载进度 + 错误弹窗
├── FileBrowserWidget.hpp/.cpp # 目录树(懒加载)
└── FileListWidget.hpp/.cpp # 文件列表 + 右键菜单

License

MIT

Buy me a coffee please.