mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 19:30:42 +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 + 双连接 文档 - 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
309 lines
12 KiB
Python
309 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""安卓端:局域网断了就无缝切回中转服务器。
|
|
|
|
用户要求:
|
|
"检测到局域网连不上了,就无缝链接到中转服务器"
|
|
|
|
场景:
|
|
手表和电脑本来在同一 WiFi,走局域网直连(快、稳)。
|
|
然后手表走出 WiFi 范围了 / 电脑换了网络 / WiFi 断了。
|
|
→ 应该**自动**切回中转,用户不用管。
|
|
|
|
做法:
|
|
1. 记住"当前是局域网直连"以及连的是谁
|
|
2. onDisconnected 时,如果刚才走的是局域网 -> 自动经中转重连
|
|
3. 反过来:中转也断了就重试几次,别立刻放弃
|
|
4. 全程给用户提示,但不能弹回连接页打断操作
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
MAIN = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\android\src"
|
|
r"\com\lanremote\watch\MainActivity.java")
|
|
|
|
# ---------- 1. 加状态字段 ----------
|
|
|
|
FIELD_OLD = ''' private void connect(final String host, final int port, final String code,
|
|
final String token, final String key) {
|
|
if (busy) {
|
|
return;
|
|
}'''
|
|
|
|
FIELD_NEW = ''' // ---- 断线自动切换用的状态 ----
|
|
/** 当前是不是走局域网直连(断了要切回中转)。 */
|
|
private volatile boolean lanDirect;
|
|
/** 刚才连的地址/配对码/令牌,切回中转时要用。 */
|
|
private volatile String lastHost = "";
|
|
private volatile int lastPort;
|
|
private volatile String lastCode = "";
|
|
private volatile String lastToken = "";
|
|
/** 正在自动切换中,避免重入。 */
|
|
private volatile boolean autoSwitching;
|
|
/** 自动重试次数,防止无限刷屏。 */
|
|
private int autoRetry;
|
|
|
|
private void connect(final String host, final int port, final String code,
|
|
final String token, final String key) {
|
|
if (busy) {
|
|
return;
|
|
}'''
|
|
|
|
# ---------- 2. connect 时记录状态 ----------
|
|
|
|
REC_OLD = ''' busy = true;
|
|
setStatus("正在连接 " + host + ":" + port + " …", C_WARN);
|
|
appendLog("连接 " + host + ":" + port);'''
|
|
|
|
REC_NEW = ''' busy = true;
|
|
// 记下来:断线时要用这些信息切回中转 / 重连
|
|
lastHost = host;
|
|
lastPort = port;
|
|
if (code != null) {
|
|
lastCode = code;
|
|
}
|
|
if (token != null) {
|
|
lastToken = token;
|
|
}
|
|
setStatus("正在连接 " + host + ":" + port + " …", C_WARN);
|
|
appendLog("连接 " + host + ":" + port);'''
|
|
|
|
# ---------- 3. 成功连上后标记是不是局域网 ----------
|
|
|
|
MODE_OLD = ''' if (serverLabel != null) {
|
|
serverLabel.setText("已连接 " + (finalName == null ? host : finalName));
|
|
}'''
|
|
|
|
MODE_NEW = ''' // 记住这次是不是局域网直连 ——
|
|
// 局域网断了要自动切回中转,靠的就是这个标记。
|
|
lanDirect = finalLanDirect;
|
|
autoRetry = 0;
|
|
if (serverLabel != null) {
|
|
serverLabel.setText("已连接 "
|
|
+ (finalName == null ? host : finalName)
|
|
+ (finalLanDirect ? "(局域网)" : "(中转)"));
|
|
}'''
|
|
|
|
# 把"是不是局域网"这个判断结果传出来
|
|
LAN_FLAG_OLD = ''' String error = null;
|
|
String newToken = null;
|
|
String serverName = null;
|
|
boolean ok = false;'''
|
|
LAN_FLAG_NEW = ''' String error = null;
|
|
String newToken = null;
|
|
String serverName = null;
|
|
boolean ok = false;
|
|
// 这次连上的是局域网还是中转(决定断线后怎么救)
|
|
boolean usedLan = !relayUrl().isEmpty() ? false : true;'''
|
|
|
|
# 直连成功时置 true
|
|
DIRECT_OK_OLD = ''' appendLog("已切换为局域网直连:" + directIp
|
|
+ ":" + directPort);'''
|
|
DIRECT_OK_NEW = ''' appendLog("已切换为局域网直连:" + directIp
|
|
+ ":" + directPort);
|
|
usedLan = true;'''
|
|
|
|
# 退回中转时置 false
|
|
FALLBACK_OLD = ''' appendLog("直连失败,退回中转:"
|
|
+ Json.str(direct, "error", "未知原因"));'''
|
|
FALLBACK_NEW = ''' appendLog("直连失败,退回中转:"
|
|
+ Json.str(direct, "error", "未知原因"));
|
|
usedLan = false;'''
|
|
|
|
# final 变量
|
|
FINAL_OLD = ''' final boolean success = ok;
|
|
final String finalError = error;
|
|
final String finalToken = newToken;
|
|
final String finalName = serverName;'''
|
|
FINAL_NEW = ''' final boolean success = ok;
|
|
final String finalError = error;
|
|
final String finalToken = newToken;
|
|
final String finalName = serverName;
|
|
final boolean finalLanDirect = usedLan && ok;'''
|
|
|
|
# ---------- 4. onDisconnected 自动切回中转 ----------
|
|
|
|
DISC_OLD = ''' public void onDisconnected(final String reason) {
|
|
|
|
// 换连另一台电脑后,「功能」页不能还显示上一台的分组
|
|
featureCache = null;
|
|
featureGroups = null; ui.post(new Runnable() {'''
|
|
|
|
DISC_NEW = ''' public void onDisconnected(final String reason) {
|
|
|
|
// 换连另一台电脑后,「功能」页不能还显示上一台的分组
|
|
featureCache = null;
|
|
featureGroups = null;
|
|
|
|
// ★ 局域网断了 -> 无缝切回中转服务器。
|
|
// 用户从 WiFi 走出去、或者电脑换了网络,都会走到这里。
|
|
// 不打断操作,自动接上。
|
|
if (lanDirect && !autoSwitching) {
|
|
String relay = relayUrl();
|
|
if (!relay.isEmpty()) {
|
|
autoSwitching = true;
|
|
appendLog("局域网断了(" + reason + "),正在切到中转服务器…");
|
|
ui.post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
setStatus("局域网断了,正在切到中转服务器…", C_WARN);
|
|
}
|
|
});
|
|
ui.postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
switchToRelay();
|
|
}
|
|
}, 600);
|
|
return;
|
|
}
|
|
appendLog("局域网断了,但没有配中转地址,只能断开");
|
|
}
|
|
|
|
// 中转也断了:重试几次再放弃(这条线路本来就会抖)
|
|
if (!lanDirect && autoRetry < 3 && !autoSwitching) {
|
|
autoRetry++;
|
|
appendLog("中转断了(" + reason + "),第 " + autoRetry + " 次自动重连…");
|
|
ui.post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
setStatus("中转断了,正在重连(第 " + autoRetry + " 次)…", C_WARN);
|
|
}
|
|
});
|
|
ui.postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
switchToRelay();
|
|
}
|
|
}, 700 * autoRetry);
|
|
return;
|
|
}
|
|
|
|
autoRetry = 0; ui.post(new Runnable() {'''
|
|
|
|
DISC_TAIL_OLD = ''' appendLog("断开:" + reason);
|
|
}
|
|
});
|
|
}'''
|
|
|
|
DISC_TAIL_NEW = ''' appendLog("断开:" + reason);
|
|
lanDirect = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 经中转重连(局域网断了、或者中转抖了之后自动调用)。
|
|
*
|
|
* 用后台线程做,不阻塞界面;成功了就继续用,失败才弹回连接页。
|
|
*/
|
|
private void switchToRelay() {
|
|
final String relay = relayUrl();
|
|
if (relay.isEmpty()) {
|
|
autoSwitching = false;
|
|
return;
|
|
}
|
|
final String code = lastCode;
|
|
final String token = lastToken;
|
|
io.execute(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
String error = null;
|
|
boolean ok = false;
|
|
String newToken = token;
|
|
String name = lastHost;
|
|
try {
|
|
Map<String, Object> reply = client.connectViaRelay(
|
|
relay, code, token, MainActivity.this, 40000);
|
|
ok = Json.bool(reply, "ok", false);
|
|
if (ok) {
|
|
newToken = Json.str(reply, "token",
|
|
token == null ? "" : token);
|
|
name = Json.str(reply, "serverName", lastHost);
|
|
} else {
|
|
error = Json.str(reply, "error", "中转连接被拒");
|
|
}
|
|
} catch (final IOException exc) {
|
|
error = describeNetworkError(exc, relay);
|
|
}
|
|
|
|
final boolean success = ok;
|
|
final String finalError = error;
|
|
final String finalToken = newToken;
|
|
final String finalName = name;
|
|
final String usedCode = code;
|
|
ui.post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
autoSwitching = false;
|
|
if (success) {
|
|
lanDirect = false;
|
|
lastToken = finalToken;
|
|
if (finalToken != null && !finalToken.isEmpty()) {
|
|
store.saveDevice(
|
|
DeviceStore.deviceKey(lastHost, lastPort,
|
|
store.deviceId()),
|
|
finalName == null ? lastHost : finalName,
|
|
lastHost, lastPort, finalToken);
|
|
refreshDeviceList();
|
|
}
|
|
if (serverLabel != null) {
|
|
serverLabel.setText("已连接 "
|
|
+ (finalName == null ? lastHost : finalName)
|
|
+ "(中转)");
|
|
}
|
|
appendLog("已切到中转服务器,连接恢复");
|
|
setStatus("已切到中转(局域网不可用)", C_OK);
|
|
showControlPanel();
|
|
} else {
|
|
autoRetry = 0;
|
|
lanDirect = false;
|
|
setStatus("中转也连不上:" + finalError, C_DANGER);
|
|
appendLog("切中转失败:" + finalError);
|
|
showConnectPanel();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}'''
|
|
|
|
|
|
def patch(pairs, label: str) -> bool:
|
|
text = MAIN.read_text(encoding="utf-8")
|
|
ok = True
|
|
for old, new, tag in pairs:
|
|
if old not in text:
|
|
print(f" [{label}] [跳过] {tag} —— 锚点没找到")
|
|
ok = False
|
|
continue
|
|
text = text.replace(old, new, 1)
|
|
print(f" [{label}] {tag}")
|
|
MAIN.write_text(text, encoding="utf-8", newline="\n")
|
|
return ok
|
|
|
|
|
|
def main() -> int:
|
|
text = MAIN.read_text(encoding="utf-8")
|
|
if "switchToRelay" in text:
|
|
print(" 已经加过自动切中转")
|
|
return 0
|
|
|
|
ok = patch([
|
|
(FIELD_OLD, FIELD_NEW, "加自动切换状态字段"),
|
|
(REC_OLD, REC_NEW, "connect 记录状态"),
|
|
(LAN_FLAG_OLD, LAN_FLAG_NEW, "加 usedLan 标记"),
|
|
(DIRECT_OK_OLD, DIRECT_OK_NEW, "直连成功置 true"),
|
|
(FALLBACK_OLD, FALLBACK_NEW, "退回中转置 false"),
|
|
(FINAL_OLD, FINAL_NEW, "传出 finalLanDirect"),
|
|
(MODE_OLD, MODE_NEW, "连上后标记模式"),
|
|
(DISC_OLD, DISC_NEW, "断线自动切中转"),
|
|
(DISC_TAIL_OLD, DISC_TAIL_NEW, "switchToRelay 方法"),
|
|
], "MainActivity")
|
|
return 0 if ok else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|