用Qt从零手写远程控制:屏幕传输、事件注入与TCP通信全解析
简介这是一套基于Qt开发的Windows远程控制软件包含服务器端被控端与客户端主控端两个部分。服务器端以进程形式运行客户端为图形界面程序连接时需指定服务器IP以及显示宽度和高度。面向需要学习Qt网络编程、远程桌面交互或Windows平台Socket通信的开发者适合具备一定C与Qt基础、希望参考完整项目结构的中级学习者。资源共40个文件主要为14个C源文件和14个头文件覆盖客户端与服务器端的功能实现另有编译后的2个exe可直接运行以及配套的6个Qt运行库dll和工程文件。压缩包约6.08MB代码量精炼便于对照阅读和二次修改。目前已有5300余人学习下载适合用来理解远程控制软件的基础流程包括连接建立、屏幕数据传输与界面显示等关键环节。通过源码和可运行程序可以快速查看或测试实际效果也能在此基础上扩展文件传输、按键控制等更多功能。1. 一个用 Qt 从零手写的 Windows 远程控制项目服务器端和客户端都有第一次拿到这份源码时我其实有点怀疑——Qt 做桌面应用界面是出了名的顺手但拿它来写远程控制这种对实时性和底层 API 依赖都很高的工具真的能行吗把代码全部过了一遍之后我的结论变了这不仅是能行而且整个工程结构还相当清爽。项目分为服务器端被控端和客户端控制端两个完整程序基于 TCP 协议进行通信核心链路围绕屏幕画面实时传输、键鼠事件转发、文件传输三个模块展开代码量不算大但涉及的关键点很全面。这套项目适合两类人第一类是刚开始接触 Qt 网络编程和 C/S 架构、想找个完整实战范例的开发者你能看到一条从 TCP 长连接、QImage 图像编码到事件模拟注入的完整链路没有绕路第二类是准备做跨平台远程协作、内部工具或上课演示的小团队直接在它基础上加功能比从零搭要省太多时间。我二次开发过类似的工具最深的感受是远程控制这类项目最麻烦的并不是思路而是数据包设计、网络粘包处理、图像压缩和事件同步这些细节。而这份源码恰恰把这些核心环节都覆盖到了我会在接下来的章节里把它们逐层拆给你看。2. 项目定位与技术底座为什么用 Qt核心架构如何分层2.1 技术栈Qt 框架的哪些模块扛起了整个远程控制链路先明确这份资源的“骨架”。它不是一个简单的 Demo而是一个完整的、可编译运行的 Qt 项目里面同时包含两个工程一个是被控端服务器程序以下简称 Server一个是控制端客户端程序以下简称 Client。两者既可以运行在同一台局域网内的不同机器上也支持跨网段连接前提是网络可达并处理好防火墙和端口映射。核心语言是 C界面层全部由 Qt Widgets 绘制不依赖第三方界面库。在 Qt 模块的选择上项目用到的都是高频实用组件Qt Network提供 QTcpServer、QTcpSocket承担两端之间的 TCP 长连接管理包括监昕、连接、断开重连和错位处理。Qt Gui Qt Widgets服务端用于截取屏幕并封装成 QImage客户端用于展示画面QLabel 或自定义 QWidget和渲染控制按钮。QDataStream / QByteArray用于数据包的序列化和粘包处理这是整个通信设计中最关键的部分后面我会详细讲。Qt 事件系统客户端接收鼠标键盘指令后通过 QMouseEvent、QKeyEvent 的构造与发送实现本地事件模拟有时也会配合 Windows API如 SendInput来提升兼容性。局域网测试场景下画面传输延迟大概在可接受范围内具体数字跟分辨率、压缩参数、网卡状况都有关系。如果你把图像质量参数调低、传输分辨率下调在千兆局域网里跑出流畅的远程操作是完全没有问题的。2.2 服务器端与客户端的角色划分和通信模型这个项目采用经典的 C/S 模型简单说就是“服务端暴露能力客户端发起协作”。整个架构大致可以分成三层网络传输层负责建立连接、维持心跳、按自定义协议收发数据。核心由 QTcpServer 和 QTcpSocket 实现。数据封装层负责把屏幕图像、鼠标键盘操作、文件内容转换为遵循特定结构的二进制流并在收到数据后反向解析。这里的“封装”不仅是打包还要解决 TCP 粘包和半包问题。业务逻辑层服务端负责屏幕捕获与指令执行客户端负责画面渲染与指令采集。下面一张简表可以快速帮你对应模块职责功能模块服务器端被控端客户端控制端网络监听QTcpServer 监听指定端口QTcpSocket 主动连接屏幕传输截屏 → 压缩 → 发送接收 → 解压 → 渲染鼠标键盘接收指令 → 注入本地事件捕获输入 → 发送指令文件传输接收文件数据并落盘选择文件 → 流式发送熟悉远程控制软件原理的开发者可能已经注意到这套分工结构与市面上的主流方案其实是一致的。区别在于这个项目的一切都是自研的没有使用第三方云服务器中转也没有依赖系统自带的远程桌面协议。它对网络环境的假设是两端网络互通如果你需要部署到公网通常还需要自行配置端口映射或内网穿透。2.3 目录结构与关键类职责一览拿到源码解压之后你第一件事应该是建立对整体目录的直觉。正常情况下这个项目会按功能拆分成两个子目录分别对应服务端和客户端外加一些公共文件。我建议你优先关注以下文件Server服务端工程main.cpp程序入口serverwindow主窗口类负责初始化监听和显示连接状态screenlistener屏幕捕获逻辑定时抓取画面并编码netserver封装 QTcpServer管理连接和收发包Client客户端工程main.cpp程序入口clientwindow主窗口类包含连接配置和画面展示netclient封装 QTcpSocket处理连接与通信inputhandler采集并序列化鼠标、键盘事件如果你在源码中看到的命名和我描述的不完全一样不用慌每个工程的目录结构在命名习惯上都有差异你只需要按“谁负责监听、谁负责发包、谁负责解析”的线索去对应就能快速定位到核心代码。2.4 通信协议设计背后的核心权衡这一节是理解整个项目的钥匙。你可能已经知道TCP 是面向字节流的协议本身不区分消息边界。如果发送方把两个数据包连续推入 socket接收方可能会在第一次 read 时同时拿到两条消息的内容也可能一条消息被拆成两次到达。如果不做特殊处理远程控制这个场景几乎必然出现“画面花屏”“鼠标点击执行错位”等让人崩溃的问题。这份源码的解决思路是自定义一套“包头 载荷”格式。每个数据包的开头固定为若干字节的包头包头里记录消息类型比如扫描到的是图像帧、鼠标事件还是文件块和载荷长度。接收方先读包头确定本次要收多少字节的正文读完后再解析下一条。这样做的好处是解析逻辑非常直观只要两端按同一套结构解释数据就不会因为字节流分界不清而解析错误。关于数据封包我的个人习惯是先用一个结构体定义包头格式再将包头序列化到 QByteArray 的前几个字节。源码里大概率也是类似做法。后文我再围绕具体的发送和解析逻辑展开这里先建立心智模型。3. 核心模块拆解从屏幕采集到指令回传的完整链路3.1 屏幕采集与图像压缩如何在“画面质量”和“实时性”之间取舍远程控制软件体验好不好第一关就是屏幕画面的流畅度。这个项目在服务端使用了QScreen::grabWindow来抓取当前桌面画面抓取结果是一张 QImage。直接发送原始 QImage 数据当然最保真但体积大得惊人——以 1920 × 1080 分辨率为例未压缩的 RGB 数据轻松超过 6MB这样的数据包在局域网里连续发带宽直接被打满延迟也难以接受。所以源码里会先对 QImage 进行缩放或编码压缩。合理的做法通常是这样的先按预设的缩放系数将图像缩小例如缩放到原始尺寸的 80% 或 50%再把 QImage 转换为QImage::Format_RGB32或Format_RGB888最后以 JPEG 格式编码。JPEG 在画面细节复杂度和体积之间能取一个不错的平衡。在源码里你大概率能看到类似下面的逻辑我以片段方式说明具体类名以源码为准// 屏幕捕获 JPEG 压缩示例 QScreen *screen QGuiApplication::primaryScreen(); if (screen) { QPixmap pixmap screen-grabWindow(0); // 抓取主屏幕 QImage image pixmap.toImage().convertToFormat(QImage::Format_RGB888); if (image.isNull()) { return; } // 按比例缩放降低网络传输压力 QImage scaled image.scaled(image.width() * ratio, image.height() * ratio, Qt::KeepAspectRatio, Qt::SmoothTransformation); QByteArray bytes; QBuffer buffer(bytes); buffer.open(QIODevice::WriteOnly); scaled.save(buffer, JPG, quality); // quality 常见取值 60-85 // 随后将 bytes 封包并通过 socket 发送 }这段代码的关键参数是quality和ratio。我把这两个参数称为画面“玄学旋钮”质量值越高画面越清晰但帧率下降质量值越低传输越流畅但可能出现马赛克和文字模糊。ratio同理缩得越小传输数据量越小但远端看到的字也越难辨认。你在跑这个项目时如果觉得操作有肉眼可见的延迟优先把质量值往下调 10~15 档再配合降低分辨率通常立竿见影。缩略图方案之外还有另一个思路是只传输屏幕变化区域也就是“帧间差”这份源码不一定实现了但如果你要二次开发这会是性能提升最大的一条路。因为远程操作时鼠标移动和文字输入其实只涉及屏幕的一小部分频繁发送全屏画面本质上是在传输大量“没变的背景”。3.2 数据包定义一类消息解决图像、事件、文件三类数据这一节是整个项目最值得反复阅读的地方。通信双方要协作必须先约定消息格式。源码里的做法是定义一套独立的包头结构通过type字段区分不同业务数据类型。典型的消息类型至少会包含下面几类图像帧鼠标事件键盘事件文件传输请求文件内容块心跳/握手包包头需要携带两个核心字段一个是“这个包是什么类型”另一个是“这个包后面跟了多少字节的数据”。// 包头定义示例 struct PacketHeader { quint16 type; // 消息类型例如 1 代表图像2 代表鼠标操作 quint32 length; // 当前包载荷的字节数不含包头本身 };发送方每次发包都先把PacketHeader写入 QByteArray再追加真正的数据载荷比如 JPEG 字节流、鼠标坐标、文件内容等。接收方在读数据时先解析出这两个值再做分支处理。这种方案复杂度低稳定性好也方便调试。如果你有网络编程经验会知道还有一种更紧凑的“TLV”设计即 Type-Length-Value这里的type和length在本质上是如出一辙的只是 Qt 源码里通常直接叫“消息类型 长度”。3.3 接收端的粘包拆包解析每个写网络程序的人都会撞上的坑TCP 是流式协议所以接收端必须自己处理消息边界。源码的解析逻辑通常会写在 socket 的readyRead信号处理函数里维护一个接收缓冲区quint32 blockSize或QByteArray buffer每收到新数据就追加到缓冲区末尾然后循环尝试从缓冲区里“剥”出一个个完整的数据包。// 接收缓冲区处理示例解决粘包/半包问题 void ClientNetwork::onReadyRead() { m_buffer.append(m_socket-readAll()); // 先收进缓冲区 while (m_buffer.size() (int)sizeof(PacketHeader)) { PacketHeader header; memcpy(header, m_buffer.data(), sizeof(header)); // 如果缓冲区还不满一个完整包体说明半包到达直接等待下一次数据 if (m_buffer.size() (int)sizeof(header) (int)header.length) { break; } // 剔除包头部分剩下的是载荷 m_buffer.remove(0, sizeof(header)); QByteArray payload m_buffer.left(header.length); m_buffer.remove(0, header.length); dispatchPacket(header.type, payload); } }我在给这个项目做二次开发时曾在这里踩过一个很隐蔽的坑memcpy直接拷贝PacketHeader时涉及结构体在内存中的对齐问题因为默认情况下编译器会为结构体填充对齐字节。如果在不同编译器或不同平台上发送端和接收端的对齐规则不一致就会导致解析出错误的数据长度。解决方式是使用#pragma pack(push, 1)或者在序列化时手动逐字段写入 QDataStream不直接对结构体做内存拷贝。这也是我强烈建议你后续维护这个项目时优先检查的点。另外要注意发送大文件时不应该把整个文件丢进一个包里发送。正确做法是分包发送每个包只携带一部分文件内容接收端边收边写。否则一旦某个大包在传输中遇到 MTU 分片接收端需要等待一整块完整数据内存占用和超时风险都会变大。3.4 鼠标键盘指令的采集与注入让远端电脑“听见”你的操作客户端把用户的鼠标移动、点击和键盘输入采集下来序列化后发送给服务端服务端把指令解析出来后再调用系统接口把这些事件注入到被控电脑。这个过程的体验决定了一个远程控制工具是否“跟手”。在工作原理上客户端需要监听鼠标事件和键盘事件拿到全局坐标或相对位移后连同事件类型、按键码一起打包发出去。服务端侧收到指令后可以选择使用 Qt 事件系统或 Windows 原生 API 进行事件注入。源码里如果只依赖 Qt 的方式通常是通过QApplication::sendEvent向目标窗口发送合成事件但很多实际场景下这种合成事件的接收并不可靠因为部分程序会检查事件来源导致模拟操作被拦截。所以如果你在源码里看到对SendInput或mouse_event/keybd_event的调用不要觉得意外——这是为了让远端操作尽可能与真实操作一致。我自己在类似的模拟项目里默认首选 SendInput原因是它属于 Input 层能触发全局钩子兼容性远好于 Qt 层合成事件。// 鼠标事件注入示例Windows API void RemoteServer::injectMouseEvent(int x, int y, int button, bool pressed) { INPUT input; memset(input, 0, sizeof(INPUT)); input.type INPUT_MOUSE; input.mi.dx (x * 65535) / screenWidth; // 归一化到绝对坐标 input.mi.dy (y * 65535) / screenHeight; input.mi.dwFlags MOUSEEVENTF_ABSOLUTE; if (button 1) { input.mi.dwFlags | pressed ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; } // 实际项目中还需要根据按钮类型设置 RIGHTDOWN/UP 等 SendInput(1, input, sizeof(INPUT)); }这里有两个值得注意的细节。第一绝对定位模式下dx/dy 的取值通常被要求映射到 0 到 65535 之间必须除以屏幕宽高做归一化否则会出现鼠标乱飞的情况。第二业务上务必要区分“收到的坐标来自哪个屏幕”因为多显示器环境下不同屏幕的坐标范围不统一直接按主屏尺寸映射会产生偏移。源码如果忽略了这个场景也是合理的但你在部署时要清楚这个限制。3.5 文件传输一个稳定的传输通道需要确认、分包和完成标记文件传输也是远程控制工具里使用频率很高的功能这个项目里同样可以找到对应模块。它的实现思路不复杂客户端先发送一个“文件传输请求”包里面包含文件名和总字节数服务端收到后回复确认随后客户端按固定大小比如 32KB 或 64KB分段读取并发送文件内容服务端每收到一块就写入本地文件最后客户端发送“传输完成”包服务端关闭文件并校验大小。// 客户端发送文件内容示例 void FileSender::sendFile(const QString filePath) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { return; } QByteArray block; qint64 totalSent 0; while (!file.atEnd()) { QByteArray chunk file.read(32 * 1024); totalSent chunk.size(); // 封包并发送type 为文件块payload 为 chunk sendPacket(PacketType::FileContent, chunk); // 避免一次发送过多导致 socket 缓冲区堆积 if (totalSent % (256 * 1024) 0) { QThread::msleep(2); } } // 发送完成标记 QByteArray endFlag; sendPacket(PacketType::FileEnd, endFlag); }在来自真实网络环境的使用反馈里最容易遇到的问题不是代码编写错误而是没有处理“接收端写入速度跟不上发送端”的情况。如果接收端用QFile::write写入磁盘而磁盘 IO 繁忙socket 接收缓冲区会迅速填满此时如果发送端继续猛发数据包会丢失或触发超时重传最终看起来就是文件传到一半卡住。所以即便是局域网环境发送端也最好加一个简单的流量控制或确认机制防止内存积压。4. 运行时配置与二次开发环境搭建从 clone 到跑通的完整路径4.1 编译环境的准备Qt 版本、编译器与依赖项先说说跑起来之前需要准备的东西。这个项目的构建链并不复杂本质上就是你本机有 Qt 环境即可。建议使用 Qt 5.15 或 Qt 6.x 系列编译器用 MSVC 或 MinGW 都行。如果你用的是 Qt Creator直接打开工程文件.pro 或 CMakeLists.txt就能加载整个项目。打开工程后需要确认.pro文件里是否引入了 network 模块这是最容易被忽略的一步。一个合格的.pro至少会包含这样几项QT core gui network greaterThan(QT_MAJOR_VERSION, 4): QT widgets TARGET RemoteServer TEMPLATE app SOURCES \ main.cpp \ serverwindow.cpp \ netserver.cpp \ screenlistener.cpp HEADERS \ serverwindow.h \ netserver.h \ screenlistener.h如果你是第一次在 Windows 下编译 Qt 项目我提醒一件事新建套件时务必选对位数x86/x64并且注意目标机与编译机的架构要一致。否则在部署时会因为位数不匹配出现运行时异常或无法加载 Qt 动态库的情况。4.2 网络模式修改端口、绑定 IP 与防火墙放行拿到代码后你不需要做任何复杂配置就能在局域网内完成测试。但有几个参数需要根据现场环境调整位置一般在两个工程的构造函数或初始化函数里服务器端监昕端口默认常见为 8888 或 9527 之类的自定义端口以源码为准客户端连接的服务器 IP 地址图像质量与传输分辨率参数在这个项目中修改连接地址我一般会在客户端代码里搜索connectToHost或serverIp它的参数就是你要填写的目标地址。修改端口时还要注意一个 Windows 特有的细节Windows 防火墙默认会拦截外部对本机的入站连接。如果你启动服务端后客户端怎么都连不上八成就是防火墙拦截或服务器程序没有“允许应用通过防火墙”。解决路径是去防火墙设置里放行对应端口或可执行文件。这一步属于运行时环境配置很多新手在这一步就放弃了其实只要多加一个放行规则就能解决。4.3 一次完整的运行测试流程如果你想验证自己的环境搭得对不对可以按下面的流程做一次单机回环测试在同一个窗口里分别启动服务端程序和客户端程序IP 填写127.0.0.1。服务端点击“开始监听”客户端点击“连接”。观察服务端界面上的连接状态提示应该显示已连接。客户端窗口中出现被控端的桌面画面并随之刷新。在客户端画面上单击鼠标观察服务端机器是否有反应此时光标位置会落在对应坐标。在客户端界面使用快捷键或按钮尝试发送文件观察文件是否完整写入被控端。如果第 2 步就失败优先检查端口占用和防火墙如果画面一直黑屏优先检查屏幕捕获代码是否在主线程执行某些 Qt 环境下屏幕抓取需要窗口系统资源。画面卡顿严重时调低图像质量和分辨率参数而不是怀疑代码写错了。5. 避坑指南我在这个项目上实际踩过的 5 个典型问题远程控制项目由于同时涉及网络、图像、系统 API最容易出现的问题是“单个模块没问题合在一起就出莫名故障”。下面这 5 条来自我在类似项目中的真实排查经历每一条都对应一种高频现象和一种可行的解决路径。5.1 现象客户端连接成功但画面一直黑屏原因可能有两种。第一服务端截屏时机早于客户端建立连接没有触发截图发送帧循环第二截屏线程与 UI 线程发生资源竞争屏幕捕获返回空图片。比较常见的是第四种服务端在发送图像前没有做“是否为空”的判断捕获函数在窗口最小化或锁屏时返回空 QPixmap导致发送的是空数据。解决方式是在捕获代码入口加一层保护判断if (pixmap.isNull()) return;同时确认截图循环由定时器驱动连接建立后立刻开始发送。如果平时运行正常、只有锁屏后黑屏那多半就是出于操作系统安全策略限制可以考虑在“解锁状态”下测试。5.2 现象鼠标点击位置总是偏移几十像素如果你用客户端控制被控端点击位置偏了首先要检查的是图像缩放。客户端渲染画面时如果按等比缩放展示而回传鼠标坐标时没有按缩放比例反向换算就会出现位置偏移。比如远端屏幕是 1920 × 1080客户端显示区域只有 960 × 540用户在显示区中间点击实际对应远端坐标不一定是中间需要乘以缩放系数。解决方式是在客户端捕获鼠标位置时除以当前显示区缩放比再发送实际坐标。或者干脆把显示区固定为原始分辨率不做缩放代价是画质和窗口占用。5.3 现象图片数据量巨大延迟非常高这是最容易出现性能瓶颈的点。某些初学者会把QImage转成 QByteArray 后直接发送不经过压缩。这样每个画面的原始字节数等于 1920 × 1080 × 3 字节大约 6MB。即使是 10 个包的分片也会瞬间占用大量带宽让指令包和图像包互相抢带宽。解决方式是把原有逻辑改为“JPEG 压缩 缩放”在发送前先做编码。实际效果是每帧通常在 30KB 到 150KB 之间视画面复杂度和质量参数而定。如果你的网络延迟还是高再尝试把缩放比改成 0.5并降低到 60% 质量。5.4 现象文件传输到一半断掉或校验不一致文件块在传输过程中本质上是连续的分隔包结构并不存在“一次传输一个超长数据包”的语义。断掉的常见原因有三个一是单个包过大导致 socket 内存堆积二是接收端写入速度跟不上三是长时间传输中某段数据被误判为粘包。排查时可以先统计真实收到的字节数和文件大小是否一致。如果不一致则大概率是数据包解析边界出了问题。解决方式是给每个文件块包增加“序号”和“总块数”字段接收端收到后可以检查是否有跳号。哪怕不先实现完整的断点续传增加序号也能极大减少排查成本。5.5 现象键盘输入只对部分应用有效这个问题的本质是合成键盘事件与真实键盘事件在系统层面的优先级不同。部分程序例如游戏、银行控件、专业软件会过滤掉由 SendInput 注入的键盘事件。碰到这种情况你很难在业务层面完全绕过去。务实的做法是保证系统以管理员权限运行程序和尝试切换键盘布局并在计划文档里把“不支持所有应用”列为合理限制。我在做类似项目时会先把常用办公软件的操作场景覆盖好再考虑游戏场景。6. 二次开发改造如何低成本加入剪贴板同步和更优的画面刷新策略聊到改造我先说说这个项目天然适合扩展的切口。远程控制的日常使用里除了画面和键鼠另一个高频需求是“剪贴板同步”——把本机复制的内容直接粘贴到远端电脑上。这个功能本质上是把系统剪贴板内容文本为主以数据包的形式同步到对端。以本项目现有的数据包类型为基础你只需要增加一种新类型比如ClipboardText整体改动量非常小。实现剪贴板同步的一种直接做法是在客户端监听QClipboard::dataChanged信号一有变化就读取文本内容并发送服务端收到后调用当前线程的剪贴板对象写入内容。需要注意剪贴板同步是一个“双向”功能两端都要监听否则你只能单向复制粘贴。如果两端循环触发A 变化发给 BB 写入后触发 B 的 dataChanged 又发给 A需要加一个标志位判断“本次剪贴板内容是否由远端同步而来”防止死循环。// 剪贴板同步发送端示例 void ClientNetwork::onClipboardChanged() { QClipboard *clip QApplication::clipboard(); QString text clip-text(); if (text.isEmpty()) { return; } // 加一个简单标志防止远端写入触发本端再次发送 if (m_updatingFromRemote) { return; } sendPacket(PacketType::ClipboardText, text.toUtf8()); }接收端的处理更是简单从载荷里取出文本写入剪贴板即可。整个过程半小时内就能在现有源码上完成不需要新增复杂依赖。另一个值得动手优化的点是画面刷新策略。目前的源码大概率是全屏定时抓取并发送这在多窗口快速切换时一定会出现短暂“残影”或“撕裂”。我建议做的改进是把全屏抓取逻辑升级为“脏矩形计算”。思路是每次抓取后与上一帧的 QImage 做像素级对比找出变化区域的矩形包围盒只把这几个矩形区域的图像压缩发送。客户端收到后用区域坐标和图像数据进行局部更新。理论上这会把 90% 的静态背景流量省掉操作感受也会明显提升。实现脏矩形计算时有一个实际困难逐像素对比整块屏幕是 CPU 密集操作。常见做法是对图像做块状分割比如 16 × 16 像素为一个基本块只比较每个块的哈希或灰度均值变化超过阈值才判定为“脏”。对比结果是一个块索引集合把这些索引对应的像素区域收集起来再压缩效率远比全屏比较要高。谈到改造的核心原则我还有两条血泪经验。第一个是改动数据包协议时必须同步更新两端的解析逻辑最好在公共类里统一维护类型常量不在业务层里离散写死数字。第二个事是每次调试完新功能必做一次“小水管模拟测试”也就是限制本机带宽或调低压缩质量以观察模块是否还会出现异常。以前我曾因为只测了千兆局域网的高清画面没有覆盖弱网场景结果交付后才发现文件传输在大延迟链路下会直接卡死。从那次以后我每次对这类远程控制项目做改动都会强制走一遍“协议变更 → 双端同步 → 弱网模拟 → 回归测试”的流程。这个项目我推荐你拿到手后也按这个路径跑一遍先在局域网把基础流程跑通再有计划地去改图像压缩参数和新增数据包类型你会发现这套代码的学习价值相当高希望这次拆解对你上手能有帮助。本文还有配套的精品资源点击获取

