You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android实现Oxy Proxy Manager同款VPN代理及Invalid Proxy报错解决

解决Android VpnService中"Invalid Proxy"异常问题

需求背景

要实现与Oxy Proxy Manager App一致的功能:用户输入代理IP和端口后,点击连接即可在系统VPN设置中创建配置,将Android设备全部网络流量先路由至内部VPN服务,再转发至已购买的IP认证代理IP和端口。

问题描述

基于Android VpnService编写的MyVpnService类,运行时抛出java.lang.IllegalArgumentException: Invalid Proxy异常,无法完成代理连接与流量路由。


核心排查方向

这个异常的本质是代理参数不符合VpnService的要求,常见触发原因包括:

  • 代理IP格式非法:包含协议前缀(如http://)、非数字字符,或IPv4段超出0-255范围
  • 端口不在合法区间(1-65535)
  • 错误匹配代理类型与VpnService配置方法(比如用setHttpProxy配置SOCKS代理)
  • 传入域名而非纯IP地址(VpnService内置代理配置仅支持直接IP)

修复方案

1. 严格校验输入参数

先对用户输入的代理IP和端口做合法性校验,过滤非法内容:

// 校验代理IP格式(支持IPv4/IPv6)
private boolean isValidProxyIp(String ip) {
    // 清理协议前缀与空格
    String cleanIp = ip.replaceAll("^http://|^https://|\\s+", "");
    // IPv4校验规则
    String ipv4Pattern = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
    if (cleanIp.matches(ipv4Pattern)) return true;
    // IPv6简单校验规则
    String ipv6Pattern = "^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$";
    return cleanIp.matches(ipv6Pattern);
}

// 校验端口合法性
private boolean isValidProxyPort(int port) {
    return port > 0 && port <= 65535;
}

2. 修正VpnService代理配置逻辑

如果需要转发全量流量到代理,不要依赖VpnService的setHttpProxy/setSocksProxy(这类方法仅针对特定协议/应用),而是自己实现流量转发逻辑;如果确实要用系统内置代理,需确保参数与方法匹配:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    String proxyIp = intent.getStringExtra("PROXY_IP");
    int proxyPort = intent.getIntExtra("PROXY_PORT", 0);

    // 先做输入校验
    if (!isValidProxyIp(proxyIp) || !isValidProxyPort(proxyPort)) {
        Toast.makeText(this, "代理IP或端口格式错误", Toast.LENGTH_SHORT).show();
        stopSelf();
        return START_NOT_STICKY;
    }

    // 清理IP中的冗余内容
    String cleanProxyIp = proxyIp.replaceAll("^http://|^https://|\\s+", "");

    try {
        VpnService.Builder builder = new VpnService.Builder();
        // 配置VPN虚拟地址与全量路由
        builder.addAddress("10.0.0.2", 32);
        builder.addRoute("0.0.0.0", 0); // 路由所有IPv4流量
        builder.addRoute("::", 0); // 路由所有IPv6流量

        // 若使用系统内置代理(仅针对HTTP/SOCKS协议)
        // HTTP代理用setHttpProxy,SOCKS代理用setSocksProxy
        ProxyInfo proxyInfo = ProxyInfo.buildDirectProxy(cleanProxyIp, proxyPort);
        builder.setHttpProxy(proxyInfo);

        // 建立VPN接口
        ParcelFileDescriptor vpnInterface = builder.establish();
        if (vpnInterface == null) {
            Toast.makeText(this, "VPN连接失败", Toast.LENGTH_SHORT).show();
            stopSelf();
            return START_NOT_STICKY;
        }

        // 启动流量转发线程(全量流量转发需自定义实现)
        startTrafficForwarding(vpnInterface, cleanProxyIp, proxyPort);

    } catch (IllegalArgumentException e) {
        e.printStackTrace();
        Toast.makeText(this, "代理配置错误:" + e.getMessage(), Toast.LENGTH_SHORT).show();
        stopSelf();
    }

    return START_STICKY;
}

3. 自定义全量流量转发实现

如果需要转发所有TCP/UDP流量到代理,需手动处理VPN接口的数据包读写与转发:

private void startTrafficForwarding(ParcelFileDescriptor vpnInterface, String proxyIp, int proxyPort) {
    FileInputStream vpnIn = new FileInputStream(vpnInterface.getFileDescriptor());
    FileOutputStream vpnOut = new FileOutputStream(vpnInterface.getFileDescriptor());

    byte[] buffer = new byte[1500]; // 匹配MTU大小
    new Thread(() -> {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                int readLen = vpnIn.read(buffer);
                if (readLen <= 0) break;

                // 解析IP数据包,获取目标地址与协议类型(需自行实现IP头解析逻辑)
                // 示例:建立到代理的Socket连接并转发数据
                Socket proxySocket = new Socket(proxyIp, proxyPort);
                OutputStream proxyOut = proxySocket.getOutputStream();
                InputStream proxyIn = proxySocket.getInputStream();

                // 发送数据包到代理
                proxyOut.write(buffer, 0, readLen);
                proxyOut.flush();

                // 将代理返回的数据写回VPN接口
                byte[] respBuffer = new byte[1500];
                int respLen;
                while ((respLen = proxyIn.read(respBuffer)) > 0) {
                    vpnOut.write(respBuffer, 0, respLen);
                    vpnOut.flush();
                }

                proxySocket.close();
            } catch (IOException e) {
                e.printStackTrace();
                break;
            }
        }
    }).start();
}

注意事项

  • 必须在Manifest中声明android.permission.BIND_VPN_SERVICE权限,并注册VpnService
  • 启动VPN前需调用prepare()方法获取用户授权
  • 若使用IP认证代理,需确保设备真实公网IP在代理服务器的白名单中

内容的提问来源于stack exchange,提问作者ByteSoft Lab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 18:05:18