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

Android VPN应用中如何绕开VPN进行DNS解析?

解决OkHttp在VPN活跃时绕过VPN执行DNS查询的方案

要让OkHttp的DNS查询绕开VPN,核心是绕过系统默认的DNS解析逻辑,改用绑定到非VPN网络的方式执行域名解析,同时配合套接字绑定确保整个请求都不走VPN。具体实现步骤如下:

  • 第一步:获取非VPN的网络实例
    先通过系统服务筛选出没有被VPN占用的网络,后续所有的DNS解析和套接字连接都绑定到这个网络:

    ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    Network nonVpnNetwork = null;
    
    if (connManager != null) {
        for (Network network : connManager.getAllNetworks()) {
            NetworkCapabilities caps = connManager.getNetworkCapabilities(network);
            // 过滤掉VPN传输类型的网络
            if (caps != null && !caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
                nonVpnNetwork = network;
                break;
            }
        }
    }
    
  • 第二步:给OkHttp配置自定义DNS解析器
    OkHttp允许自定义Dns接口实现,在解析域名时直接使用非VPN网络的getAllByName方法,这个方法会强制在指定网络上执行DNS查询,完全绕开VPN的路由:

    OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .dns(hostname -> {
            if (nonVpnNetwork == null) {
                // 无可用非VPN网络时,降级到系统默认解析
                return Arrays.asList(InetAddress.getAllByName(hostname));
            }
            // 用非VPN网络执行DNS解析
            InetAddress[] addresses = nonVpnNetwork.getAllByName(hostname);
            return Arrays.asList(addresses);
        })
        .build();
    
  • 第三步:额外配置套接字绑定(可选但推荐)
    为了确保整个HTTP请求的套接字也不走VPN,需要自定义SocketFactory,让创建的套接字绑定到非VPN网络:

    OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .dns(hostname -> {
            if (nonVpnNetwork == null) {
                return Arrays.asList(InetAddress.getAllByName(hostname));
            }
            return Arrays.asList(nonVpnNetwork.getAllByName(hostname));
        })
        .socketFactory(new SocketFactory() {
            @Override
            public Socket createSocket() throws IOException {
                if (nonVpnNetwork == null) {
                    return new Socket();
                }
                // 创建绑定到非VPN网络的Socket
                return nonVpnNetwork.createSocket();
            }
    
            @Override
            public Socket createSocket(String host, int port) throws IOException {
                Socket socket = createSocket();
                socket.connect(new InetSocketAddress(host, port));
                return socket;
            }
    
            // 其余重载方法可直接调用默认Socket的实现,示例如下:
            @Override
            public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
                Socket socket = createSocket();
                socket.bind(new InetSocketAddress(localHost, localPort));
                socket.connect(new InetSocketAddress(host, port));
                return socket;
            }
    
            @Override
            public Socket createSocket(InetAddress host, int port) throws IOException {
                Socket socket = createSocket();
                socket.connect(new InetSocketAddress(host, port));
                return socket;
            }
    
            @Override
            public Socket createSocket(InetAddress host, int port, InetAddress localHost, int localPort) throws IOException {
                Socket socket = createSocket();
                socket.bind(new InetSocketAddress(localHost, localPort));
                socket.connect(new InetSocketAddress(host, port));
                return socket;
            }
        })
        .build();
    
  • 注意事项

    • 权限要求:需要在Manifest中声明ACCESS_NETWORK_STATE权限,Android 6.0及以上需动态申请该权限。
    • 版本兼容:Network.getAllByName()和Network.createSocket()是API 24(Android 7.0)引入的,若需兼容更低版本,需添加版本判断并做降级处理。
    • 异常处理:务必处理nonVpnNetwork为null的情况,避免无可用非VPN网络时出现崩溃。

内容的提问来源于stack exchange,提问作者Mark Smith

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 12:47:03