This commit is contained in:
lbl
2026-02-24 18:31:40 +08:00
parent ecdb359bbd
commit 5a31ad89f4
14 changed files with 2290 additions and 4 deletions
@@ -0,0 +1,97 @@
package com.vnt;
import org.json.JSONObject;
/**
* 注册结果
*
* 包含服务器分配的IP地址、掩码等信息
* 注意:如果能创建此对象,说明注册一定成功了(失败会抛异常)
*/
public class RegisterResult {
private final String ip;
private final int prefixLen;
private final String gateway;
private final String broadcast;
private RegisterResult(String ip, int prefixLen, String gateway, String broadcast) {
this.ip = ip;
this.prefixLen = prefixLen;
this.gateway = gateway;
this.broadcast = broadcast;
}
/**
* 从JSON字符串解析注册结果
* @throws VntException 如果注册失败或解析失败
*/
static RegisterResult fromJson(String json) throws VntException {
try {
JSONObject obj = new JSONObject(json);
boolean success = obj.getBoolean("success");
if (success) {
return new RegisterResult(
obj.getString("ip"),
obj.getInt("prefix_len"),
obj.getString("gateway"),
obj.getString("broadcast")
);
} else {
// 注册失败,抛出异常
String error = obj.getString("error");
throw new VntException("Registration failed: " + error);
}
} catch (VntException e) {
throw e; // 重新抛出VntException
} catch (Exception e) {
throw new VntException("Failed to parse register result: " + e.getMessage(), e);
}
}
/**
* 获取分配的IP地址
*/
public String getIp() {
return ip;
}
/**
* 获取前缀长度(掩码位数)
*/
public int getPrefixLen() {
return prefixLen;
}
/**
* 获取网关地址
*/
public String getGateway() {
return gateway;
}
/**
* 获取广播地址
*/
public String getBroadcast() {
return broadcast;
}
/**
* 转换为CIDR格式字符串(例如:10.0.0.2/24
*/
public String toCidr() {
return ip + "/" + prefixLen;
}
@Override
public String toString() {
return "RegisterResult{" +
"ip='" + ip + '\'' +
", prefixLen=" + prefixLen +
", gateway='" + gateway + '\'' +
", broadcast='" + broadcast + '\'' +
'}';
}
}
+428
View File
@@ -0,0 +1,428 @@
package com.vnt;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
/**
* VNT API接口 - 用于查询网络状态和信息
*
* 通过VntNetwork.getApi()获取实例
*/
public class VntApi {
private final long nativeHandle;
// 包内构造,只能通过VntNetwork创建
VntApi(long handle) {
this.nativeHandle = handle;
}
/**
* 获取客户端列表
* @return 客户端信息列表
*/
public List<ClientInfo> getClientList() throws VntException {
try {
String json = nativeGetClientList(nativeHandle);
JSONArray array = new JSONArray(json);
List<ClientInfo> clients = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
JSONObject obj = array.getJSONObject(i);
clients.add(new ClientInfo(
obj.getString("ip"),
obj.getBoolean("online")
));
}
return clients;
} catch (Exception e) {
throw new VntException("Failed to get client list: " + e.getMessage(), e);
}
}
/**
* 获取当前网络配置
* @return 网络信息,未连接返回null
*/
public NetworkInfo getNetwork() throws VntException {
try {
String json = nativeGetNetwork(nativeHandle);
if ("null".equals(json)) {
return null;
}
JSONObject obj = new JSONObject(json);
return new NetworkInfo(
obj.getString("ip"),
obj.getInt("prefix_len"),
obj.getString("gateway"),
obj.getString("broadcast")
);
} catch (Exception e) {
throw new VntException("Failed to get network info: " + e.getMessage(), e);
}
}
/**
* 获取本地NAT信息
* @return NAT信息,未检测到返回null
*/
public NatInfo getNatInfo() throws VntException {
try {
String json = nativeGetNatInfo(nativeHandle);
if ("null".equals(json)) {
return null;
}
return NatInfo.fromJson(json);
} catch (Exception e) {
throw new VntException("Failed to get NAT info: " + e.getMessage(), e);
}
}
/**
* 获取服务器节点列表
* @return 服务器信息列表
*/
public List<ServerInfo> getServerList() throws VntException {
try {
String json = nativeGetServerList(nativeHandle);
JSONArray array = new JSONArray(json);
List<ServerInfo> servers = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
JSONObject obj = array.getJSONObject(i);
servers.add(new ServerInfo(
obj.getInt("server_id"),
obj.getString("server_addr"),
obj.getBoolean("connected"),
obj.isNull("rtt") ? null : obj.getInt("rtt"),
obj.getLong("data_version"),
obj.isNull("server_version") ? null : obj.getString("server_version")
));
}
return servers;
} catch (Exception e) {
throw new VntException("Failed to get server list: " + e.getMessage(), e);
}
}
/**
* 获取路由表
* @return 路由信息列表
*/
public List<RouteInfo> getRouteTable() throws VntException {
try {
String json = nativeGetRouteTable(nativeHandle);
JSONArray array = new JSONArray(json);
List<RouteInfo> routes = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
JSONObject obj = array.getJSONObject(i);
String ip = obj.getString("ip");
JSONArray routesArray = obj.getJSONArray("routes");
List<RouteDetail> details = new ArrayList<>();
for (int j = 0; j < routesArray.length(); j++) {
JSONObject route = routesArray.getJSONObject(j);
details.add(new RouteDetail(
route.getString("route_key"),
route.getString("protocol"),
route.getInt("metric"),
route.getInt("rtt")
));
}
routes.add(new RouteInfo(ip, details));
}
return routes;
} catch (Exception e) {
throw new VntException("Failed to get route table: " + e.getMessage(), e);
}
}
/**
* 检查目标IP是否直连(P2P)
* @param ip 目标IP地址
* @return true表示直连,false表示通过服务器中转
*/
public boolean isDirect(String ip) {
return nativeIsDirect(nativeHandle, ip);
}
/**
* 获取对端NAT信息
* @param ip 目标IP地址
* @return NAT信息,未知返回null
*/
public NatInfo getPeerNatInfo(String ip) throws VntException {
try {
String json = nativeGetPeerNatInfo(nativeHandle, ip);
if ("null".equals(json)) {
return null;
}
return NatInfo.fromJson(json);
} catch (Exception e) {
throw new VntException("Failed to get peer NAT info: " + e.getMessage(), e);
}
}
/**
* 获取对端丢包信息
* @param ip 目标IP地址
* @return 丢包信息,未知返回null
*/
public PacketLossInfo getPacketLoss(String ip) throws VntException {
try {
String json = nativeGetPacketLoss(nativeHandle, ip);
if ("null".equals(json)) {
return null;
}
JSONObject obj = new JSONObject(json);
return new PacketLossInfo(
obj.getString("ip"),
obj.getLong("sent"),
obj.getLong("received"),
obj.getDouble("loss_rate")
);
} catch (Exception e) {
throw new VntException("Failed to get packet loss: " + e.getMessage(), e);
}
}
/**
* 获取对端流量统计
* @param ip 目标IP地址
* @return 流量信息,未知返回null
*/
public TrafficInfo getTrafficInfo(String ip) throws VntException {
try {
String json = nativeGetTrafficInfo(nativeHandle, ip);
if ("null".equals(json)) {
return null;
}
JSONObject obj = new JSONObject(json);
return new TrafficInfo(
obj.getString("ip"),
obj.getLong("tx_bytes"),
obj.getLong("rx_bytes")
);
} catch (Exception e) {
throw new VntException("Failed to get traffic info: " + e.getMessage(), e);
}
}
// ========== Native 方法 ==========
private static native String nativeGetClientList(long apiHandle);
private static native String nativeGetNetwork(long apiHandle);
private static native String nativeGetNatInfo(long apiHandle);
private static native String nativeGetServerList(long apiHandle);
private static native String nativeGetRouteTable(long apiHandle);
private static native boolean nativeIsDirect(long apiHandle, String ip);
private static native String nativeGetPeerNatInfo(long apiHandle, String ip);
private static native String nativeGetPacketLoss(long apiHandle, String ip);
private static native String nativeGetTrafficInfo(long apiHandle, String ip);
// ========== 数据类 ==========
public static class ClientInfo {
private final String ip;
private final boolean online;
public ClientInfo(String ip, boolean online) {
this.ip = ip;
this.online = online;
}
public String getIp() { return ip; }
public boolean isOnline() { return online; }
@Override
public String toString() {
return "ClientInfo{ip='" + ip + "', online=" + online + "}";
}
}
public static class NetworkInfo {
private final String ip;
private final int prefixLen;
private final String gateway;
private final String broadcast;
public NetworkInfo(String ip, int prefixLen, String gateway, String broadcast) {
this.ip = ip;
this.prefixLen = prefixLen;
this.gateway = gateway;
this.broadcast = broadcast;
}
public String getIp() { return ip; }
public int getPrefixLen() { return prefixLen; }
public String getGateway() { return gateway; }
public String getBroadcast() { return broadcast; }
@Override
public String toString() {
return "NetworkInfo{ip='" + ip + "', prefixLen=" + prefixLen +
", gateway='" + gateway + "', broadcast='" + broadcast + "'}";
}
}
public static class NatInfo {
private final String natType;
private final List<String> publicIps;
private final String ipv6;
private NatInfo(String natType, List<String> publicIps, String ipv6) {
this.natType = natType;
this.publicIps = publicIps;
this.ipv6 = ipv6;
}
static NatInfo fromJson(String json) throws Exception {
JSONObject obj = new JSONObject(json);
JSONArray ipsArray = obj.getJSONArray("public_ips");
List<String> publicIps = new ArrayList<>();
for (int i = 0; i < ipsArray.length(); i++) {
publicIps.add(ipsArray.getString(i));
}
return new NatInfo(
obj.getString("nat_type"),
publicIps,
obj.isNull("ipv6") ? null : obj.getString("ipv6")
);
}
public String getNatType() { return natType; }
public List<String> getPublicIps() { return publicIps; }
public String getIpv6() { return ipv6; }
@Override
public String toString() {
return "NatInfo{natType='" + natType + "', publicIps=" + publicIps +
", ipv6='" + ipv6 + "'}";
}
}
public static class ServerInfo {
private final int serverId;
private final String serverAddr;
private final boolean connected;
private final Integer rtt;
private final long dataVersion;
private final String serverVersion;
public ServerInfo(int serverId, String serverAddr, boolean connected,
Integer rtt, long dataVersion, String serverVersion) {
this.serverId = serverId;
this.serverAddr = serverAddr;
this.connected = connected;
this.rtt = rtt;
this.dataVersion = dataVersion;
this.serverVersion = serverVersion;
}
public int getServerId() { return serverId; }
public String getServerAddr() { return serverAddr; }
public boolean isConnected() { return connected; }
public Integer getRtt() { return rtt; }
public long getDataVersion() { return dataVersion; }
public String getServerVersion() { return serverVersion; }
@Override
public String toString() {
return "ServerInfo{serverId=" + serverId + ", serverAddr='" + serverAddr +
"', connected=" + connected + ", rtt=" + rtt + "}";
}
}
public static class RouteInfo {
private final String ip;
private final List<RouteDetail> routes;
public RouteInfo(String ip, List<RouteDetail> routes) {
this.ip = ip;
this.routes = routes;
}
public String getIp() { return ip; }
public List<RouteDetail> getRoutes() { return routes; }
@Override
public String toString() {
return "RouteInfo{ip='" + ip + "', routes=" + routes + "}";
}
}
public static class RouteDetail {
private final String routeKey;
private final String protocol;
private final int metric;
private final int rtt;
public RouteDetail(String routeKey, String protocol, int metric, int rtt) {
this.routeKey = routeKey;
this.protocol = protocol;
this.metric = metric;
this.rtt = rtt;
}
public String getRouteKey() { return routeKey; }
public String getProtocol() { return protocol; }
public int getMetric() { return metric; }
public int getRtt() { return rtt; }
@Override
public String toString() {
return "RouteDetail{routeKey='" + routeKey + "', protocol='" + protocol +
"', metric=" + metric + ", rtt=" + rtt + "}";
}
}
public static class PacketLossInfo {
private final String ip;
private final long sent;
private final long received;
private final double lossRate;
public PacketLossInfo(String ip, long sent, long received, double lossRate) {
this.ip = ip;
this.sent = sent;
this.received = received;
this.lossRate = lossRate;
}
public String getIp() { return ip; }
public long getSent() { return sent; }
public long getReceived() { return received; }
public double getLossRate() { return lossRate; }
@Override
public String toString() {
return "PacketLossInfo{ip='" + ip + "', sent=" + sent +
", received=" + received + ", lossRate=" + lossRate + "}";
}
}
public static class TrafficInfo {
private final String ip;
private final long txBytes;
private final long rxBytes;
public TrafficInfo(String ip, long txBytes, long rxBytes) {
this.ip = ip;
this.txBytes = txBytes;
this.rxBytes = rxBytes;
}
public String getIp() { return ip; }
public long getTxBytes() { return txBytes; }
public long getRxBytes() { return rxBytes; }
@Override
public String toString() {
return "TrafficInfo{ip='" + ip + "', txBytes=" + txBytes +
", rxBytes=" + rxBytes + "}";
}
}
}
+310
View File
@@ -0,0 +1,310 @@
package com.vnt;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
/**
* VNT网络配置
*
* 使用Builder模式构建配置
*/
public class VntConfig {
private final List<String> servers;
private final String networkCode;
private final String password;
private final String deviceId;
private final String deviceName;
private final String tunName;
private final String ip;
private final String certMode;
private final boolean noPunch;
private final boolean compress;
private final boolean rtx;
private final boolean fec;
private final boolean noNat;
private final boolean noTun;
private final Integer mtu;
private final boolean allowMapping;
private final List<String> portMapping;
private final List<String> udpStun;
private final List<String> tcpStun;
private VntConfig(Builder builder) {
this.servers = builder.servers;
this.networkCode = builder.networkCode;
this.password = builder.password;
this.deviceId = builder.deviceId;
this.deviceName = builder.deviceName;
this.tunName = builder.tunName;
this.ip = builder.ip;
this.certMode = builder.certMode;
this.noPunch = builder.noPunch;
this.compress = builder.compress;
this.rtx = builder.rtx;
this.fec = builder.fec;
this.noNat = builder.noNat;
this.noTun = builder.noTun;
this.mtu = builder.mtu;
this.allowMapping = builder.allowMapping;
this.portMapping = builder.portMapping;
this.udpStun = builder.udpStun;
this.tcpStun = builder.tcpStun;
}
/**
* 转换为JSON字符串
*/
String toJson() {
JSONObject json = new JSONObject();
// 必填项
JSONArray serverArray = new JSONArray();
for (String server : servers) {
serverArray.put(server);
}
json.put("server", serverArray);
json.put("network_code", networkCode);
// 可选项
if (password != null) json.put("password", password);
if (deviceId != null) json.put("device_id", deviceId);
if (deviceName != null) json.put("device_name", deviceName);
if (tunName != null) json.put("tun_name", tunName);
if (ip != null) json.put("ip", ip);
if (certMode != null) json.put("cert_mode", certMode);
if (mtu != null) json.put("mtu", mtu);
// 布尔值
json.put("no_punch", noPunch);
json.put("compress", compress);
json.put("rtx", rtx);
json.put("fec", fec);
json.put("no_nat", noNat);
json.put("no_tun", noTun);
json.put("allow_mapping", allowMapping);
// 数组
if (!portMapping.isEmpty()) {
JSONArray mappingArray = new JSONArray();
for (String mapping : portMapping) {
mappingArray.put(mapping);
}
json.put("port_mapping", mappingArray);
}
if (!udpStun.isEmpty()) {
JSONArray stunArray = new JSONArray();
for (String stun : udpStun) {
stunArray.put(stun);
}
json.put("udp_stun", stunArray);
}
if (!tcpStun.isEmpty()) {
JSONArray stunArray = new JSONArray();
for (String stun : tcpStun) {
stunArray.put(stun);
}
json.put("tcp_stun", stunArray);
}
return json.toString();
}
/**
* 配置构建器
*/
public static class Builder {
private List<String> servers = new ArrayList<>();
private String networkCode;
private String password;
private String deviceId;
private String deviceName;
private String tunName;
private String ip;
private String certMode;
private boolean noPunch = false;
private boolean compress = false;
private boolean rtx = false;
private boolean fec = false;
private boolean noNat = false;
private boolean noTun = false;
private Integer mtu;
private boolean allowMapping = false;
private List<String> portMapping = new ArrayList<>();
private List<String> udpStun = new ArrayList<>();
private List<String> tcpStun = new ArrayList<>();
/**
* 添加服务器地址(必填)
* @param server 服务器地址,格式:tcp://host:port 或 wss://host:port
*/
public Builder addServer(String server) {
this.servers.add(server);
return this;
}
/**
* 设置网络代码(必填)
* @param networkCode 组网代码
*/
public Builder setNetworkCode(String networkCode) {
this.networkCode = networkCode;
return this;
}
/**
* 设置密码(可选)
*/
public Builder setPassword(String password) {
this.password = password;
return this;
}
/**
* 设置设备ID(可选,默认自动生成)
*/
public Builder setDeviceId(String deviceId) {
this.deviceId = deviceId;
return this;
}
/**
* 设置设备名称(可选)
*/
public Builder setDeviceName(String deviceName) {
this.deviceName = deviceName;
return this;
}
/**
* 设置TUN设备名称(可选)
*/
public Builder setTunName(String tunName) {
this.tunName = tunName;
return this;
}
/**
* 设置固定IP(可选)
*/
public Builder setIp(String ip) {
this.ip = ip;
return this;
}
/**
* 设置证书验证模式(可选)
* @param certMode "insecure" | "system" | "embedded"
*/
public Builder setCertMode(String certMode) {
this.certMode = certMode;
return this;
}
/**
* 禁用打洞(默认false)
*/
public Builder setNoPunch(boolean noPunch) {
this.noPunch = noPunch;
return this;
}
/**
* 启用压缩(默认false)
*/
public Builder setCompress(boolean compress) {
this.compress = compress;
return this;
}
/**
* 启用QUIC重传(默认false
*/
public Builder setRtx(boolean rtx) {
this.rtx = rtx;
return this;
}
/**
* 启用FEC冗余传输(默认false)
*/
public Builder setFec(boolean fec) {
this.fec = fec;
return this;
}
/**
* 禁用NAT(默认false
*/
public Builder setNoNat(boolean noNat) {
this.noNat = noNat;
return this;
}
/**
* 无TUN模式(默认false
*/
public Builder setNoTun(boolean noTun) {
this.noTun = noTun;
return this;
}
/**
* 设置MTU(可选,默认1380)
*/
public Builder setMtu(int mtu) {
this.mtu = mtu;
return this;
}
/**
* 允许端口映射(默认false)
*/
public Builder setAllowMapping(boolean allowMapping) {
this.allowMapping = allowMapping;
return this;
}
/**
* 添加端口映射规则(可选)
* @param mapping 格式:tcp:80->192.168.1.100:8080
*/
public Builder addPortMapping(String mapping) {
this.portMapping.add(mapping);
return this;
}
/**
* 添加UDP STUN服务器(可选)
*/
public Builder addUdpStun(String stun) {
this.udpStun.add(stun);
return this;
}
/**
* 添加TCP STUN服务器(可选)
*/
public Builder addTcpStun(String stun) {
this.tcpStun.add(stun);
return this;
}
/**
* 构建配置对象
*/
public VntConfig build() {
if (servers.isEmpty()) {
throw new IllegalArgumentException("At least one server must be specified");
}
if (networkCode == null || networkCode.isEmpty()) {
throw new IllegalArgumentException("Network code must be specified");
}
return new VntConfig(this);
}
}
}
@@ -0,0 +1,21 @@
package com.vnt;
/**
* VNT异常
*
* VNT操作失败时抛出的异常
*/
public class VntException extends Exception {
public VntException(String message) {
super(message);
}
public VntException(String message, Throwable cause) {
super(message, cause);
}
public VntException(Throwable cause) {
super(cause);
}
}
@@ -0,0 +1,56 @@
package com.vnt;
/**
* VNT网络管理器 - 主入口类
*
* 使用示例:
* 1. 初始化: VntManager.init()
* 2. 创建网络: VntNetwork network = VntManager.createNetwork(config)
* 3. 注册: RegisterResult result = network.register()
* 4. (Android端用result的IP/掩码创建VPN接口,获取tunFd)
* 5. 启动TUN: network.startTun(tunFd)
* 6. 获取API: VntApi api = network.getApi()
* 7. 关闭: network.stop()
*/
public class VntManager {
static {
// 加载JNI库
System.loadLibrary("vnt_jni");
}
/**
* 初始化VNT模块(全局初始化,只需调用一次)
* @return true表示成功,false表示失败
*/
public static boolean init() {
return nativeInit();
}
/**
* 销毁VNT模块(全局清理)
*/
public static void destroy() {
nativeDestroy();
}
/**
* 创建网络实例
* @param config 网络配置对象
* @return VntNetwork实例,失败返回null
*/
public static VntNetwork createNetwork(VntConfig config) {
String configJson = config.toJson();
long handle = nativeCreateNetwork(configJson);
if (handle < 0) {
return null;
}
return new VntNetwork(handle);
}
// ========== Native 方法 ==========
private static native boolean nativeInit();
private static native void nativeDestroy();
private static native long nativeCreateNetwork(String configJson);
}
@@ -0,0 +1,122 @@
package com.vnt;
/**
* VNT网络实例
*
* 代表一个VNT网络连接,持有native资源
*/
public class VntNetwork {
private long nativeHandle;
private boolean closed = false;
// 包内构造,只能通过VntManager创建
VntNetwork(long handle) {
this.nativeHandle = handle;
}
/**
* 注册网络(连接服务器)
* @return 注册结果,包含分配的IP、掩码等信息
* @throws VntException 注册失败时抛出异常
*/
public RegisterResult register() throws VntException {
checkClosed();
String resultJson = nativeRegister(nativeHandle);
return RegisterResult.fromJson(resultJson);
}
/**
* 启动TUN设备
* @param tunFd TUN设备文件描述符(Android VpnService.Builder.establish()返回的fd
* 传入-1表示让VNT自动创建(仅非Android平台支持)
* @throws VntException 启动失败时抛出异常
*/
public void startTun(int tunFd) throws VntException {
checkClosed();
if (!nativeStartTun(nativeHandle, tunFd)) {
throw new VntException("Failed to start TUN device");
}
}
/**
* 设置网络IP(仅非Android平台使用)
* @param ip IP地址
* @param prefixLen 前缀长度
* @throws VntException 设置失败时抛出异常
*/
public void setNetworkIp(String ip, int prefixLen) throws VntException {
checkClosed();
if (!nativeSetNetworkIp(nativeHandle, ip, prefixLen)) {
throw new VntException("Failed to set network IP");
}
}
/**
* 获取VNT API实例
* @return VntApi实例
* @throws VntException 获取失败时抛出异常
*/
public VntApi getApi() throws VntException {
checkClosed();
long apiHandle = nativeGetApi(nativeHandle);
if (apiHandle < 0) {
throw new VntException("Failed to get VntApi");
}
return new VntApi(apiHandle);
}
/**
* 检查是否为无TUN模式
* @return true表示无TUN模式
*/
public boolean isNoTun() {
checkClosed();
return nativeIsNoTun(nativeHandle);
}
/**
* 停止并关闭网络
*/
public void stop() {
if (closed) {
return;
}
nativeStop(nativeHandle);
closed = true;
}
/**
* 获取native句柄(供内部使用)
*/
long getNativeHandle() {
return nativeHandle;
}
/**
* 检查是否已关闭
*/
private void checkClosed() {
if (closed) {
throw new IllegalStateException("VntNetwork has been closed");
}
}
@Override
protected void finalize() throws Throwable {
try {
stop();
} finally {
super.finalize();
}
}
// ========== Native 方法 ==========
private static native String nativeRegister(long handle);
private static native boolean nativeStartTun(long handle, int tunFd);
private static native boolean nativeSetNetworkIp(long handle, String ip, int prefixLen);
private static native long nativeGetApi(long handle);
private static native boolean nativeIsNoTun(long handle);
private static native boolean nativeStop(long handle);
}