相关新闻

RS485全双工通信原理与工程实现指南

RS485全双工通信原理与工程实现指南

简介:本资源是面向嵌入式开发工程师与工业通信初学者的RS485全双工通信实战项目,聚焦工业控制与物联网场景中长距离、多节点可靠通信的核心需求,系统解决主机与双从机协同通信的设计难点。压缩包共183个文件,涵盖13个C源码文件&am…

2026/10/16 13:32:03 阅读更多 →
Ubuntu 18.04部署Moodle 3.9实战:稳定、安全、可运维的LMS搭建指南

Ubuntu 18.04部署Moodle 3.9实战:稳定、安全、可运维的LMS搭建指南

1. 项目概述:为什么在 Ubuntu 18.04 上部署 Moodle 仍是值得认真对待的实操课题Moodle 是全球教育技术领域最成熟、使用最广泛的开源学习管理系统(LMS)之一,它不是简单的“网课平台”,而是一套具备课程管理、作业批改、…

2026/10/16 13:32:03 阅读更多 →
多品种小批量精益落地:从需求底数到拉动机制的关键路径

多品种小批量精益落地:从需求底数到拉动机制的关键路径

掐指一算,我这些年见过的中小制造企业,凡是多品种、小批量、订单式生产的,几乎没有几家敢说自己真正把精益落地了。最常见的状态就是:人在办公室盯着订单表,催料、催产、催出货,现场靠几个老师傅凭经验救火…

