琚帶绔?- 淇 MTU 闂锛氬す MSS + 璺緞 MTU 鎺㈡祴锛堝叕缃戝彲鐢ㄧ巼 48% -> 100%锛?- 澶嶇敤 HTTP 杩炴帴锛堝厤鎺夋瘡娆℃柊寤?TLS 鐨?30% 鎻℃墜澶辫触锛?- 姣忓垎閽熷洖澶存壘灞€鍩熺綉锛岃嚜鍔ㄥ垏鍥炲揩 400 鍊嶇殑璺緞 - 鐢婚潰蹇冭烦甯э細闈欐鏃剁渷娴侀噺锛屽鎴风浠嶇煡閬撴祦鏄椿鐨?- 閿佸睆鏃剁粰鍑哄彲鎿嶄綔鐨勬彁绀? 鎵嬭〃绔?- 鍐呯疆 GTS Root R4 / GlobalSign Root CA锛屼慨 SSLHandshakeException - 杩炴帴閿欒缈昏瘧鎴愪汉璇濓紙璇佷功/鏃堕棿/瑙f瀽/瓒呮椂鍒嗗埆鎻愮ず锛?- 缁忎腑杞繛涓婂悗鐢ㄦ湇鍔″櫒鎶ョ殑鍦板潃鍒锋柊宸查厤瀵硅褰?- 鏂板灞€鍩熺綉杩炴帴椤甸潰銆佹湇鍔″櫒寤惰繜妫€娴? 鏈嶅姟鍣ㄧ - 鏀寔 chunked 璇锋眰浣擄紙淇瘡 16 绉掍竴娆$殑杩炴帴閿欎贡涓庡ぇ闈㈢Н 502锛?- 缃戠粶璋冧紭鑴氭湰 net-tune.sh锛堝紑鏈鸿嚜鍔ㄥ簲鐢級 - 绠$悊椤碉細鎵归噺鍒犻櫎銆佺綉缁滀綋妫€銆佷笅杞藉尯 - 闅ч亾鏀?HTTP/2 + 鍙岃繛鎺? 鏂囨。 - 璁稿彲鍗忚 / 瀹夎鍓嶅繀璇?/ 瀹夎鍚庨』鐭ワ紙浣滆€咃細灏忛害瀛愶級
31 KiB
局域网遥控 · LAN Remote
用手表(安卓)遥控自己的电脑。同一个 WiFi 下即可,不经过任何服务器。
┌─────────────────┐ 局域网 TCP ┌──────────────────┐
│ 手表控制端 APK │ ─────────────────────────► │ Windows 被控端 │
│ 490×602 大按钮 │ ◄───────────────────────── │ 可见托盘 + 配对码 │
└─────────────────┘ └──────────────────┘
一、先说清楚这个工具「不是什么」
你最初的描述里,有几条我明确没有做,因为它们属于恶意软件特征:
| 你提的要求 | 为什么不做 |
|---|---|
| 注册成系统更新组件、杀毒软件杀不掉 | 这是免杀对抗。正常远控工具(TeamViewer、向日葵、AnyDesk)从不做这个。 |
| 托盘不显示、任务管理器里看不到 | 这是对电脑使用者隐藏自身。 |
| 伪装成普通系统文件常驻 | 持久化 + 伪装。 |
原因很简单:如果被控的是你自己的电脑,你不需要隐藏——你会希望它可见、能管理、能一键断开。而"杀软杀不掉、任务管理器看不到"只在一种场景下有意义:用电脑的人不知道你在控制它。
所以这一版是:被控端全程可见、可随时断开、权限可随时关闭。它是个正经工具,不是木马。
二、快速开始(3 步)
第 1 步:电脑端
把 LanRemote-Agent-Windows.zip 解压到任意目录,双击 LanRemote-Agent.exe。
程序会弹出窗口,窗口中间有一组 6 位配对码,例如:
在手表上输入这组配对码
4 5 4 5 3 4
窗口里同时能看到:
- 当前连接状态
- 防火墙是否放行(没放行会显示橙色提示 + 「一键修复」按钮)
- 「打开接收目录」「退出」
关掉窗口 = 程序继续在后台跑(没有托盘图标)。 想再打开窗口,再双击一次 EXE 就行,不会重复起进程。 想彻底退出,在窗口上点「退出」。
第 2 步:手表端
安装 LanRemote-Watch-1.3.0.apk,打开后点那个大按钮 「自动搜索电脑」。
App 会往局域网广播一个探测包,被控端收到后回自己的名字和端口, 所以手表端不用输 IP、不用管网段、也不用管电脑上装了几个虚拟网卡。
搜到之后点一下那台电脑,输入被控端窗口上的 6 位数字,就连上了。
第 3 步:之后就不用再输了
配对成功后令牌保存在手表上,下次打开 App 会自动搜到并直接连, 连数字都不用再输。
多台电脑就配对多次,列表里点哪个连哪个。长按可以删除配对。
如果搜不到,展开「手动输入地址」可以自己填 IP 和端口 —— 但绝大多数情况 下搜不到都不是 IP 的问题,而是防火墙没放行(见下面第五节)。
工具箱(唯一入口)
被控端目录里有个 工具箱.bat,双击它就能看到所有功能:
============================================================
局域网遥控 . 工具箱 版本 1.3.0
============================================================
1. 启动被控端 日常使用就点这个
2. 一键修复防火墙 连不上时先试这个(需要管理员)
3. 网络自检 还连不上就用它排查
4. 查看使用说明
5. 查看运行日志
6. 打开接收文件夹
7. 查看当前防火墙状态
0. 退出
排查连接问题的推荐顺序:1 → 2 → 3。
| 选项 | 作用 |
|---|---|
| 1 启动被控端 | 确认程序在跑(任务管理器里能看到 LanRemote-Agent.exe) |
| 2 一键修复防火墙 | 弹一次 UAC 点「是」。删掉「阻止」规则 + 补一条程序级放行规则。后者才是治本 |
| 3 网络自检 | 跑起来后到手表上点「测一下」,看电脑窗口有没有 >>> 收到 TCP 连接 <<< |
| 7 查看防火墙状态 | 随时确认四条规则在不在、有没有阻止规则 |
网络自检的两种结果:
- 收到连接 → 包能到电脑,问题在本机(程序没跑 / 端口被占)
- 一个包都没收到 → 包没到,多半是路由器开了「AP 隔离」
三、功能
手表上全部是 58~64dp 的大按钮,两列排布,整页可滚动。
| 分类 | 功能 |
|---|---|
| 媒体 | 播放/暂停、停止、上一曲、下一曲、音量±、静音 |
| 显示 | 投图到电脑(全屏显示,按 Esc 或点一下关闭) |
| 屏幕 | 实时屏幕镜像(默认 4 fps,可调帧率/画质/分辨率)、一键截屏并保存到手表 |
| 音频 | 放音到电脑(用系统 MCI 播放,不弹播放器窗口) |
| 文件 | 发送文件到电脑 / 接收电脑上的文件 |
| 剪贴板 | 手表文字 → 电脑剪贴板;电脑剪贴板 → 手表(自动复制) |
| 语音 | 让电脑语音播报指定文字(系统自带 TTS) |
| 通知 | 在电脑托盘弹通知 |
| 电源 | 关机、重启、锁屏、睡眠、取消关机(二次确认) |
| 其他 | 自定义命令(回显输出和退出码)、查看电脑系统信息 |
| 被动 | 电脑上复制东西时,手表会收到推送提示 |
四、安全设计
这是重点,请看完。
- 首次连接必须配对:电脑端生成 6 位随机码,只在窗口上显示,手表输入正确才能连。
- 令牌制:配对成功后发一个 32 位随机令牌,之后靠令牌免配对码登录。
- 未鉴权拒绝一切命令:没通过握手就发命令,服务端直接拒绝。
- 权限开关实时生效:窗口上可以随时关掉"自定义命令"、"电源操作"、"屏幕镜像",关掉后远程立刻执行不了。
- 无托盘但仍有可见性:按需求去掉了托盘图标;只要有设备连着, 屏幕右上角就会出现一条「已连接」提示条,可一键断开。
- 屏幕共享有强制提示(见下)。
- 不写注册表:所有数据放在程序目录下的
data/,绿色便携。 - 只在局域网:不联外网、不上传任何数据。想更安全就在防火墙里只放行内网段。
屏幕镜像的强制可见提示
只要有人在看你的屏幕,被控端屏幕右上角就会出现一条红色提示条:
┌──────────────────────────────────────────────────────────┐
│ ● 屏幕正在被共享 1 台设备正在观看 · 4.0 fps [停止共享] │
└──────────────────────────────────────────────────────────┘
这条提示的规则:
- 一定有:开始推流就出现,没有开关能关掉它。界面里也没有"隐藏提示"这类选项。
- 本机可一键掐断:点提示条上的「停止共享」,立刻断开所有观看,并通知手表。
- 截屏也会闪一下:单次截屏虽然短暂,同样会闪 2.5 秒提示。
- 无人观看自动停:所有手表都断开后,抓屏线程立即结束,提示条消失,不留后台耗电。
- 关掉权限立刻断:把窗口上的「允许屏幕镜像 / 截屏」取消勾选,正在进行的共享立刻被掐断。
这是本工具和"屏幕监视软件"的分界线:被控端永远知道、永远能停。
⚠️ 自定义命令 = 远程执行任意命令。这是你自己电脑上你自己开的功能, 但请务必:只在你信任的局域网里用,用完可以在窗口里关掉它。
安卓端权限
手表 App 在第一次启动时一次性把需要的权限全要了,之后选图片、选音频、发文件都不再弹窗:
| 权限 | 用途 |
|---|---|
INTERNET / ACCESS_NETWORK_STATE / ACCESS_WIFI_STATE |
连局域网 |
READ_MEDIA_IMAGES / READ_MEDIA_AUDIO / READ_MEDIA_VIDEO(Android 13+) |
列出手表上的媒体文件 |
READ_EXTERNAL_STORAGE(Android 12 及以下,maxSdkVersion=32) |
同上 |
有权限时用内置选择器(带缩略图,直接在 App 里列文件);万一权限被拒,会自动回退到系统选择器, 所以功能不会因为拒权限而不可用。
五、连不上?先看这里
实测中「连不上」最常见的原因只有一个:Windows 防火墙没放行。
家用 WiFi 一般会被 Windows 归类成「公用网络(Public)」, 而公用网络的默认入站动作是阻止。如果没有显式放行规则, 手表的连接会被静默丢包 —— 表现就是「配对码明明对,但一直转圈连不上」。
真机上验证过:
Get-NetFirewallRule -Direction Inbound -Action Allow -> 0 条规则
Get-NetConnectionProfile -InterfaceAlias WLAN -> Public
解决办法:被控端窗口会自动检测并显示一条橙色提示
防火墙未放行,手表连不上。 [一键修复]
点「一键修复」,同意一次管理员请求即可(放行需要管理员权限,这一步躲不掉)。
程序也会在目录下生成 放行防火墙.bat,可以右键「以管理员身份运行」。
手动执行等价于:
netsh advfirewall firewall add rule name="LAN Remote TCP" dir=in action=allow protocol=TCP localport=8765 profile=any
netsh advfirewall firewall add rule name="LAN Remote UDP" dir=in action=allow protocol=UDP localport=8766 profile=any
如果防火墙已放行还是连不上,依次检查:
- 手表和电脑是不是同一个 WiFi(不是手机热点、不是客人网络)
- 路由器有没有开 「AP 隔离」(开了的话同一 WiFi 的设备也互相不通)
- 电脑上是不是有 VPN / 加速器 / 虚拟网卡在抢占路由
(程序会优先选真实网卡地址;本机装 Tailscale 时
hostname会解析到 169.254 链路地址,程序已按网段优先级排除了这种情况) - 端口被别的程序占用(程序会提示「端口被占用」)
六、连不上时:用「设备诊断」
诊断已经合并进手表主程序了,不需要再装单独的 App。
手表上:主界面 ->「设备诊断」 -> 自动开始检测。
它会自动:
- 采集环境信息 —— 厂商/型号/安卓版本/API、屏幕像素与 density、 屏幕 dp 尺寸、字体缩放、是否被识别为手表、各权限的授予状态
- 采集网络参数 —— 连接类型、WiFi 名称/信号、本机所有 IPv4 地址与 前缀长度、DHCP 网关/掩码/DNS
- 真的跑一次局域网自动发现(UDP 广播 4 秒),列出找到的电脑
- 对找到的每台电脑做真实 TCP 连接测试,报告成功/失败与耗时
- 测完自动复制到剪贴板,直接到聊天窗口粘贴发给开发者即可
如果一台都没搜到,可以在诊断界面手动填电脑 IP 点「测一下」—— 它会分别做 UDP 单播探测 和 TCP 连接,能直接区分问题在哪:
| 结果 | 说明 |
|---|---|
| 单播有应答 + TCP 成功 | 网络是通的,问题在广播(路由器多播过滤 / AP 隔离) |
| 单播有应答 + TCP 失败 | 广播被拦,且电脑防火墙拦了 TCP 8765 → 点被控端的「一键修复」 |
| 单播无应答 + TCP 失败 | 根本没通(不同网段 / 电脑没运行 / 防火墙全拦) |
它刻意不收集什么:
- IMEI、序列号、手机号、运营商
- 联系人、短信、通话记录
- 相册、文件列表、剪贴板内容
- 位置坐标(定位权限只用于读取 WiFi 名称,不读经纬度)
- 账号、已安装应用列表
报告内容全部显示在屏幕上,复制由使用者主动触发。
诊断与主程序在同一个 APK 里,共用同一份自动发现实现
(DiscoveryCore),所以不会出现「诊断说能通、主程序连不上」的假象。
底部还有一行「手动测电脑 IP」+「测一下」按钮——广播被拦时, 它会分别做 UDP 单播和 TCP 连接,直接告诉你问题在哪一段。
六点五、电脑端全部图形化(不依赖任何 .bat)
被控端窗口底部一行短按钮:
[ 接收 ] [ 自检 ] [ 说明 ] [ 设置 ] [ 退出 ]
| 按钮 | 作用 |
|---|---|
| 接收 | 打开收到的文件目录 |
| 自检 | 图形化网络自检(见下) |
| 说明 | 窗口里直接看使用说明 |
| 设置 | 投图标题 / 播报音色 / 语速 / 音乐目录 |
| 退出 | 真正结束进程并释放端口 |
图形化自检
不再需要点任何 .bat。 自检直接读被控端自己的计数器, 所以被控端在跑的时候也能诊断(旧的控制台版本要抢端口,跑不了):
● 正常 一切正常,手表应该能连上。
--- 本机网络 ---
192.168.2.64 [默认路由]
>>> 手表上填这个 IP:192.168.2.64
WiFi: 1602
--- 监听 ---
控制端口 TCP 8765: 监听中
发现端口 UDP 8766: 监听中
--- 收到过多少(关键)---
自动发现请求: 12 个
自检请求: 1 个
TCP 连接: 3 次
有数字说明手表的包能到达这台电脑
--- 防火墙 ---
端口放行 TCP: 是
程序级放行: 是 (LanRemote-Agent.exe)
阻止规则: 0 条(针对本程序)
--- 结论 ---
[OK] 一切正常,手表应该能连上。
窗口里还有「复制」和「一键修复防火墙」按钮。
手表上点「连不上?」,这台电脑会自动把自检窗口弹出来 (走 UDP,所以 TCP 被防火墙拦掉时也能到达)。
六点六、手表端:分页 + 37 个功能
连接后的界面是 6 个分页标签,每页最多 7 个按钮 —— 一屏放得下,完全不用滚动。
| 分页 | 功能 |
|---|---|
| 媒体 | 播放/暂停、停止、上一曲、下一曲、音量 +、音量 −、静音 |
| 屏幕 | 屏幕镜像、截屏、投图到电脑、放音到电脑、关显示器、设为壁纸 |
| 文件 | 发送文件、接收文件、剪贴板→电脑、取电脑剪贴板、剪贴板历史、打开文件夹 |
| 语音 | 语音播报、音色、朗读剪贴板、电脑音乐、弹通知 |
| 电脑 | 快捷命令、打开网址、进程管理、性能、磁盘、电量 |
| 系统 | 锁屏、睡眠、关机、重启、取消定时关机、定时关机、设备诊断 |
几个值得说的:
- 电脑音乐 —— 列出电脑音乐目录里的音频,选一个直接在电脑上播
- 音色 —— 列出电脑上所有 TTS 音色(中文 HuiHui / 英文 Zira 等),选一个
- 进程管理 —— 按内存排序列出电脑进程,可以结束
- 剪贴板历史 —— 电脑端保留最近 20 条剪贴板,手表上点一条就取回来
- 设为壁纸 —— 把刚投过去的那张图设成电脑桌面壁纸
- 快捷命令 —— 任务管理器 / 资源管理器 / CMD / 计算器 / 记事本 / 画图 / 控制面板 / 设备管理器
- 定时关机 —— 15 / 30 / 60 / 120 分钟后关机,可取消
安全边界(都有测试):
- 打开网址只允许
http/https,拒绝file://之类 - 播放本机音频只允许音乐目录内的文件
- 快捷命令和文件夹都是白名单,不接受任意路径
- 结束进程必须由用户在手表上确认
关于杀毒软件误报
先说结论
这个工具没有做任何免杀。 它会被某些杀软启发式报毒,这是真的, 我不打算靠混淆/加壳/加密去绕,因为那正是恶意软件的做法 —— 绕过去之后,你也分不清自己装的是不是真有毒。
真正能解决误报的路只有一条:代码签名(要买证书,见下)。
为什么会被盯上
| 特征 | 为什么杀软敏感 | 这是谁的需求 |
|---|---|---|
| PyInstaller 打包 | 打包器的引导代码是启发式的重灾区 | 你要「多文件 exe,双击就跑」 |
| 写注册表 Run 键 | 自启动是持久化的典型手法 | 你要「开机自启」 |
| 无窗口后台常驻 | 隐藏进程是木马的常见形态 | 你要「全程静默」 |
| 监听端口 + 收指令 | 远控和被控端在行为上无法区分 | 这就是远控工具 |
| 提权改防火墙 | 改防火墙规则经常被拦 | 你要「黑终端一闪修防火墙」 |
这五条全是你明确要求的。 它们组合在一起,行为特征上和远控木马是一回事 —— 区别在于:配对码是你自己设的、认证是真的、你不会拿它去连别人的电脑。
我做了哪些合法的降误报
-
--onedir而不是--onefileonefile 每次启动都往临时目录解压自己,这是最容易被判毒的行为之一。 onedir 不解压,启动也更快。 -
--noupx加壳压缩是杀软重点关注对象,明确关掉。 -
完整的版本信息 EXE 里写入了公司名、产品名、文件描述、版本、版权。 未签名二进制有正经的版本资源,比光秃秃的少一点可疑度, 也方便你在杀软里按「厂商 + 产品」加例外。
-
不做任何偷偷摸摸的事 没有自我复制、没有进程注入、没有内存加载、没有关闭杀软、 没有偷偷加防火墙例外。自启写注册表是你勾了勾选框才做的。
你遇到误报时怎么办
优先做成例外,不要关杀软。
被控端目录整个加进排除项即可(就是你放 LanRemote-Agent.exe 那个文件夹):
Windows 安全中心 → 病毒和威胁防护 → 管理设置
→ 排除项 → 添加或删除排除项 → 添加文件夹
→ 选 LanRemote-Agent 那个目录
加文件夹而不是单个 exe —— 因为 _internal\ 里的 dll/pyd 也可能被单独报。
真正根治:代码签名
有了签名,SmartScreen 和大部分杀软就不会再拦。需要:
- 买一张代码签名证书(OV 约 ¥1000-2000/年,EV 更贵但立刻获得 SmartScreen 信誉)
- 用
signtool给LanRemote-Agent.exe和_internal里的 dll 签名 - 加时间戳(否则证书过期后签名失效)
买了证书之后告诉我,我把签名步骤加进构建脚本。
我查到的情况(本机)
这台机器上 Windows Defender 的按需扫描是坏的:
MpSmartConfigInitialize() failed (80070002)
Defender 的 PowerShell 模块(Get-MpComputerStatus 等)也不存在,
所以我没法在这台机器上给你一个权威的"报不报毒"结论。
我不会假装测过。
想自己确认的话,把 EXE 传到 VirusTotal 看一下 —— 那是最快的办法。
七、后台运行方式
按需求设计成没有托盘图标:
- 双击 EXE → 弹出一个小窗口,上面一串 6 位配对码
- 关掉窗口 → 程序继续在后台跑,仍然占着 8765 / 8766 端口
- 任务管理器里能看到
LanRemote-Agent.exe - 再双击一次 EXE → 把已经在跑的那个窗口叫回来,不会重复起进程
(这里用
SO_EXCLUSIVEADDRUSE:Windows 上SO_REUSEADDR会允许 第二个进程绑同一端口,导致单实例判定失效 —— 实测踩到过) - 想彻底退出:窗口上点「退出」
- 运行记录写在
data\运行日志.txt
保留了一条可见性:只要有设备连着,屏幕右上角会出现一条小提示条
(● 已连接:<设备名>,蓝色,带「断开」按钮);屏幕共享时它变红,
并带「停止共享」。没有托盘图标的前提下,这是让「有人正在控制这台电脑」
不被完全隐形的最低限度做法。空闲时它完全不显示。
八、常见问题
Q:手表连不上? A:先看被控端窗口上有没有橙色「防火墙未放行」提示,有就点「一键修复」。 没有的话,装附带的「遥控诊断」跑一次,把结果发给开发者即可定位。
Q:提示"电脑拒绝连接"?
A:被控端程序没在运行(任务管理器里找 LanRemote-Agent.exe),或者端口被改了。
Q:提示"配对码错误"? A:配对码会变——点了"换一组"或者被控端重启过,就用窗口上最新的那组。
Q:下次打开还要输配对码? A:不该要。如果还要,说明上次配对没成功保存。
Q:托盘图标在哪? A:按需求没有托盘图标。关窗后程序继续后台运行;再双击一次 EXE 就能找回窗口。
Q:投图之后电脑上窗口关不掉?
A:点一下图片,或者按 Esc。也可以把设置里的 image_timeout 改成秒数自动关闭。
Q:手表屏幕显示不全?
A:界面是按 490×602 自适应做的:宽度小于 200dp 时自动切成单列并加大按钮。
别的分辨率改 MainActivity.java 里 adaptGridToScreen() 的阈值即可。
九、项目结构
LanRemote/
├── windows/ # 被控端(Python)
│ ├── lanremote/
│ │ ├── protocol.py # 协议常量 + 分块编解码
│ │ ├── config.py # 数据持久化(设备令牌、设置)
│ │ ├── actions.py # 投图/放音/媒体/剪贴板/电源/命令/TTS
│ │ ├── screen.py # 屏幕捕获 + 帧流(Pillow,JPEG)
│ │ ├── discovery.py # UDP 广播应答(手表不用输 IP)
│ │ ├── firewall.py # 防火墙检测 + 一键提权放行
│ │ ├── instance.py # 单实例锁(再次双击找回窗口)
│ │ ├── server.py # TCP 服务 + 配对鉴权 + 命令分发 + 帧广播
│ │ └── ui.py # 小窗口 + 状态提示条(无托盘)
│ ├── run_agent.py # 启动入口
│ └── build_exe.ps1 # PyInstaller --onedir 打包
│
├── android/ # 手表控制端(纯 Java,无第三方依赖)
│ ├── src/com/lanremote/watch/
│ │ ├── Json.java # 自写 JSON(桌面 JVM 也能跑,便于联调)
│ │ ├── B64.java # 自写 Base64(兼容老 Android)
│ │ ├── RemoteClient.java # 协议客户端(纯 Java)
│ │ ├── DiscoveryCore.java # 自动发现核心(纯 Java,可桌面联调)
│ │ ├── Discovery.java # 安卓包装(MulticastLock)
│ │ ├── DeviceStore.java # 设备令牌持久化
│ │ ├── MediaPicker.java # 内置媒体选择器(免系统弹窗)
│ │ └── MainActivity.java # 490×602 自适应大按钮 + 镜像面板
│ ├── AndroidManifest.xml
│ ├── res/ # 图标 + 字符串
│ └── build_apk.ps1 # aapt2+javac+d8+apksigner 手工构建
│
├── diag/ # 早期独立诊断 App 的源码(已合并进主程序,不再单独出包)
│ ├── src/com/lanremote/diag/
│ │ └── DiagActivity.java # 环境采集 + 真实连通测试 + 一键复制
│ ├── AndroidManifest.xml
│ ├── res/ # 图标 + 字符串
│ └── build_diag_apk.ps1 # 复用主程序的 Json/DiscoveryCore 一起编译
│
├── _tests/ # 全部测试
│ ├── test_agent_protocol.py # 被控端协议回归(69 项)
│ ├── check_layout.py # 490×602 布局数学核算(22 项)
│ ├── test_indicator_visible.py# 屏幕共享强制提示(真实界面,9 项)
│ ├── test_simple_ui.py # 小窗口/无托盘/后台常驻(真实界面,12 项)
│ ├── java/JavaClientTest.java # 跨语言联调(48 项)
│ ├── run_stub_server.py # 无界面桩服务端
│ └── verify_all.ps1 # 一键全量验证(7 个阶段)
│
└── _build/ # 构建辅助
十、协议(想自己扩展可以看)
TCP 长连接,每条消息一行 UTF-8 JSON(\n 结尾),二进制用 Base64 分块(256KB/块)。
握手
| 方向 | 消息 |
|---|---|
| 手表→电脑 | {"cmd":"pair","code":"454534","deviceId":"watch-xxx","deviceName":"Watch"} |
| 电脑→手表 | {"ok":true,"token":"<32位>","serverName":"DESKTOP-XXX"} |
| 手表→电脑 | {"cmd":"auth","deviceId":"...","token":"..."} |
命令(需鉴权)
| cmd | 参数 | 说明 |
|---|---|---|
ping |
- | 心跳 |
info |
- | 系统信息 |
media |
action |
playpause/stop/prev/next/volup/voldown/mute |
image |
name,seq,last,data |
投图(分块) |
audio |
name,seq,last,data |
放音(分块) |
clip_set / clip_get |
text / - |
剪贴板 |
say |
text |
语音播报 |
notify |
title,text |
通知 |
power |
action |
shutdown/restart/lock/sleep/abort |
run |
command |
自定义命令 |
file_push |
name,seq,last,data |
上传文件 |
file_list |
- | 列出接收目录 |
file_pull |
path |
下载文件(分块回传) |
screen_start |
fps,maxWidth,quality |
开始实时屏幕镜像(1~15 fps) |
screen_stop |
- | 停止屏幕镜像 |
screenshot |
maxWidth,quality |
单次截屏,回包里直接带 JPEG(base64) |
自动发现(UDP 8766,让手表不用输 IP)
| 方向 | 报文 |
|---|---|
| 手表→广播 | {"lanremote":"LANREMOTE_DISCOVER","proto":1} |
| 电脑→单播 | {"lanremote":"LANREMOTE_REPLY","proto":1,"name":"DESKTOP-X","port":8765,"version":"1.1.0"} |
手表拿到回复后就知道电脑的 IP 和端口,直接连 TCP。与在哪个网段、 电脑有几张网卡都无关。
事件推送(电脑→手表,异步)
| event | 说明 |
|---|---|
clip |
电脑剪贴板变化 |
file |
file_pull 的分块数据 |
frame |
屏幕镜像的一帧(JPEG base64,带 w/h/size/ts) |
screen_stopped |
被控端本机掐断了共享 |
status |
状态消息 |
注意三个容易踩的坑(都是实测踩过的):
- 分块上传时,中间块不回包,只在最后一块回一次。否则回复洪泛会导致后续命令消息错位。
file_pull分块之后还会有一条最终回执,客户端必须读走,不然会污染下一条命令。- 镜像帧是高频事件,客户端读取线程必须把
frame当事件异步处理, 绝不能让frame进入请求-应答队列,否则命令会全部超时。
十一、测试情况
跑 _tests\verify_all.ps1 一键全量验证:
| 项目 | 内容 | 结果 |
|---|---|---|
| Python 被控端协议回归 | 配对/鉴权/媒体/剪贴板/投图/放音/文件/命令/电源/屏幕镜像/截屏/多设备 | 69/69 PASS |
| 手表布局适配核算 | 490×602 在 density 1.0~3.0 下的按钮尺寸与文字容量 | 22/22 PASS |
| 屏幕共享强制提示 | 真实 tkinter 界面 + Win32 EnumWindows 枚举窗口 |
9/9 PASS |
| 小窗口与后台常驻 | 真实界面 + Win32:窗口尺寸、关窗后进程存活、双启动找回窗口、无托盘 | 12/12 PASS |
| Java 协议层编译 | javac 目标 Java 8 | PASS |
| Java ↔ 桩服务端 | 跨语言联调(含帧流、截屏、UDP 自动发现) | 48/48 PASS |
| Java ↔ 打包后 EXE | 端到端跑真交付物 | 48/48 PASS |
| 交付 zip 解压重跑 | 解压到全新目录后运行 EXE 再跑一遍 | PASS |
测试中发现并修掉的真实 bug:
- 分块上传回复洪泛 —— 服务端给每个分块都回一次 ack,客户端只读一次,导致后续所有命令消息错位(
run收到的是file_pull的回包)。改为只在最后一块回包。 - 同名文件拉回旧版本 —— 同名文件第二次上传会存成
name_1.bin,但file_pull("name.bin")会拉到上一轮的旧文件。改为按名字匹配取最新的一份。(靠三方 SHA-256 比对才定位出来:长度完全一致,只有内容不同。) - 损坏图片被当成成功 —— 实测发现传随机字节时 PIL 打开失败并返回错误;确认这是正确行为并加了回归用例。
- 自定义命令的控制台噪音 ——
shutdown /a的报错会打到 stderr,改成全静默。 - 镜像帧污染命令队列 —— 帧是高频事件,必须走事件通道;一旦混入请求-应答队列,所有命令都会超时。已在客户端读取线程里分流。
屏幕相关的安全属性,是被单独验证过的:
_tests/test_indicator_visible.py 启动真实界面(不是桩),开始共享后用 Win32 EnumWindows
在系统窗口列表里查找提示条,确认:开始共享时出现 → 停止后消失 → 再次共享再出现 →
客户端断开后自动消失。这条属性不靠"看代码相信",靠枚举窗口实证。
未能验证的部分(如实说明):
- 没有真机跑 APK。这台机器上没接安卓设备,也没装模拟器。APK 的验证做到了:构建成功、签名校验通过、
aapt dump badging结构正确、dex 里类齐全、协议层在桌面 JVM 上连真实服务端 35/35 通过。但界面在真实手表上的渲染效果没实测过。 - 装到手表上如果布局有问题,改
adaptGridToScreen()的阈值即可。
十二、自己重新构建
# 被控端 → 多文件 EXE
powershell -ExecutionPolicy Bypass -File windows\build_exe.ps1
# 手表端 → APK
powershell -ExecutionPolicy Bypass -File android\build_apk.ps1
# 全量验证
powershell -ExecutionPolicy Bypass -File _tests\verify_all.ps1
APK 用的是工作区里现成的工具链(starklink\toolchain:JDK 17 + Android SDK 34 + build-tools 34),
走 aapt2 → javac → d8 → zipalign → apksigner 手工链,不需要 Gradle。
十三、法律与责任
本工具是你自己的设备之间的局域网遥控工具。
- 只用于你拥有或已获授权控制的电脑。
- 在他人电脑上安装并隐藏运行,属于违法行为(《刑法》285/286 条)。
- 本工具不提供任何隐藏、免杀、伪装能力,就是为了让它不能被用于那种场景。
请合法使用。