Files
ykxmz/_build/patch_watch_relay.py
xmz7891 b62ac865ea ykxmz 1.5.0:修复手表端 TLS 证书校验 + MTU 修复网络稳定性
被控端
- 修复 MTU 问题:夹 MSS + 路径 MTU 探测(公网可用率 48% -> 100%)
- 复用 HTTP 连接(免掉每次新建 TLS 的 30% 握手失败)
- 每分钟回头找局域网,自动切回快 400 倍的路径
- 画面心跳帧:静止时省流量,客户端仍知道流是活的
- 锁屏时给出可操作的提示

手表端
- 内置 GTS Root R4 / GlobalSign Root CA,修 SSLHandshakeException
- 连接错误翻译成人话(证书/时间/解析/超时分别提示)
- 经中转连上后用服务器报的地址刷新已配对记录
- 新增局域网连接页面、服务器延迟检测

服务器端
- 支持 chunked 请求体(修每 16 秒一次的连接错乱与大面积 502)
- 网络调优脚本 net-tune.sh(开机自动应用)
- 管理页:批量删除、网络体检、下载区
- 隧道改 HTTP/2 + 双连接

文档
- 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
2026-10-06 19:01:05 +08:00

232 lines
8.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""让 RemoteClient 支持中转(跨网络)。
改动很小,因为把"怎么传字节"抽成了 Transport:
SocketTransport —— 原来的局域网直连
RelayTransport —— 走中转服务器
RemoteClient 里协议相关的代码(readLoop / call / awaitReply / send)
一行都不用动。
新增 connectViaRelay(baseUrl, code, token, listener, timeout):
和原有 connect(host, port, ...) 走同一套握手流程。
"""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
CLIENT = ROOT / "android" / "src" / "com" / "lanremote" / "watch" / "RemoteClient.java"
JSON = ROOT / "android" / "src" / "com" / "lanremote" / "watch" / "Json.java"
# ---------------- 1. Json.escape ----------------
JSON_ANCHOR = " public static String write(Object value) {"
JSON_NEW = ''' /**
* 把字符串转义成能安全塞进 JSON 字符串字面量的形式。
* 只处理引号、反斜杠和控制字符 —— 中文不用转,JSON 本身是 UTF-8。
*/
public static String escape(String text) {
if (text == null) {
return "";
}
StringBuilder out = new StringBuilder(text.length() + 16);
for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
switch (ch) {
case '"':
out.append("\\\\\\"");
break;
case '\\\\':
out.append("\\\\\\\\");
break;
case '\\n':
out.append("\\\\n");
break;
case '\\r':
out.append("\\\\r");
break;
case '\\t':
out.append("\\\\t");
break;
default:
if (ch < 0x20) {
out.append(String.format("\\\\u%04x", (int) ch));
} else {
out.append(ch);
}
break;
}
}
return out.toString();
}
''' + JSON_ANCHOR
# ---------------- 2. RemoteClient ----------------
FIELD_OLD = ''' private Socket socket;
private BufferedReader reader;'''
FIELD_NEW = ''' /**
* 传输层。局域网直连时是 SocketTransport,跨网络时是 RelayTransport。
* 抽出来之后协议逻辑完全不用管数据是走 TCP 还是走中转的 HTTPS。
*/
private Transport transport;
private BufferedReader reader;'''
CONNECTED_OLD = ''' public boolean isConnected() {
return running && socket != null && !socket.isClosed();
}'''
CONNECTED_NEW = ''' public boolean isConnected() {
return running && transport != null && !transport.isClosed();
}
/** 当前传输方式的描述,界面上用来显示"直连"还是"经中转"。 */
public String transportName() {
if (transport == null) {
return "";
}
return transport instanceof RelayTransport ? "中转" : "直连";
}
public RelayTransport relayTransport() {
return transport instanceof RelayTransport
? (RelayTransport) transport : null;
}'''
CONNECT_OLD = ''' Socket sock = new Socket();
sock.connect(new InetSocketAddress(host, port), timeoutMs);
sock.setSoTimeout(0);
sock.setTcpNoDelay(true);
this.socket = sock;
this.reader = new BufferedReader(new InputStreamReader(sock.getInputStream(), UTF8), 8192);
this.writer = sock.getOutputStream();
Thread thread = new Thread(new Runnable() {'''
CONNECT_NEW = ''' Socket sock = new Socket();
sock.connect(new InetSocketAddress(host, port), timeoutMs);
sock.setSoTimeout(0);
sock.setTcpNoDelay(true);
return startTransport(new Transport.SocketTransport(sock), code, token,
listener, timeoutMs);
}
/**
* 经中转服务器连接(跨网络)。
*
* 手表没法直连电脑(两边都在 NAT 后面),所以两边都主动连出去,
* 由中转按配对码把两条连接对上。中转只转发字节、不认识协议内容,
* 所以电脑那边的配对和令牌校验照样生效。
*/
public Map<String, Object> connectViaRelay(String baseUrl, String code,
String token, Listener listener,
int timeoutMs) throws IOException {
close();
RelayTransport relay = new RelayTransport(baseUrl, code, deviceName);
if (!relay.attach()) {
String reason = relay.getLastError();
relay.close();
throw new IOException(reason == null || reason.isEmpty()
? "连不上中转服务器" : reason);
}
return startTransport(relay, code, token, listener, timeoutMs);
}
/** 握手 + 起读取线程。局域网和中转共用这一段。 */
private Map<String, Object> startTransport(Transport channel, String code,
String token, Listener listener,
int timeoutMs) throws IOException {
this.listener = listener;
this.running = true;
this.replies.clear();
this.transport = channel;
this.reader = new BufferedReader(
new InputStreamReader(channel.input(), UTF8), 8192);
this.writer = channel.output();
Thread thread = new Thread(new Runnable() {'''
CLOSE_OLD = ''' public void close() {
running = false;
try {
if (socket != null) {
socket.close();
}
} catch (IOException ignored) {
// 关闭时的异常无需处理
}
socket = null;
reader = null;
writer = null;
replies.clear();
}'''
CLOSE_NEW = ''' public void close() {
running = false;
try {
if (transport != null) {
transport.close();
}
} catch (Exception ignored) {
// 关闭时的异常无需处理
}
transport = null;
reader = null;
writer = null;
replies.clear();
}'''
def main() -> int:
ok = True
# ---- Json.escape ----
text = JSON.read_text(encoding="utf-8")
if "public static String escape(" in text:
print(" Json.escape 已存在")
elif JSON_ANCHOR not in text:
print(" [失败] Json 锚点没找到")
ok = False
else:
text = text.replace(JSON_ANCHOR, JSON_NEW, 1)
JSON.write_text(text, encoding="utf-8")
print(" Json.escape 已加")
# ---- RemoteClient ----
text = CLIENT.read_text(encoding="utf-8")
if "connectViaRelay" in text:
print(" RemoteClient 已经改过")
else:
for old, new, tag in ((FIELD_OLD, FIELD_NEW, "字段换成 Transport"),
(CONNECTED_OLD, CONNECTED_NEW, "isConnected 用 Transport"),
(CONNECT_OLD, CONNECT_NEW, "connect 拆出 startTransport + connectViaRelay"),
(CLOSE_OLD, CLOSE_NEW, "close 关 Transport")):
if old not in text:
print(f" [失败] {tag} —— 锚点没找到")
ok = False
continue
text = text.replace(old, new, 1)
print(f" {tag}")
CLIENT.write_text(text, encoding="utf-8")
# 检查还有没有裸用 socket 的地方
text = CLIENT.read_text(encoding="utf-8")
leftovers = [line.strip() for line in text.splitlines()
if "socket" in line and "Transport" not in line
and "Socket" not in line and "//" not in line]
if leftovers:
print(f" [注意] 还有裸用 socket 的行:{leftovers}")
else:
print(" 没有残留的裸 socket 引用")
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())