2026/10/16 13:32:03 阅读更多 →

最新新闻

TPC-DS基准测试:ClickHouse与Doris OLAP性能对比实战

TPC-DS基准测试:ClickHouse与Doris OLAP性能对比实战

做OLAP数据库选型,最怕的就是厂商自己掏出来的那份压测报告——查询速度一个比一个快,资源占用一个比一个省,真到自己业务上一跑,数据量一上去,才发现根本不是那么回事。所以我一直坚持拿统一的标准来测,TP…

2026/10/16 14:16:20 阅读更多 →
涨停选股法:从资金共识到竞价分时的短线交易框架

涨停选股法:从资金共识到竞价分时的短线交易框架

涨停板是A股最刺眼的信号,也是短线资金博弈的终点。每天收盘后翻一遍涨停列表,再结合次日竞价和分时走势去判断延续性,这套“涨停选股法”本质上不是让你去打板,而是利用涨停板作为筛选器,把全市场四五千只股票过滤到只…

2026/10/16 14:16:20 阅读更多 →
Java+OpenCV实战:HSV颜色分离与几何图形识别

Java+OpenCV实战:HSV颜色分离与几何图形识别

简介:这份PDF面向Java开发者与计算机视觉初学者,聚焦如何用Java调用OpenCV视觉库完成几何图像的颜色与形状识别,可解决传统RGB识别率不高、Java实现案例偏少的问题。资源共1个PDF文件,压缩包约894KB,内容为完整技术论文…

