
简介一套基于易语言编写的远程控制系统完整源码包含主控端与被控端两大部分适合易语言学习者、网络通信开发人员研究远控实现原理。压缩包内共9个文件主要包含易模块、工程文件、音频及说明文档等扩展模块负责底层API与视频捕捉功能上下线提示音用于状态反馈说明文档辅助源码阅读与调试整体仅309KB体积紧凑可快速导入易语言环境进行编译测试。资源当前已有1119人学习下载具有不错的参考价值。透过源码可系统了解读取配置、消息提示、发送数据、上线/下线处理、IP地理查询、视频捕获发送等关键环节的实现手法既能帮助初学者理解远程控制系统的架构与网络通信流程也为二次开发或安全研究提供了可复用的代码框架。使用时需遵守相关法律法规仅限学习与技术交流。1. 易语言主控源码、易语言被控源码、易语言远控这套工具到底能解决什么易语言主控源码、易语言被控源码、易语言远控这三样东西放在一起就是一套用中文编程语言写成的远程控制系统主控端负责连接、下发指令被控端负责监听、执行并回报结果。我最早碰这个方向纯粹是为了远程维护家里和单位那几台常年吃灰的测试机——屏幕卡住、服务起不来、半夜要跑个脚本人跑一趟太折腾。用易语言做远控的核心优势是中文关键字和贴近自然语言的语法通信协议、界面、进程操作都在一个语言体系里不用在 C 和界面库之间来回切。这篇笔记面向已经会写易语言窗口程序的开发者从协议设计到主控与被控两端落地再到排错和进阶功能按我实际跑通的路径讲整套下来大概能写出一份自用可控的远控工具。2. 远控的骨架主控、被控与通信协议设计2.1 远控由三个角色组成写代码前先分清职责任何远控系统都能拆成三个角色主控端、被控端、连接协议。主控端是主动发起连接的那一方负责展示设备列表、接收用户指令、把指令打包成消息发送出去同时要接收被控端回传的状态和结果。被控端正好相反它不主动找主控而是长时间监听一个固定端口等主控连上来收到指令后执行系统操作再把结果发回去。协议则是双方都能识别的消息格式包括消息长度、消息类型、数据体。我见过不少新手上来就写界面写了两天发现还没调到通信原因就是没把这三个角色拆清楚。先确定一条原则主控只管“发”和“收”被控只管“收”和“执行”状态同步靠心跳协议业务逻辑靠命令协议两边谁也不要越界。通信方式上我一般直接选 TCP 长连接而不是 UDP 或 HTTP。原因有三个第一远控场景需要可靠有序的字节流TCP 自带确认重传省掉一整套丢包处理第二主控和被控之间是长时间会话TCP 长连接不用每次发命令都重新握手延迟低第三易语言的“客户”组件和“服务器”组件本身就是 TCP 封装用起来最顺手。UDP 的好处是穿透 NAT 容易但可靠性、连接管理都要自己做对一般自用远控来说得不偿失。角色主动方主要职责易语言组件主控端主动连接下发指令、收状态、展示结果客户组件被控端被动监听执行指令、回传结果、发心跳服务器组件协议双方共有封包、解包、类型识别字节集操作2.2 自定协议头用易语言字节集封出一条带类型的消息通信协议最忌讳两件事一是没有长度字段靠“判断结尾符”切消息结果数据里一旦出现同样的结尾符就拆错包二是不分消息类型所有数据都混在一起收包方不知道这条是心跳还是命令。我的做法是给每条消息加一个统一头部格式概括成四个部分4字节整数表示包体总长度1字节表示消息类型4字节表示命令号最后是变长的文本参数。对应易语言代码封包子程序如下.版本 2 .支持库 spec .程序集 协议处理 .子程序 封包_命令, 字节集 .参数 命令号, 整数型 .参数 参数文本, 文本型 .局部变量 包体, 字节集 .局部变量 包长, 整数型 包体 1字节类型 4字节命令号 参数文本 包体 到字节集 (协议类型_命令) 到字节集 (命令号) 到字节集 (参数文本) 包长 取字节集长度 (包体) 返回 (到字节集 (包长) 包体)这段代码的关键在于“先拼包体、再拼长度”。包体里包含类型、命令号和参数文本取到包体长度后把这个长度值放在最前面组成“4字节长度 包体”的完整消息。接收端拿到消息后先读前4个字节得到包长然后从第5个字节开始取“包长”这么多字节正好就是完整包体。命令号用整数型占4字节能覆盖几千条不重复的指令对自用远控绰绰有余参数文本用“到字节集”转换在双方都是中文 Windows 系统时按系统默认编码传输两端文本一致到文本转回来就是原内容。接收端的拆包逻辑要放在一个独立的子程序里不要拿到数据立刻就处理。常见做法是维护一个收包缓冲字节集新数据到了先追加进去再循环尝试拆出完整包.程序集变量 收包缓冲, 字节集 .子程序 拆包_尝试取完整包, 字节集 .局部变量 包长, 整数型 .局部变量 完整包, 字节集 如果真 (取字节集长度 (收包缓冲) 4) 返回 ({ }) 包长 到整数 (取字节集中间 (收包缓冲, 1, 4)) 如果真 (取字节集长度 (收包缓冲) 4 包长) 返回 ({ }) 半包继续等后续数据 完整包 取字节集中间 (收包缓冲, 5, 包长) 收包缓冲 取字节集中间 (收包缓冲, 5 包长, 取字节集长度 (收包缓冲) 4 包长) 返回 (完整包)我特别强调一个细节判断“取字节集长度(收包缓冲) 4 包长”时必须用小于而不是小于等于。等于说明缓冲里恰好是一个完整包可以直接拆出来小于才说明是半包。很多断线、乱码问题都出在边界判断上这个等号别省。2.3 心跳、超时与重连没有心跳的远控是黑匣子网络连接不是一直在线的交换机重启、无线网络切换、电脑休眠都会让 TCP 连接悄悄断掉。如果被控端不发任何消息主控端这边显示“在线”的列表就会变成一座黑匣子——你根本不知道对端是死是活。心跳包就是专门解决这个问题的被控端每隔固定时间主动发一条极短的消息主控端只要在超时时间内没收到就判定这条连接失效。参数我一般这样设心跳周期30秒意思是每30秒发一条判定超时90秒意思是连续90秒没收到心跳就认为是掉线。理论上快速计算要设成“周期重连时间”实际使用中不需要那么极限30秒心跳配90秒超时能覆盖绝大多数网络抖动又不会等太久才发现断线。心跳消息单独分配一个类型值比如0x01包体直接用短文本“OK”.子程序 封包_心跳, 字节集 .局部变量 包体, 字节集 包体 到字节集 (协议类型_心跳) 到字节集 (“OK”) 返回 (到字节集 (取字节集长度 (包体)) 包体)心跳包不要复用命令消息。原因很简单命令消息需要被控端确认执行结果如果网络半断开命令可能发出去但结果不确定心跳是单向探测不依赖业务逻辑响应路径越短越好。把心跳独立出来之后主控端只需要一个“最后心跳时间”变量每次收到类型0x01就刷新这个值一个时钟周期检查一次超过90秒就标记离线。被控端端口选择上建议避开80、443、3389这些容易被占用或被系统服务抢占的端口我一般用7001这种高位端口冲突概率低也能直接用命令查看监听状态。3. 主控端源码落地界面、连接管理、指令下发3.1 主控端用客户组件还是自建连接池主控端本质是一个 TCP 客户端。易语言里最简单的方案是用“客户”组件它在工具箱里拖出来就能用有连接、断开、发送数据三个核心方法还有连接成功、连接断开、数据到达三个事件。单台设备管理一个客户组件就够但如果要同时连多台被控端就得创建客户组件数组。动态创建客户组件的常见做法是“复制窗口组件”一次复制出多个同名的客户组件索引从0开始。这里有个易语言的坑复制出来的组件必须连续命名比如批量复制出“客户0”“客户1”“客户2”代码里引用“客户[i]”才能正常工作。我见过有人手动拖了三个客户组件结果名字叫“客户A”“客户B”“客户C”循环访问时直接编译报错。组件数量要事先想清楚。被控端数量少的场景比如三五台测试机客户组件数组开10个循环试连就行如果以后可能要管几十台机器就不该用界面组件硬扛应该用自建连接数据结构加线程收发。我做自用远控的建议是第一版先别贪多控制在10台以内把单条连接的收发稳定性调好再考虑扩展。3.2 主控界面布局与连接事件界面按功能块划分顶部是目标IP和端口输入框加一个“连接”按钮左边是设备列表用超级列表框展示设备名、IP、状态下方是命令输入框和“发送”按钮最下面是日志区域用编辑框的“内容”属性追加文本。布局的意义在于操作路径是“选设备 → 输命令 → 看日志”所有反馈都要能在界面上直接看到。连接按钮的事件代码可以这样写.程序集 窗口程序集_启动窗口 .程序集变量 当前连接索引, 整数型 .子程序 _按钮_连接_被单击 客户1.断开 () 当前连接索引 0 客户1.连接 (编辑框_IP.内容, 到整数 (编辑框_端口.内容)) 日志输出 (“正在连接 ” 编辑框_IP.内容)连接成功后客户组件会触发“_客户1_连接成功”事件这时再去更新设备列表状态连接失败触发“_客户1_连接断开”日志里要给出明确提示。我把“断开”放在连接前面调用一次是防止用户连着一半又点连接导致状态错乱。3.3 指令下发命令号定义、封包发送与收包解析命令号是整个命令体系里的“字典”两端必须一致。我习惯在一开始就规划好常用命令号后续扩展只往里面加不随意复用。示例规划如下命令号含义参数说明1打开程序程序路径或URL2执行DOS命令完整命令行3锁定屏幕无参数4关机延迟秒数5截取屏幕无参数6上传文件本地路径、远端路径发送指令时调用之前写好的封包子程序把命令号和参数文本传进去生成一条完整的字节集消息然后交给客户组件发送.子程序 发送命令, 逻辑型 .参数 命令号, 整数型 .参数 参数文本, 文本型 .局部变量 封包, 字节集 封包 封包_命令 (命令号, 参数文本) 客户1.发送数据 (封包) 日志输出 (“发送命令” 到文本 (命令号)) 返回 (真)这条代码本身很简单但要注意一个易选型问题发送动作放在按钮事件里没问题因为它只是把字节集交给组件真正的网络写操作由组件异步完成。不要在发送之后立刻加“循环等待回执”那会把界面线程堵死。回执应该在“数据到达”事件里解析。数据到达事件的完整收包流程我在2.2节已经给了拆包子程序主控端要做的是把它接到组件事件里.子程序 _客户1_数据到达 .局部变量 完整包, 字节集 .局部变量 消息类型, 整数型 .局部变量 结果文本, 文本型 收包缓冲 收包缓冲 客户1.取回数据 () 完整包 拆包_尝试取完整包 () 如果真 (取字节集长度 (完整包) 0) 返回 消息类型 取字节集中间 (完整包, 1, 1) 的字节值 结果文本 到文本 (取字节集中间 (完整包, 6, 取字节集长度 (完整包) 5)) 日志输出 (“收到类型 ” 到文本 (消息类型) 文本结果)这里“取字节集中间(完整包, 6, …)”是因为完整包布局是“1字节类型 4字节命令号 参数文本”参数从第6字节开始。每个数据到达事件都要先追加缓冲再尝试拆包拆出来的包可能不止一条所以循环要写完整拆出一条处理一条直到缓冲里剩余长度不足。3.4 在线状态怎么维护三次心跳容错与界面状态刷新主控端对每个被控端维护三个状态字段是否在线、最后心跳时间、连续丢失心跳次数。判断逻辑可以简化为收到心跳包就重置最后心跳时间时钟组件每1秒检查一次如果当前时间减去最后心跳时间超过90秒就把状态改为离线同时不再对该设备发送命令避免向黑洞连接发指令。更细一点的容错策略是连续三次超时才算离线。实用做法是维护一个“连续丢包计数”每次收到心跳清零每30秒检查一次如果没有在最近一个周期收到心跳就加1计数达到3次再判定离线。这个策略能避免单次网络抖动造成误判实际体验比“只看最后一次心跳时间”更稳缺点是代码多一个变量。我会在自用版本里用后者因为很多场景是笔记本休眠唤醒后网络短暂中断不希望在唤醒瞬间看到一堆设备全部变红。界面刷新不要写在每个事件里。超级列表框每收到一条包就刷新一次会把人眼闪瞎性能也差。我一般让“设备状态”这一列只在两个时机更新心跳到达时更新对应表项的文本时钟周期里批量修正所有超时设备。这两处更新都做断言判断只在状态发生变化时才重写列表项日志里也不要刷屏打“心跳收到”否则日志区几秒钟就被填满。4. 被控端源码落地监听、命令分发、执行与回传4.1 被控端监听服务器组件的启动与事件被控端是服务端监听入口用易语言的“服务器”组件最直接。启动监听前要做几件收尾工作先把工作目录切到程序所在目录方便后面读写配置文件再检查端口是否被占用被占用就提示并停止启动最后把服务器组件的端口属性赋值调用启动方法。.版本 2 .支持库 internet .程序集 窗口程序集_启动窗口 .子程序 _按钮_启动服务_被单击 服务器1.停止 () 服务器1.端口 到整数 (编辑框_端口.内容) 服务器1.启动 () 日志输出 (“已监听端口 ” 编辑框_端口.内容)这段代码里启动前调用“停止”是习惯性防御动作防止上一次服务还在运行就重复启动。服务器组件的事件有三个客户进入、数据到达、客户离开。客户进入时记录一下客户标识和IP往界面日志输出一行“新连接”客户离开时从在线列表里移除对应客户。客户标识是一个整数型值服务器组件靠它区分不同的客户端后续“发送数据(客户标识, 数据)”都靠它定位发送目标。4.2 半包与粘包处理拆包逻辑是远控的命门被控端收到的一条“数据到达”可能包含半条、一条甚至多条完整消息。原因是底层 TCP 会把小包合并成大包发送也可能因为网络分片导致一条消息被拆成好几次到达。如果不处理后果就是乱码、命令丢失、解析位置错位。拆包逻辑必须放在被控端所有数据处理的最前端。我在2.2节已经写了一个拆包子程序被控端几乎可以原样复用只是把缓冲变量名换成被控端自己的。处理流程固定为四步追加数据到缓冲、判断缓冲是否够4字节、读取包长判断是否够一个完整包、取出完整包并裁剪缓冲。.程序集变量 收包缓冲, 字节集 .子程序 _服务器1_数据到达 .参数 客户标识, 整数型 .局部变量 本次数据, 字节集 .局部变量 完整包, 字节集 .局部变量 包长, 整数型 本次数据 服务器1.取回数据 () 收包缓冲 收包缓冲 本次数据 判断循环首 (取字节集长度 (收包缓冲) 4) 包长 到整数 (取字节集中间 (收包缓冲, 1, 4)) 如果真 (取字节集长度 (收包缓冲) 4 包长) 跳出循环 () 完整包 取字节集中间 (收包缓冲, 5, 包长) 收包缓冲 取字节集中间 (收包缓冲, 5 包长, 取字节集长度 (收包缓冲) 4 包长) 处理完整包 (客户标识, 完整包) 判断循环尾 ()注意判断条件用的是“4”而不是“≥4”这样能少一次空转真正拆包前再做一次半包判断。跳出的时机很关键缓冲长度大于4但还不够一个完整包时直接跳出循环等下一次“数据到达”再继续拆。很多新人在这里写错把缓冲重新赋值成“剩余部分”的位置算偏一位结果后续所有包都拆歪排错半天。4.3 命令分发从类型字段到具体执行的开关逻辑拆出完整包之后第一步是判断消息类型。类型0x01是心跳直接更新最后活跃时间不回发业务数据保持极短处理路径类型0x02是命令进一步解析命令号和参数文本再进入分支执行。易语言里没有 switch 语句我习惯用“如果真”连串写分派逻辑好处是易读、好扩展.子程序 处理完整包 .参数 客户标识, 整数型 .参数 完整包, 字节集 .局部变量 消息类型, 整数型 .局部变量 命令号, 整数型 .局部变量 参数文本, 文本型 消息类型 取字节集字节 (完整包, 1) 如果真 (消息类型 协议类型_心跳) 返回 如果真 (消息类型 协议类型_命令) 命令号 到整数 (取字节集中间 (完整包, 2, 4)) 参数文本 到文本 (取字节集中间 (完整包, 6, 取字节集长度 (完整包) 5)) 执行命令 (客户标识, 命令号, 参数文本)执行命令子程序里用“如果真”逐条判断命令号命令号1运行指定路径的程序命令号2执行DOS命令命令号3调用系统锁定功能命令号4发起系统关机。执行系统命令时我常用的易语言方式是“运行(命令行, 假, )”第二个参数“假”表示不等待程序运行结束因为等待可能在界面线程里造成卡顿。执行结果必须回传。这里我踩过一个坑直接回传“成功”两个字没意义要回传可操作的文本。比如命令号2执行DOS命令时结果先重定向到临时文本文件再读文件内容作为参数回传.子程序 执行DOS命令 .参数 命令行, 文本型 .局部变量 临时文件, 文本型 .局部变量 执行输出, 文本型 临时文件 取运行目录 () “\cmd_out.txt” 运行 (“cmd /c ” 命令行 “ ” 临时文件, 真, ) 执行输出 读入文本 (打开文件 (临时文件, #读入)) 发送给主控 (执行输出)“运行”的第二个参数“真”表示等待运行结束命令行执行完并完成重定向后再读临时文件内容最后把文本作为命令结果封装成消息发回给主控。回传消息可以复用封包子程序只是类型字段换成“协议类型_命令结果”命令号保留原命令号这样主控端能对应上哪条命令返回了结果。4.4 心跳与多连接被控端向所有主控回心跳被控端的心跳要发给所有已连接的主控。服务器组件内部维护一个客户集合但并没用简单接口能一次性遍历全部客户所以要在“客户进入”事件里把客户标识加入一个整数型数组“客户离开”事件里移除。心跳时钟触发时遍历数组对每个客户标识发送封包好的心跳字节集.子程序 _时钟_心跳_周期事件 .局部变量 i, 整数型 计次循环首 (取数组成员数 (已连接客户数组), i) 服务器1.发送数据 (已连接客户数组 [i], 封包_心跳 ()) 计次循环尾 ()网络切换或电脑休眠醒来后监听也可能失效。被控端不需要主动去重连主控因为主控会定期重连被控端要做的只是确保服务器组件始终处于监听状态。我一般会在系统网络变化时做一次检查如果“服务器1.取端口状态”异常就执行一次“停止 → 启动”。这条路径不常触发但真遇到就会救你一次。5. 易语言远控最常见的5个翻车点断线、假死、乱码与内存暴涨怎么排查5.1 断线重连风暴5秒心跳为什么让两端互相折磨现象被控端明明在线主控端却不停地显示“设备上线”“设备离线”日志刷得飞快两端CPU占用同时升高。原因我早期把心跳周期设成5秒超时判定设成10秒结果无线网络一次轻微抖动超过2秒主控就判定离线并触发重连重连成功后刚建立连接又赶上心跳发送窗口错位再次抖动再次重连形成了循环。解决把心跳周期放宽到30秒超时放宽到90秒。重连间隔要单独设计第一次断开后等5秒第二次等10秒最多间隔60秒不要一断就立刻猛连。这条改动让网络抖动从“事故”变成“可容忍事件”也是我后来最想提醒新手的参数组合。5.2 按钮点了就卡死网络事件占据了界面线程现象点“发送命令”按钮后界面卡住不能拖动过几十秒才恢复有时直接提示“未响应”。原因在按钮事件里写了循环等待发送命令后用一个“判断循环首”等回执把界面线程阻塞了。易语言的客户组件“数据到达”事件也在界面线程上触发事件被阻塞后回执即使到了也处理不了形成死等。解决按钮事件只负责封包和发送绝不循环等结果。回执统一在“数据到达”事件里处理需要同步等待的业务用一个“等待命令号”变量加一个时钟去轮询。如果处理逻辑本身耗时就启动线程.子程序 _客户1_数据到达 启动线程 (处理回执, , )线程里不要再碰界面组件需要更新界面时用“发送消息”投递回界面线程或者在窗口事件里取共享变量值。5.3 中文参数变乱码从主控到被控的编码不一致现象主控端发送“打开记事本”被控端执行时变成“鎵撳紑璁颁簨鏈”程序路径乱码无法运行。原因两端系统默认代码页不一致或者中间有一层代理程序把 GBK 字节序列当成 UTF-8 处理了一遍。被控端用“到文本”转换时按本地代码页还原主控按另一套编码发送必然乱。解决两端都是易语言程序时约定用 GBK/ANSI 发送文本被封包和拆包都不做转码直接“到字节集”和“到文本”。如果被控端将来要兼容非中文系统就要在协议层面加一个编码字段并把参数文本统一转成 UTF-8 再传输接收端按 UTF-8 解码。自用中文 Windows 场景不做转码反而最稳。5.4 内存挂机一晚上涨几百兆收包缓冲裁剪出错现象挂了十几个小时被控端内存从30MB涨到300MB主控端也一样。原因拆包时“收包缓冲”裁剪位置算错导致缓冲里堆积了大量已经处理过的头部和数据。典型错误是用“取字节集中间(收包缓冲, 包长5, 取字节集长度(收包缓冲))”来截取剩余部分没有扣掉前4字节长度字段每次多留4字节积少成多。解决统一用“起始位置 5 包长长度 总长度 - 4 - 包长”来裁剪剩余部分。日志和超级列表框也要限长日志超过500行自动清理旧行超级列表框超过1000条时删除头部记录否则哪怕协议正确界面数据量也会拖垮内存。5.5 服务器组件启动报“端口被占用”的排查路径现象被控端启动服务时弹窗提示端口被占用改端口又能正常启动。原因上一次进程异常退出端口没有被系统及时释放或者机器上有其他服务正在使用同一个端口。我遇到过一类情况是杀毒软件把被控端进程特征识别后反复拉起导致端口被抢占。解决启动服务前先调“服务器1.停止()”等待1秒再启动。处理提示弹窗后在命令行执行“netstat -ano | findstr 7001”看占用端口的进程ID再用任务管理器确认是什么程序。若确认是残留进程任务管理器结束它即可。这里要强调自己写的远控程序只应部署在自己拥有或已获授权的设备上端口占用排查同理按正常系统问题处理。6. 从能跑通到能用屏幕回传、文件传输与开机自启6.1 屏幕回传快照、JPEG压缩与分块传输能发命令的远控只是半成品真正好用的是“看到对方屏幕”。实现思路是被控端调用系统快照功能取整块屏幕的位图数据转成JPEG格式压缩然后按每块64KB切片发送。压缩这一步不能省裸位图一张就有好几MB压缩后通常只有几十KB体验完全不一样。主控端收到所有分块后按块序号拼接成完整JPEG字节集塞进图片框显示即可。参数上我会把刷新间隔设为1秒一次块大小设为64KB超过MTU太多容易触发IP分片小一点更稳。6.2 文件传输分块读取、MD5校验与断点续传文件传输比屏幕回传更讲究完整性。常见做法是主控端先发送文件信息包文件名、总大小、MD5被控端确认后主控按256KB一块循环读取文件发送。每收到一块被控端就写入本地文件并回传一个块序号确认主控收到确认才发下一块。全部传完后被控端计算接收文件的MD5和文件信息包里的MD5比对一致回传“传输完成”不一致回传“校验失败”。断点续传参数设计也不难文件信息包加一个“起始块号”被控端接收时跳过起始块之前的写入。失败重传时不用整个文件重来只重发未确认的块。这个功能实现起来代码量不大但排查要多花时间我建议核心流程跑通后再加断点续传。6.3 开机自启与托盘只加启动项不做无提示后台被控端做开机自启最常见的做法是写注册表启动项。我只会把它用在自用机器的个人设备管理上并且一定做成可见的托盘程序不做无提示后台运行。注册表路径固定代码就两行写注册项 ( #现行用户, “Software\Microsoft\Windows\CurrentVersion\Run\MyRemote”, 取运行目录 () “\remote.exe”)卸载自启时删除对应注册项配合托盘图标让用户能一键退出这属于尊重设备所有者的基本边界。我自己的教训是刚开始做远控时什么都想“后台自动运行”结果部署到自己的测试机后因为临时调试重启反而找不到入口最后被迫远程进系统手动清理启动项。从那以后所有自启功能都做成显式可控宁可每次手动启动也不给自己留麻烦。另一个收尾习惯是定期检查心跳日志。自用远控跑一段时间后翻一翻主控端日志看哪些设备经常离线、哪条命令平均耗时变长往往能提前发现测试机网络状态恶化。希望帮到你。本文还有配套的精品资源点击获取