mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-19 04:50:43 +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 + 双连接 文档 - 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
169 lines
5.7 KiB
Java
169 lines
5.7 KiB
Java
package com.lanremote.watch;
|
|
|
|
import android.content.Context;
|
|
import android.net.wifi.WifiManager;
|
|
|
|
import java.net.Inet4Address;
|
|
import java.net.InetAddress;
|
|
import java.net.InterfaceAddress;
|
|
import java.net.NetworkInterface;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* 安卓侧的自动发现包装。
|
|
*
|
|
* 真正干活的是 {@link DiscoveryCore}(纯 Java,可桌面联调)。这里多做两件事:
|
|
* 1. 抓 WifiManager.MulticastLock(部分手表息屏/省电时会过滤广播)
|
|
* 2. 算出**本网段的定向广播地址**,这是广播探测能不能收到回复的关键
|
|
*/
|
|
public final class Discovery {
|
|
|
|
public static final int PORT = DiscoveryCore.PORT;
|
|
|
|
private Discovery() {
|
|
}
|
|
|
|
/** 本机在某张网卡上的 IPv4 与前缀长度。 */
|
|
public static class LocalNet {
|
|
public final String ip;
|
|
public final int prefix;
|
|
public final String iface;
|
|
|
|
LocalNet(String ip, int prefix, String iface) {
|
|
this.ip = ip;
|
|
this.prefix = prefix;
|
|
this.iface = iface;
|
|
}
|
|
|
|
public String broadcast() {
|
|
return DiscoveryCore.directedBroadcast(ip, prefix);
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return iface + " " + ip + "/" + prefix;
|
|
}
|
|
}
|
|
|
|
/** 取当前可用的局域网 IPv4(优先 wlan*)。 */
|
|
public static LocalNet localNet() {
|
|
try {
|
|
LocalNet fallback = null;
|
|
for (NetworkInterface ni : Collections.list(NetworkInterface.getNetworkInterfaces())) {
|
|
if (!ni.isUp() || ni.isLoopback()) {
|
|
continue;
|
|
}
|
|
String name = ni.getName() == null ? "" : ni.getName();
|
|
if (name.startsWith("p2p") || name.startsWith("dummy")) {
|
|
continue; // WiFi Direct / 占位网卡
|
|
}
|
|
for (InterfaceAddress ia : ni.getInterfaceAddresses()) {
|
|
InetAddress addr = ia.getAddress();
|
|
if (!(addr instanceof Inet4Address) || addr.isLoopbackAddress()) {
|
|
continue;
|
|
}
|
|
int prefix = ia.getNetworkPrefixLength();
|
|
if (prefix <= 0 || prefix > 32) {
|
|
continue;
|
|
}
|
|
LocalNet net = new LocalNet(addr.getHostAddress(), prefix, name);
|
|
if (name.startsWith("wlan")) {
|
|
return net; // wlan 最优,直接返回
|
|
}
|
|
if (fallback == null) {
|
|
fallback = net;
|
|
}
|
|
}
|
|
}
|
|
return fallback;
|
|
} catch (Exception e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/** 本网段定向广播地址,如 192.168.10.255。 */
|
|
public static String directedBroadcast() {
|
|
LocalNet net = localNet();
|
|
return net == null ? null : net.broadcast();
|
|
}
|
|
|
|
/** 广播搜索(自动带上本网段定向广播)。 */
|
|
public static List<DiscoveryCore.Found> search(Context context, int timeoutMs) {
|
|
WifiManager.MulticastLock lock = acquireLock(context);
|
|
try {
|
|
return DiscoveryCore.search(timeoutMs, directedBroadcast());
|
|
} finally {
|
|
releaseLock(lock);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 子网 TCP 扫描 —— 广播被拦时的兜底。
|
|
* 注意:会阻塞,必须在后台线程调用。
|
|
*/
|
|
public static List<String> scanSubnet(Context context, int port, int connectTimeoutMs) {
|
|
LocalNet net = localNet();
|
|
if (net == null || net.prefix > 30) {
|
|
return new java.util.ArrayList<String>();
|
|
}
|
|
WifiManager.MulticastLock lock = acquireLock(context);
|
|
try {
|
|
return DiscoveryCore.scanSubnet(net.ip, net.prefix, port,
|
|
connectTimeoutMs, 300);
|
|
} finally {
|
|
releaseLock(lock);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 请电脑打开「网络自检」。
|
|
* 返回电脑名;没收到回应返回 null。
|
|
*/
|
|
public static String requestSelfCheck(Context context, int timeoutMs) {
|
|
WifiManager.MulticastLock lock = acquireLock(context);
|
|
try {
|
|
return DiscoveryCore.requestSelfCheck(directedBroadcast(), timeoutMs);
|
|
} finally {
|
|
releaseLock(lock);
|
|
}
|
|
}
|
|
|
|
public static DiscoveryCore.Found probeHost(String host, int timeoutMs) {
|
|
return DiscoveryCore.probeHost(host, timeoutMs);
|
|
}
|
|
|
|
public static boolean ping(String host, int port, int timeoutMs) {
|
|
return DiscoveryCore.ping(host, port, timeoutMs);
|
|
}
|
|
|
|
private static WifiManager.MulticastLock acquireLock(Context context) {
|
|
try {
|
|
WifiManager manager = (WifiManager) context.getApplicationContext()
|
|
.getSystemService(Context.WIFI_SERVICE);
|
|
if (manager == null) {
|
|
return null;
|
|
}
|
|
WifiManager.MulticastLock lock = manager.createMulticastLock("lanremote");
|
|
lock.setReferenceCounted(false);
|
|
lock.acquire();
|
|
return lock;
|
|
} catch (Exception e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static void releaseLock(WifiManager.MulticastLock lock) {
|
|
if (lock == null) {
|
|
return;
|
|
}
|
|
try {
|
|
if (lock.isHeld()) {
|
|
lock.release();
|
|
}
|
|
} catch (Exception ignored) {
|
|
// 释放失败不影响结果
|
|
}
|
|
}
|
|
}
|