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
相关产品推荐
相关产品推荐