2026/10/16 14:16:20 阅读更多 →
智能计算售前题库拆解:从AI服务器选型到有效算力

智能计算售前题库拆解:从AI服务器选型到有效算力

简介:面向华为H19-381智能计算售前认证备考者的题库资料,内容覆盖服务器、存储、安全、网络与计算等核心知识域,包含Taishan服务器、iBMC管理接口、RAID技术、SSD分类、TPM可信平台模块、鲲鹏自研芯片等关键考点,适合备考华为售前…

2026/10/16 14:16:20 阅读更多 →
边缘检测原理与实战:从Sobel到Canny算子详解

边缘检测原理与实战:从Sobel到Canny算子详解

1. 为什么说边缘是图像的"骨架信息"我最早接触边缘检测,是很多年前帮一个师兄处理显微镜图像里的细胞计数问题。当时什么都不懂,以为边缘检测就是把图片变模糊再变白,折腾了三天,结果出来的图连我自己都看不出哪里是细胞…

2026/10/16 14:16:20 阅读更多 →
《信号与系统》郑君里第三版7z课件:验货、解压与高效使用指南

《信号与系统》郑君里第三版7z课件:验货、解压与高效使用指南

简介:《信号与系统》课件(郑君里第三版)是电子信息和通信工程专业的核心课程配套学习资源,适合在校本科生同步复习、考研备考,以及相关从业者重温信号处理基础。整份课件以7z压缩包发布,体积约23.76MB&…

2026/10/16 14:15:19 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

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

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →