用户报了三件事,一起处理。
== 一、发行版介绍里泄漏了服务器公网 IP ==
_build/CHANGELOG.md 的 2.1.3 那节写了服务器公网 IP,而发布脚本
把这个文件当"更新说明"传给 Gitee,于是发行版介绍里就带出去了。
为什么 IP 不该公开:直接打 IP 就绕过 Cloudflare 了 ——
没有 CDN、没有防护、没有速率限制。源站本来就不该被人知道是谁
(这也是为什么 nginx 那层只认域名、中转只绑 127.0.0.1)。
处理:
* 已发的 v2.1.3 发行版介绍用 API 改掉(扫了全部 11 个发行版,只它一个)
* 仓库里 16 个文件里的 IP 全部换成域名或占位符
* 运维脚本改成从环境变量读,公开仓库里不留具体地址
顺手发现**更严重的一处**:旧管理员密码硬编码在
docs/AFTER-INSTALL.txt(会打进服务端 zip)和
installer/license-combined.txt(**安装包的许可页,每个装的人都能看到**)里。
而用户当前密码就含这个串,等于公开了一半。一并清掉,
公开材料里改成"密码在你服务器上"的说明。
== 二、手表端填了地址却连不上 ==
用户截图:填域名(没写协议头)-> 点「检查服务器」报
MalformedURLException: no protocol: <域名>/v1/health
根因:补协议头的逻辑只写在连接那条路(RelayTransport 的构造函数)上,
checkServer() 直接拿原始字符串拼 URL —— 同一个地址,连接能用、检查却崩。
修法:把规范化收到 relayUrl() 这**唯一一个入口**,所有调用方都从那里取。
规则和电脑端一致:IP / localhost / *.local -> http,域名 -> https。
顺带补了 _tests/test_relay_url_normalize.py 盯住它(含 9 个用例实算)。
== 三、Gitee 的 README 被判"可能含有违规信息" ==
Gitee 挂的是软性公告(可自行修改或申诉),但确实挂在那。
逐项看了 README,改掉几处**不准确**且容易误判的表述:
* 原文写"看看学生机是不是在打游戏" —— 这是**错的**:
被控端只装在一台电脑上(讲台那台),根本看不到别的机器。
既不准确又像监控软件,改成"作用于被控端那一台"
* 课堂那几项功能如实说明:改的是本机 hosts、结束的是本机进程,
都可逆,下课点「一键恢复全部」就还原
* "免杀对抗""伪装成系统进程"这类词(虽然是在说"我们不做这些")
本身就在审核词库里,换成正面的说法
* 成品文件名从 1.8.2 更新到当前版本,并且以后发版自动跟着改
(把 README 加进了 bump 脚本;以前一直停在 1.8.2,
用户照着 README 去 releases 根本找不到那个文件)
== 四、做这件事时自己踩的坑(都写进注释了)==
* 批量替换时**没排除 gitignore 的文件**,把本地密码文件也改了
* 替换时往字典字面量中间塞注释,**弄坏了 5 个文件的语法**
* 修的时候手写"坏文件列表",漏了 2 个 ——
改成全仓库扫模式(列表是观察到的,不是穷尽的)
* 清理工具自己又成了泄漏点(脚本里写了要匹配的 IP/密码),
加了 _build/precommit_leak_gate.py 当闸门才抓到
教训:批量替换要先排除不该碰的文件,而且不能往表达式中间塞东西。
== 按用户要求:测完再提交 ==
全量回归 41 个阶段全 PASS(2 项因锁屏 SKIP)
601 个 Python 文件语法通过
pyflakes 干净
安装包完整周期 20/20
对外材料清洁度 15/15
提交前泄漏闸门通过
(这条信息是补写的:上一个提交因为 heredoc 把提交说明打到屏幕而不是
文件,信息变成空的 "remove temp" 了,内容本身是对的。)
22 KiB
ykxmz · 教室电脑手表控制
老师在讲台上抬一下手腕,就能把教室里那台电脑管了。
放课件、放音频、投个图、看看有没有在跑无关程序、下课关机 —— 不用走回讲台去点鼠标。
名字来自"控制手表"的拼音首字母,读作"控制拇指"也行。
先看这个:你是哪一类人
这份文档不短,但不用全看。挑自己那一节就行。
| 你是谁 | 从这里开始 |
|---|---|
| 老师 | 三步装好 → 上课时怎么用 |
| 电教 / 学校信息中心 | 一个学校怎么铺开 → 批量管教室 |
| 开发者 | 自己编译 → 通信协议 |
| 普通用户(家里自己用) | 三步装好 → 连不上怎么办 |
这东西是干嘛的
一句话:用手表控制电脑。
为什么用手表而不是手机?因为老师上课时手上是空的,手机在兜里,掏手机的动作比抬手大得多。手表抬一下就行,还不打断讲课。
它不只能放 PPT。教室里那些事它基本都管得着:
- 课件全屏投到教室大屏
- 放听力音频(电脑音箱比手表响多了)
- 上课时把教室电脑切到专注状态,下课一键恢复
- 下课关机、锁屏
- 喊一声"上课了"让电脑播音(TTS)
电脑那边不用装 Python,一个 EXE 双击就跑,绿色免安装,不留注册表。
三件套
一套完整的部署是三个程序,装在三个地方:
| 装在哪 | 是什么 | 文件 |
|---|---|---|
| 教室电脑(Windows) | 被控端 | ykxmz-setup-2.1.4.exe |
| 小服务器(随身 WiFi / 云主机 / 树莓派) | 中转站 | ykxmz-server-2.1.4.zip |
| 手表(安卓) | 控制端 | ykxmz-watch-2.1.4.apk |
只在一个教室用? 那第三件(中转站)可以先不装,手表和电脑连同一个 WiFi 就能直连。
要在办公室管教室? 那必须自己部署一台中转站(见 跨网络)。
⚠️ 软件里不带任何中转地址
这一点先讲清楚,免得白折腾:
这个软件默认是"空的" —— 没有任何内置的中转服务器地址, 也没有官方服务器可以用,作者不对外提供中转服务。
同一个 WiFi 下用(教室里):不需要中转,装完就能连,什么地址都不用填。
要跨网络用(办公室管教室):你得自己部署一台中转站。 服务端压缩包里有一键脚本,一条命令装好:
bash deploy.sh装完它会打印一个属于你自己的公网地址,把它填到电脑和手表上。 详见 装中转站。
为什么不做一个"填个公共地址就能用"?因为中转站的带宽是实打实的成本, 谁用谁出;而且中转站能看到经过它的字节流, 自己搭一台,数据才真正只在你自己手里。
三步装好
第一步:电脑上装被控端
双击 ykxmz-setup-2.1.4.exe,按提示装完(默认装到 C:\Program Files\ykxmz)。
第一次运行会弹出窗口,填两样:
- 6 位数字 —— 自己定,好记就行,比如
789113 - 两个勾:
设置开机自启、放行防火墙(放行那个要过一次管理员确认)
点确定,屏幕会黑闪一下(在加防火墙规则),然后窗口消失,程序转到后台跑。
窗口关了程序还在跑,这是故意的 —— 上课时讲台上不该有个窗口杵着。 想再打开窗口,再双击一次程序图标就行,不会起第二个进程。 想彻底退出,窗口上点「退出」。
不想用安装包的话,
ykxmz-agent-2.1.4.zip是绿色版: 解压出来直接跑ykxmz.exe,整个文件夹拷走就能换台机器用,不写注册表。
第二步:手表上装 App
把 ykxmz-watch-2.1.4.apk 传到手表装上。手表可能提示"未知来源",去设置里允许一下。
打开 App,首页有四个入口:
┌──────────────────────────┐
│ 控制手表 │
│ 和电脑连同一个 WiFi,不用输 IP │
├──────────────────────────┤
│ 中转设置(跨网络用) │ ← 办公室管教室才需要
│ 局域网连接(同一个 WiFi) │ ← 教室里用这个
│ 输入配对码连接 │ ← 跨网络连的时候用
├──────────────────────────┤
│ 服务器:还没测 检查服务器 │
└──────────────────────────┘
│ 已配对的电脑(点一下直连) │
│ DESKTOP-XXXX │
└──────────────────────────┘
教室里(手表和电脑连同一个 WiFi):点「局域网连接」→「自动搜索电脑」→ 点搜到的电脑 → 输第一步那 6 位数字。
不用输 IP。 App 往局域网广播一个包,电脑收到会自报家门,所以电脑上装了几块虚拟网卡、IP 是多少都不用管。
第三步:以后不用再输了
配对一次之后令牌存在手表里,下次打开 App 自动就连上,数字都不用再输。
多台电脑就多配对几次,列表里点哪个连哪个。长按删除配对。
上课时怎么用
连上之后手表上是一排标签页,左右滑动切换。下面挑上课最常用的讲。
教室
这一页是给上课准备的。
先说清楚作用范围:被控端装在一台 Windows 电脑上 —— 就是讲台那台,或者你自己的机器。 所有操作只影响这一台,看不到也管不着其他电脑。 装的时候要输配对码,没配对码连不上。
| 按钮 | 干什么 |
|---|---|
| 课堂专注模式 | 在本机 hosts 里把常见游戏站点指到本地,本机打开是空白页 |
| 结束游戏进程 | 扫本机正在跑的常见游戏进程,结束掉 |
| 看有哪些限制 | 列出当前开了哪些,别自己忘了 |
| 一键恢复全部 | 把这节课改的全撤掉,下课必点 |
怎么实现的、有什么边界,一并说清楚:
- 屏蔽是改本机
hosts文件,不装驱动、不装服务 - 只挡"随手点开":懂行的人自己把 hosts 里那几行删了就过去了。 它防的不是刻意行为,是减少无关干扰
- 站点列表是写死的常见站点,换一个站就得重新加
- 全部改动可逆,点「一键恢复全部」或「下课清理」就还原
- 重启后保不保留,取决于你们学校有没有装还原卡
屏幕
- 实时屏幕镜像 —— 手表上看被控端那台电脑在显示什么(低头确认课件放没放对,不用走回讲台)
- 一键截屏 —— 存到手表相册
⚠️ 屏幕只要被看,电脑右上角就有一条红色提示条,关不掉。 这是刻意做的:谁在看,电脑前面的人必须能立刻知道。 电脑前面的人随时可以点提示条上的「停止共享」把你掐断。
媒体 / 音频
放课件自带的音频,或者直接放电脑上的音乐。音量、静音、上一曲下一曲都在这一页。
放音到电脑走的是系统自带的播放器,不会弹出一个播放器窗口盖住课件。
投影
投图到电脑 —— 手表上选一张图,全屏显示在教室大屏上。点一下或者按 Esc 关掉。
考试时贴个"考试中,请勿喧哗"的图,比在黑板写快。
系统
关机、重启、锁屏、睡眠,都有二次确认。定时关机可以设"30 分钟后关",下课忘了也不怕。
另外这一页最上面有三个按钮:
- 回退上一步 —— 刚才手滑点错了,撤销
- 回退全部 —— 把能撤的都撤了
- 看能回退什么 —— 列出还能撤哪些
能撤的是各种开关和画面。关机和删文件这类撤不了,也不会出现在列表里,免得你以为按了就能回来。
其他几页
- 文件 —— 往电脑传文件、从电脑拿文件(课件来回倒腾用)
- 语音 —— 让电脑念一段字。"上课了,请回到座位"这种
- 剪贴板 —— 手表上的字发到电脑剪贴板,或者把电脑复制的字拿到手表
- 通知 —— 在电脑上弹个提示
- 效率 —— 待办清单
- 娱乐 —— 掷骰子、抽奖、抛硬币(课堂互动随机点名能用)
跨网络:在办公室管教室
教室的电脑和你在的办公室不是一个网,直连不可能。这时候要装中转站。
原理说白了就是:两边都主动连到那台小服务器上,服务器按配对码把两条线接起来。
手表 ──┐ ┌── 教室电脑
├── 中转站(公网) ──┤
办公室 ──┘ └── 你手机
中转站只是水管子,它不认识里面流的是什么。 配对、令牌校验、权限判断全在电脑那边做。所以就算中转机被人拿走了,也进不了你的电脑。
装中转站
中转站就是一台能上外网的 Linux 小机器(随身 WiFi、树莓派、云主机都行, Debian 12 / Ubuntu 实测可用,512 MB 内存就够)。
把 ykxmz-server-2.1.4.zip 解压到那台机器上,然后一条命令:
bash deploy.sh
它会自己做完这些,不用你一步步敲:
- 装 cloudflared(已经装了就用现成的)
- 把中转程序装到
/root/lanremote-relay/ - 网络调优(keepalive、MSS、MTU、开机加速)
- 注册 systemd 服务,设成开机自启
- 配好 Cloudflare 隧道
- 启动、自检,然后把公网地址打印出来
装完会打印一个属于你自己的公网地址,长这样:
https://relay.你的域名
记住这个地址,电脑和手表都要填它。忘了就在那台机器上跑
lanremote-url,或者浏览器打开 http://<小机器的IP>:8080/url 查。
三种常见情形
# ① 机器上已经有隧道凭据(/root/.cloudflared/*.json)
bash deploy.sh
# ② Cloudflare 给了一个隧道 Token(Zero Trust 后台复制的那串)
TUNNEL_TOKEN=eyJhIjoi... bash deploy.sh
# ③ 换域名 / 只装中转、不要隧道
bash deploy.sh --hostname relay.你的域名
bash deploy.sh --skip-tunnel
看全部参数:bash deploy.sh --help
用环境变量而不是
--token:写进命令行的 Token 会留在 shell 历史里。
重跑一遍 = 升级
deploy.sh 是幂等的:已经装过的机器再跑一次就是升级。
程序文件会被覆盖成新的,而设备数据、配对码、隧道凭据都不会被冲掉。
需要准备什么
- 一台能上外网的 Linux 小机器(Debian 12 / Ubuntu,512 MB 内存够)
- 一个 Cloudflare 账号 + 一个域名(隧道要用)
- 不需要装 Python 第三方包 —— 服务端是纯标准库,不装任何依赖
告诉电脑中转地址
在电脑上(cmd 或 PowerShell):
ykxmz.exe --relay "192.168.1.50:8080;https://relay.你的域名"
两个地址用分号隔开,按顺序试,第一个通的就用。这样在教室里走局域网(快、稳),出了学校自动切隧道。
如果只在跨网络的场景用,也可以只填一个域名:
ykxmz.exe --relay https://relay.你的域名
看看当前用的是哪个地址:
ykxmz.exe --relay-status
告诉手表中转地址
手表首页点「中转设置」,填上面同一个地址。
然后回家首页点「输入配对码连接」,填电脑上那 6 位数字。
中转站的公网地址打开是什么
打开就是一个普通的官网页面 —— 介绍、截图、三个文件的下载按钮, 最新版的安装包挂在上面,点一下就能下。
安装包和截图这类静态文件由 Cloudflare 的边缘节点直接发送, 所以下载速度和那台小机器的带宽无关(实测比走隧道回源快几十倍)。
想看运行状态、排查问题、重启服务,看服务端压缩包里的
README-server.txt —— 那份文档是给运维的人看的,里面写了日常命令。
一个学校怎么铺开
如果是一个学校几十间教室,上面那套手工装会很累。这一节给电教。
被控端
ykxmz.exe 是绿色单文件,整个文件夹拷过去就能用。批量装有两种办法:
办法一:机房同传 / 还原卡
把文件夹放进母盘镜像,开机自启那条在程序里勾上就行。注意 data/ 目录里存着这台机器的配对码和令牌,母盘里别带上别的机器的 data/,否则一堆机器共用一个码。
办法二:脚本推
# 把文件夹拷到目标机,然后静默设置配对码和自启
Copy-Item -Recurse "\\服务器\share\ykxmz" "C:\ykxmz"
Start-Process "C:\ykxmz\ykxmz.exe" -ArgumentList `
'--code 789113', '--relay 192.168.1.50:8080;https://relay.你的域名', '--auto-start', '--silent'
--silent 不弹窗口,适合批量。静默模式下配对码一定要用参数指定,不然每台机器随机生成一个,你也不知道是多少。
配一次码,服务器就记住了
这是这套东西比较省心的一点:一台电脑第一次设的配对码,服务器会记住。
以后同一台电脑再报别的码(重装、误操作、母盘没清干净),服务器会自动改回原来那个。所以你不会因为手抖把一台机器的码搞乱,手表永远认得出它。
反过来,一个码不能同时属于两台电脑,服务器会拒绝后来那个。这也是为什么母盘里不能带别人的 data/。
网络规划
- 中转站放在学校出口能上外网的地方,带宽不用大,它是控制通道不是视频通道
- 走局域网时是手表直连电脑,完全不经过中转站,所以一个班几十只手表也不挤服务器
- 教室之间如果是隔离的 VLAN,那手表和电脑得在同一个 VLAN 里,或者干脆都走中转
权限怎么收
被控端窗口上有几个权限开关,随时关,关了远程立刻执行不了:
- 允许自定义命令
- 允许电源操作
- 允许屏幕镜像 / 截屏
给班主任的电脑可以把"自定义命令"和"电源操作"关掉,只留日常那几个按钮。
连不上怎么办
按这个顺序排查,九成问题在第二步。
第一步:程序在跑吗
电脑任务管理器里找 ykxmz.exe。没有就双击 EXE 启动。
第二步:防火墙放行了吗
被控端窗口上如果显示橙色提示"防火墙未放行",点旁边的一键修复,过一次管理员确认。
或者用目录里的 工具箱.bat → 选 2。
这一条是最常见的坑。很多学校的电脑装了第三方安全软件,它会把新程序的入站连接全挡了。 一键修复会加一条程序级的放行规则(不是端口级),这个才管用。
第三步:还是不行就跑网络自检
工具箱.bat → 选 3,然后到手表上点「检查服务器」。
看电脑窗口:
- 出现
>>> 收到 TCP 连接 <<<→ 包到电脑了,问题在电脑本机(程序没跑 / 端口被占) - 一个包都没收到 → 包没到电脑,八成是路由器开了**「AP 隔离」**(也叫客户端隔离),去路由器后台关掉
手表上会显示具体原因
App 把错误翻译成人话了,看到哪句就照做:
| 手表上显示 | 什么意思 |
|---|---|
| 证书校验失败 | 手表系统太旧,或者网络被人劫持 |
| 证书时间不对 | 把手表的日期时间调成自动获取,这个很常见 |
| 连接超时 | 网络慢,或者被防火墙挡了 |
| 电脑拒绝连接 | 电脑上 ykxmz 没开 |
| 域名解析不了 | 手表没联网,检查 WiFi |
| 连不上中转 | 中转站挂了,或者地址填错了 |
首页那个「检查服务器」能测出到中转的延迟,快/一般/慢会用不同颜色标出来。
安全
被控端装的往往是公家的电脑,所以这套工具的安全设计偏"让人知道"而不是"低调运行"。
每次连接都要配对。 电脑上生成 6 位数字,只在窗口上显示,手表输对了才连得上。
配对之后发令牌。 32 位随机串存在手表上,之后靠它登录,不用每次输数字。
没通过握手,一条命令都不执行。 服务端直接拒。
屏幕被看,电脑上一定有提示条。 关不掉,没有开关。电脑前面的人随时能掐断。
权限开关实时生效。 窗口上关掉哪一项,远程立刻做不了那一项。
不写注册表。 数据全在程序目录下的 data/ 里,拔了硬盘拷走就能换台机器跑。
只在你自己填的地址之间通信。 局域网模式下完全不联外网。跨网络模式只连你自己那台中转站,不经过任何第三方。
所有行为都在明面上。 被控端在托盘有图标、窗口看得见、随时可以退出、 权限可以逐项关闭。它不做任何"让机器上其他程序看不见自己"的事 —— 因为管的是自己要用的一台电脑,能看见、能随时停,才让人放心。
代码全公开。 三端加起来不到两万行,都是纯标准库, 想看它到底干了什么直接读源码就行。
自己编译
给开发者。三端都是零第三方依赖,不用 npm、不用 Gradle 拉一堆东西。
被控端(Windows)
Python 3.11 + tkinter + Pillow。打包用 PyInstaller。
windows\build_exe.ps1
# 产物:dist_exe\ykxmz\ykxmz.exe
控制端(安卓手表)
纯 Java,没有 Gradle,直接命令行编:
android\build_apk.ps1
# 流程:aapt2 → javac → d8 → zipalign → apksigner
# 产物:dist\ykxmz-watch-2.1.4.apk
最低支持 Android 7.0(API 24),targetSdk 34。
服务器端
Python 3 标准库,没有第三方包。
bash relay/deploy.sh
deploy.sh 是一键部署(装服务 + 调优 + 配隧道 + 自检);
只想调网络参数的话是 relay/deploy-tune.sh。
老的 relay/install.sh(匿名快速隧道)仍在包里,只为兼容以前装过的机器。
跑测试
_tests\verify_all.ps1
会跑编码审计、协议回归、打包验证、Java 编译、端到端,最后打一张汇总表。
打包三个成品
python _build\package_remote.py
出三个文件:ykxmz-agent-*.zip、ykxmz-server-*.zip、ykxmz-watch-*.apk。
通信协议
给要改代码或者做二次开发的人。
传输是 TCP 上的换行分隔 UTF-8 JSON,一行一个消息。
{"cmd": "handshake", "code": "789113", "deviceId": "..."}
{"ok": true, "token": "32位随机串", "serverName": "DESKTOP-XXXX"}
二进制数据(图片、文件、音频)走 Base64 分块传输,单次会话上限 256 MB。
跨网络时同样的消息套在 HTTP 长轮询里(单次挂 60 秒, 有数据立刻返回、不空等),中转站按配对码把两端字节对接,它不解析内容。
跨网络这条链路上还有几个实测调出来的设计,改代码前值得知道:
- 隧道两种协议同时跑(QUIC + HTTP/2,合计 10 条连接)。 两种协议各有各的毛病 —— QUIC 尾部延迟好但建连容易超时, HTTP/2 建连可靠但丢包时容易被队头阻塞卡住。 同时跑就是互相兜底:一种犯病,另一种还在服务。
- 长轮询 60 秒。隧道每次请求要多花 1~2 秒,这个开销是按次数收的; 拉到 60 秒把请求数降到 1/4,撞上丢包窗口的概率也降到 1/4。 命令响应速度不变(有数据立刻返回,不是等满 60 秒)。
- 偶发失败不当场判离线。4G 链路上本来就有百分之几的偶发抖动、 一两秒自己恢复;连续失败 3 次才算真的离线。
- 静态资源交给 Cloudflare 边缘缓存。下载走边缘节点, 不用穿隧道回那台小机器,快几十倍也更省流量。
Java 和 Python 两端都实现了同一套协议,_tests/java/ 下有互相打通的测试。
常见问题
问:手表能连多台电脑吗? 能。配对多台,列表里点哪个连哪个。
问:一台电脑能被几只手表连? 可以多只,屏幕镜像会显示"几台设备正在观看"。教室场景一般一只就够。
问:换台电脑要重新配对吗? 要。配对码跟着电脑走,不是跟着手表走。
问:电脑重装系统了,配对码会变吗?
不会。只要 data/ 目录还在,服务器就认原来那个码。母盘部署时注意别把 data/ 一起发出去。
问:走中转会不会很慢? 实测局域网 3 毫秒左右;跨网络要绕到中转站再回来,约 1~2 秒(取决于你那台小机器的线路)。所以同一个 WiFi 一定走局域网,程序会优先选它。
问:必须自己搭中转站吗?能用作者的服务器吗?
必须自己搭。软件不内置任何中转地址,作者也不对外提供中转服务。
跨网络的话,服务端 zip 里有 deploy.sh,一条命令装好(见 装中转站)。
只在同一个 WiFi 下用的话,压根不需要中转站。
问:中转站会不会看到我的数据? 它转发的是加密前的字节流,所以技术上能。这正是"自己搭一台"的理由 —— 搭在你自己手上,数据就只经过你自己的机器。
问:下载安装包为什么比以前快这么多? 最新版把静态文件交给 Cloudflare 的边缘节点缓存了。原来每次下载都要穿过隧道回到那台小机器上取,实测只有几 KB/s,15 MB 的安装包根本下不动;现在从离你最近的边缘节点直接发,实测快了四十多倍,也不占小机器的流量。
问:能在手机上用吗? 协议是通的,但界面是按手表的小屏幕设计的(大按钮、两列、可滚动),手机上会显得按钮特别大。要做手机版得重写布局。
问:收费吗? 看 LICENSE.txt。里面有使用红线,装之前看一眼。
出问题了
先看 连不上怎么办。
还不行的话,把这几样发过来,能定位得快很多:
- 手表上显示的那句错误(App 现在是中文的,直接念出来就行)
- 电脑上
工具箱.bat→ 选5的日志内容 - 跨网络的话,把你那台中转站的公网地址发过来(不用给任何密码)
作者:小麦子 许可见 LICENSE.txt