Qt远程控制双端源码实战:屏幕采集、输入注入与性能优化
简介这是一套基于Qt框架开发的Windows平台远程控制软件源码与可执行程序面向具备一定C与Qt基础、希望学习网络通信与远程桌面实现原理的开发者。资源包含服务器端被控端与客户端主控端两部分服务器端以进程形式后台运行客户端为图形界面程序连接时需指定服务器端IP地址及显示画面的宽高且宽高不能超过服务器端屏幕分辨率适合用于局域网远程协助、教学演示或二次开发。压缩包共40个文件以14个.h头文件与14个.cpp源文件为核心另含6个dll运行库、2个exe可执行程序、2个.pro工程文件及2个.user配置整体约6.08MB源码与编译产物齐备可直接运行或对照学习。目前已有5326人学习下载读者可从中掌握Qt网络编程、图像采集与传输、跨进程通信等关键实现思路并借助完整工程结构快速搭建自己的远程控制原型。1. 从零拆解一套 Qt 远程控制双端源码它到底能跑通哪些真实场景如果你手头正缺一套能直接编译、能看懂、能改的远程控制原型这套基于 Qt 的 Windows 双端源码值得花时间拆一遍。它把服务器端和客户端拆成两个独立工程服务器端负责采集屏幕、模拟输入、管理会话客户端负责连接、解码画面、回传键鼠事件。整套东西不依赖第三方远控框架核心通信和图像处理都写在 Qt 的 socket 与 QImage 体系里对想搞明白“远程桌面到底怎么把画面搬过去”的人来说比直接调一个封装库有价值得多。它适合三类人一是做内网运维工具、需要自己掌控通信链路的开发者二是学生或转行者想找一个完整 C/Qt 项目练手而不是写计算器三是已有产品雏形、需要参考屏幕分块传输和输入注入实现细节的工程师。下面按“能跑起来 → 看懂关键路径 → 避开翻车点 → 做进阶改造”的顺序拆每一步都落到具体文件和参数上。2. 双端工程结构与通信链路先搞清楚谁在传什么2.1 服务器端与客户端的职责边界这套源码最容易被误读的地方是很多人以为“服务器端”就是被控端。实际上在这类 Qt 远控实现里命名习惯通常是服务器端 被控主机上运行的程序负责抓屏和注入输入客户端 控制方负责显示画面和发送操作。两个工程各自有独立的main.cpp、.pro文件和一套TcpServer/TcpSocket封装。服务器端核心模块一般包括屏幕采集模块定时抓取桌面位图常见做法是用QScreen::grabWindow(0)或 Windows GDI 的BitBlt前者跨平台但帧率一般后者在 Windows 上更快。图像编码模块把QImage转成QByteArray可选 JPEG 压缩或原始 RGB 分块。网络发送模块基于QTcpServer监听端口接受客户端连接后按协议头发送帧数据。输入注入模块接收客户端发来的鼠标坐标、按键码通过SendInput或mouse_event还原操作。客户端核心模块对应为连接管理QTcpSocket连接指定 IP 和端口处理断线重连。帧解析按协议头读取长度字段拼包后交给解码器。画面渲染QLabel::setPixmap或自绘QWidget::paintEvent显示。事件采集重写mouseMoveEvent、keyPressEvent把本地事件序列化后发回服务器端。理解这个边界之后再看代码就不会把“谁发谁收”搞反。常见做法是在协议头里放一个 4 字节的魔数加 4 字节长度接收端先读 8 字节判断包类型再按长度收完整帧。2.2 一次完整交互的数据流把一次鼠标点击拆开看链路是这样的客户端捕获mousePressEvent拿到相对画面控件的坐标(x, y)。客户端把坐标按当前缩放比例还原成服务器端真实分辨率坐标。序列化成固定结构比如[类型:1字节][x:4字节][y:4字节][按键:1字节]。通过QTcpSocket::write发出。服务器端readyRead触发按同样结构解析。调用SendInput或SetCursorPosmouse_event注入。服务器端下一帧抓屏时画面里就带上了这次点击的效果。这个流程里最容易出问题的是第 2 步的坐标还原。如果客户端显示控件被拉伸过而代码里直接拿控件坐标当屏幕坐标用点上去就会偏。正确做法是记录原始帧的宽高用realX x * frameWidth / widgetWidth换算。2.3 编译前必须确认的环境参数这套工程对 Qt 版本和编译套件有要求踩错版本会直接卡在链接阶段。建议按下面这张表核对项目建议值说明Qt 版本Qt 5.12 及以上低于 5.12 时QScreen相关接口不完整编译套件MSVC 2017/2019 或 MinGW 7.3服务器端输入注入依赖 Windows APIMSVC 更稳模块依赖core gui widgets network.pro里必须带QT network系统Windows 10/11输入注入接口在 Windows 上才有效权限管理员运行否则无法向高权限窗口注入输入.pro文件里典型配置如下QT core gui widgets network CONFIG c11 TARGET RemoteServer TEMPLATE app SOURCES main.cpp \ screenserver.cpp \ inputinjector.cpp HEADERS screenserver.h \ inputinjector.h这段配置里network是必须的漏掉会报QTcpServer未定义。c11是因为源码里用了 lambda 连接信号槽。如果编译时报SendInput找不到检查是否包含了windows.h并且链接了user32.lib。3. 屏幕采集与图像传输帧率、压缩和分块怎么调3.1 抓屏方式的选型与实测差异Qt 里抓屏有两条路QScreen::grabWindow和 Windows 原生BitBlt。前者写起来简单三行代码就能拿到QPixmap后者需要建兼容 DC但速度快不少。源码里如果用的是QScreen在 1080p 下大概能跑到 10 到 15 帧换成BitBlt后同样分辨率能到 25 帧以上。QScreen抓屏的核心代码QScreen *screen QGuiApplication::primaryScreen(); QPixmap pixmap screen-grabWindow(0); QImage image pixmap.toImage(); // 转成 RGB888 便于后续压缩避免带 alpha 通道浪费带宽 QImage rgbImage image.convertToFormat(QImage::Format_RGB888);逻辑说明grabWindow(0)里的 0 表示抓整个桌面。convertToFormat这步不能省默认的Format_ARGB32每像素 4 字节转成RGB888后每像素 3 字节1080p 一帧能省下约 2MB。参数上如果只关心变化区域可以在抓屏前记录上一帧用QImage::operator或逐块比对只发差异块。3.2 JPEG 压缩质量与带宽的平衡原始 RGB 数据太大必须压缩。源码里常见做法是用QImage::save到QBuffer格式选 JPEG质量参数 60 到 80 之间。质量 80 时 1080p 一帧约 150KB质量 60 时约 80KB但文字边缘会发虚。QByteArray ba; QBuffer buffer(ba); buffer.open(QIODevice::WriteOnly); // 质量 70 是清晰度和体积的折中点再低文字就糊了 rgbImage.save(buffer, JPEG, 70); QByteArray frameData ba;参数说明第三个参数是质量范围 0 到 100。内网千兆环境下可以开到 85跨公网或无线环境建议 60 到 70。如果画面以文字为主JPEG 的块效应会很明显这时可以改用 PNG 或分块传输原始数据代价是带宽上升。3.3 协议头设计与粘包处理TCP 是流式协议不处理粘包就会出现“画面撕裂”或“解析错位”。源码里一般会定义一个固定头// 协议头4 字节魔数 4 字节数据长度 QByteArray header; QDataStream stream(header, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::BigEndian); stream (quint32)0x52454D4F; // REMO stream (quint32)frameData.size(); socket-write(header); socket-write(frameData);接收端对应逻辑// 先收 8 字节头再按长度收正文 if (buffer.size() 8) return; QDataStream stream(buffer); stream.setByteOrder(QDataStream::BigEndian); quint32 magic, length; stream magic length; if (magic ! 0x52454D4F) { buffer.clear(); return; } if (buffer.size() 8 length) return; QByteArray frame buffer.mid(8, length); buffer.remove(0, 8 length);这段代码的关键是buffer要作为成员变量跨次readyRead累积不能每次清空。BigEndian要两端一致否则长度字段会解析成天文数字。如果发现客户端画面卡住不动先打印length看是不是异常大通常是字节序反了。4. 输入注入与坐标映射点击不准的根因都在这4.1 Windows 输入注入接口的选择服务器端收到坐标和按键后有两种注入方式mouse_event和SendInput。前者是老接口简单但已被标记为废弃后者是推荐方式支持多屏和更精细的控制。源码里如果用mouse_event在部分高 DPI 场景下会偏移。SendInput的典型用法INPUT input {0}; input.type INPUT_MOUSE; input.mi.dx (LONG)(x * 65535 / screenWidth); // 归一化到 0-65535 input.mi.dy (LONG)(y * 65535 / screenHeight); input.mi.dwFlags MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; SendInput(1, input, sizeof(INPUT));参数说明MOUSEEVENTF_ABSOLUTE表示使用绝对坐标此时dx、dy需要归一化到 0 到 65535 之间而不是直接填像素值。这是最容易翻车的地方直接填像素会导致鼠标飞到屏幕角落。screenWidth和screenHeight要用GetSystemMetrics(SM_CXSCREEN)获取不能硬编码。4.2 客户端坐标到服务器坐标的换算客户端显示画面时如果控件大小和原始帧不一致必须做比例换算。假设原始帧是 1920x1080客户端控件是 960x540那么// widgetPos 是鼠标在控件内的坐标 double scaleX (double)frameWidth / widget-width(); double scaleY (double)frameHeight / widget-height(); int realX (int)(widgetPos.x() * scaleX); int realY (int)(widgetPos.y() * scaleY);如果控件保持了宽高比scaleX和scaleY相等直接用其中一个即可。如果控件被自由拉伸两个方向比例不同必须分别算。常见错误是只算了一个方向导致横向或纵向点击偏移。另外如果画面有黑边letterbox还要先减去黑边偏移再换算。4.3 键盘事件的序列化与还原键盘比鼠标麻烦因为要处理按下和抬起两个状态还要区分普通键和扩展键。源码里一般用Qt::Key枚举值传输服务器端再映射回虚拟键码。// 客户端发送 QByteArray keyData; QDataStream stream(keyData, QIODevice::WriteOnly); stream (quint8)MSG_KEY; stream (quint32)event-key(); stream (quint8)(event-type() QEvent::KeyPress ? 1 : 0); socket-write(keyData);服务器端还原时Qt::Key_A到Qt::Key_Z可以直接映射到0x41到0x5A但功能键、方向键需要单独查表。KEYEVENTF_KEYUP标志用于抬起事件漏掉会导致按键“卡住”。如果发现远程端一直重复输入某个字符先检查抬起事件有没有发出去。5. 避坑与排查这套源码最容易翻车的五个地方5.1 现象编译通过但运行闪退无任何提示原因服务器端在main里创建QTcpServer前就调用了输入注入相关 API或者.pro里漏了network模块导致运行时找不到符号。另一种常见情况是 Qt 版本和编译器不匹配比如用 MinGW 编译的库去链接 MSVC 编译的 Qt。解决先在main函数第一行加qDebug() start;确认程序能进到主循环。如果连这行都不打印说明是链接期问题检查 Qt 套件是否和编译器一致。如果打印了但后面崩用QApplication::aboutToQuit加日志定位到具体模块。5.2 现象客户端能连上但画面一直黑屏原因服务器端抓屏返回了空QPixmap常见于程序没有以管理员权限运行或者运行在远程桌面会话里被系统限制。另一个原因是抓屏线程和网络发送线程共用了QImage没有做深拷贝数据被下一帧覆盖。解决先判断pixmap.isNull()如果是空就打印错误并跳过这一帧。跨线程传递QImage时用QImage::copy()做深拷贝或者用QMetaObject::invokeMethod切回主线程处理。权限问题就右键以管理员身份运行。5.3 现象鼠标点击位置整体偏移一个固定值原因高 DPI 缩放。Windows 在 125% 或 150% 缩放下GetSystemMetrics返回的是逻辑坐标而抓屏拿到的是物理像素两者不一致。源码里如果没有调用SetProcessDPIAware就会产生固定偏移。解决在main函数最前面调用SetProcessDPIAware()让进程感知真实分辨率。或者在.pro里加QMAKE_LFLAGS /MANIFESTUAC:levelasInvoker uiAccessfalse并配置 DPI 感知清单。调用之后重新获取screenWidth和screenHeight偏移就会消失。5.4 现象传输几帧后连接断开原因单帧数据超过 TCP 缓冲区或者发送频率太高导致对端处理不过来。JPEG 质量开到 95 时1080p 一帧可能超过 500KB连续发送会撑爆缓冲区。另一个原因是write之后没有等待bytesWritten就继续写数据堆积。解决在服务器端加一个发送队列用QTcpSocket::bytesToWrite判断积压量超过阈值就跳过当前帧。质量参数降到 70 以下。客户端接收端用readAll配合累积缓冲区不要假设一次readyRead就是完整一帧。5.5 现象键盘输入乱码或功能键无效原因Qt::Key枚举值和 Windows 虚拟键码不是一一对应直接强转会导致功能键错乱。另外中文输入法状态下keyPressEvent拿到的是拼音字母而不是最终字符。解决建一张映射表把Qt::Key_F1到Qt::Key_F12、方向键、Home、End等单独处理。输入法问题可以在客户端拦截inputMethodEvent或者提示用户在远程操作时切换到英文输入法。如果只是普通字符输入用event-text()拿实际字符比用event-key()更可靠。6. 进阶改造把帧率从 15 提到 30 的两个具体手段第一个手段是改抓屏方式。把QScreen::grabWindow换成 Windows 的BitBlt配合CreateCompatibleDC和CreateCompatibleBitmap在 1080p 下实测能省掉约 40% 的抓屏耗时。核心代码结构是先建内存 DC把屏幕 DC 的内容BitBlt过去再从位图里取像素数据。注意每次抓完要DeleteObject释放位图否则跑几分钟就内存泄漏。第二个手段是只传变化区域。把画面切成 64x64 的块每帧和上一帧做逐块比对只把有差异的块编码发送。客户端维护一份完整的帧缓冲收到差异块后更新对应位置。这样在静态画面下带宽能降到原来的十分之一。代价是实现复杂度上升需要处理块边界和客户端缓冲同步。验证改造效果的方法很简单在服务器端每帧结束时打印抓屏耗时和编码耗时客户端打印接收帧率和解码耗时。如果抓屏耗时占比超过 50%优先换BitBlt如果编码耗时高降 JPEG 质量或改分块。我自己的习惯是每次改完参数都跑一遍 1080p 静态画面和 1080p 视频播放两个场景前者看带宽后者看帧率两个都达标才算调好。从那以后我每次动传输参数都强制走一遍这两个场景省得上线后才发现某一类画面卡成幻灯片。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

OpenHands Software Agent SDK快速上手:10分钟构建你的第一个AI Agent

OpenHands Software Agent SDK快速上手:10分钟构建你的第一个AI Agent

【免费下载链接】software-agent-sdk A clean, modular SDK for building AI agents with OpenHands V1. 项目地址: https://gitcode.com/gh_mirrors/so/software-agent-sdk 点击查看 免费下载 OpenHands Software Agent SDK 是 OpenHands 推出的开源 Python SDK&a…

2026/10/14 17:52:15 阅读更多 →
估计目标框架落地指南:从ICH E9(R1)到敏感性分析实践

估计目标框架落地指南:从ICH E9(R1)到敏感性分析实践

简介:在临床试验统计中,估计目标(Estimand)并非一种新的统计方法,而是对治疗效应定义与分析逻辑的重构。ICH E9(R1)增补指导原则要求研究者先明确“估计什么样的治疗效应”,再选择模型与缺失数据处理策略&a…

2026/10/14 17:52:15 阅读更多 →
C语言读写STL文件:从格式解析到实战避坑指南

C语言读写STL文件:从格式解析到实战避坑指南

去年接手一个模拟项目X的时候,客户甩过来一批STL格式的三维模型文件,要求统计每个模型的三角面片数量、体积和包围盒。我一开始以为是C标准模板库,差点用错工具链,搞清楚之后才发现这里的STL是3D打印和CAD领域最常见的网格文件格式…

2026/10/14 17:52:15 阅读更多 →

最新新闻

纯C语言实现LSTM:从张量设计到嵌入式部署的完整指南

纯C语言实现LSTM:从张量设计到嵌入式部署的完整指南

简介:这是一份面向C语言开发者与神经网络初学者的LSTM递归神经网络实现资源,旨在帮助读者在资源受限的环境中理解并实践循环网络。项目参考Andrej Karpathy的char-rnn思路,但改用C语言重写,可用于文本生成等长期依赖建模任务&…

2026/10/14 18:46:02 阅读更多 →
2026 年西安员工持股平台有限合伙架构会带来哪些长期管理问题

2026 年西安员工持股平台有限合伙架构会带来哪些长期管理问题

2026年西安企业设立员工持股平台并采用有限合伙架构,需直面四大长期管理问题,分别为合伙人权责约束、税务合规衔接、控制权稳定性、本地征管适配风险。以下将结合西安2026年的财税征管规则与有限合伙架构的法定要求,明确各类问题的核心内容与…

2026/10/14 18:46:02 阅读更多 →
从补丁文件名到OPatch:WebLogic 12.1.3安全补丁安装与回滚完整指南

从补丁文件名到OPatch:WebLogic 12.1.3安全补丁安装与回滚完整指南

