mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 14:50:45 +00:00
被控端 - 修复 MTU 问题:夹 MSS + 路径 MTU 探测(公网可用率 48% -> 100%) - 复用 HTTP 连接(免掉每次新建 TLS 的 30% 握手失败) - 每分钟回头找局域网,自动切回快 400 倍的路径 - 画面心跳帧:静止时省流量,客户端仍知道流是活的 - 锁屏时给出可操作的提示 手表端 - 内置 GTS Root R4 / GlobalSign Root CA,修 SSLHandshakeException - 连接错误翻译成人话(证书/时间/解析/超时分别提示) - 经中转连上后用服务器报的地址刷新已配对记录 - 新增局域网连接页面、服务器延迟检测 服务器端 - 支持 chunked 请求体(修每 16 秒一次的连接错乱与大面积 502) - 网络调优脚本 net-tune.sh(开机自动应用) - 管理页:批量删除、网络体检、下载区 - 隧道改 HTTP/2 + 双连接 文档 - 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
232 lines
8.0 KiB
Python
232 lines
8.0 KiB
Python
# -*- 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())
|