简介:WebLogic Server 12.1.3补丁p33385024_121300_Generic.zip面向WebLogic系统运维与管理人员,用于修复12.1.3版本已知问题并提升稳定性和安全性,且是后续补丁p33494824_121300_Generic.zip安装的必要前置条件,任何平台均可使用…

2026/10/14 18:46:02 阅读更多 →
Unity配置表链路重构:从JSON反射到自研二进制读表

Unity配置表链路重构:从JSON反射到自研二进制读表

上个月某个版本迭代,线上曝出一个很诡异的问题:活动配置里的奖励数值怎么都不生效,玩家领取到的跟后台配置的完全是两套数。排查到最后,定位到旧 JSON 解析器在真机上对某种类型转换的兼容性差异,一个字段静默错位&…

2026/10/14 18:46:02 阅读更多 →
工业机器人现场调试全流程详解:从硬件检查到工艺优化

工业机器人现场调试全流程详解:从硬件检查到工艺优化

把一台机器人从静止状态变成一条产线上的稳定劳动力,中间隔着一条完整的机器人现场调试流程。这句话我想放在最前面,是因为太多人把调试理解成“把程序灌进去、按下自动、看它动起来”,结果真到了现场,信号对不上机器人不动&#…

2026/10/14 18:46:02 阅读更多 →
W5500 TCP Server连接偶发断开:Socket状态机与重连机制深度解析

W5500 TCP Server连接偶发断开:Socket状态机与重连机制深度解析

简介:针对W5500 TCP服务器在物联网、嵌入式设备联网中频繁掉线的问题,这份工程资源包提供了完整的解决思路与可运行代码,面向嵌入式开发、物联网设备联网及网络通信调试人员,重点讲解如何通过TCP Keep-Alive机制维持长连接&#x…

2026/10/14 18:45:01 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →