Android 4.4.4 -Andoird 5.0.0代理(ProxySlector 中select函数)导致的BUG分析

Android 4.4.4 发展到 Andoird 5.0.0 其中 OKhttp 中的一点点变化,导致的一个问题:
在使用PRoxySelector 中的select函数返回proxy ,构造proxy如果使用的resolve的方式构造,
在而后的网络请求中会经历如下过程:
1、host 解析成 InetAddress
2、InetAddress反向解析成name
3、然后将name正向解析成InetAddress
过程及其繁琐,导致请求的效率下降,也可能导致错误。
什么样的错误呢?
例如先给ip:
1、1.2.3.4,
2、逆向解析成 :4.3.2.1.broad.wh.hb.dynamic.163data.com.cn
3、正向解析出错 public1.114dns.com can't find 4.3.2.1.broad.wh.hb.dynamic.163data.com.cn: Non-existent domain

仔细分析原因后发现,Android 5.0.0以下的网络请求使用OKhttp,然后里面有一个比较关键的类RouteSelector。其中有一段代码是这样的:

213
    socketAddresses = null; // Clear the addresses. Necessary if getAllByName() below throws!
214
 
215
    String socketHost;
216
    if (proxy.type() == Proxy.Type.DIRECT) {
217
      socketHost = uri.getHost();
219
    } else {
220
      SocketAddress proxyAddress = proxy.address();
221
      if (!(proxyAddress instanceof InetSocketAddress)) {
223
            "Proxy.address() is not an " + "InetSocketAddress: " + proxyAddress.getClass());
224
      }
225
      InetSocketAddress proxySocketAddress = (InetSocketAddress) proxyAddress;
226
      socketHost = proxySocketAddress.getHostName();
227
      socketPort = proxySocketAddress.getPort();
228
    }
229
 
230
    // Try each address for best behavior in mixed IPv4/IPv6 environments.
233
  }



public class InetAddress implements Serializable {

    private static InetAddress getHostByAddrImpl(InetAddress address) throws UnknownHostException {
        BlockGuard.getThreadPolicy().onNetwork();
        try {
           String hostname = Libcore.os.getnameinfo(address, NI_NAMEREQD);// 如果找不到主机名字, 将其作为一个错误对待.
            return makeInetAddress(address. ipaddress .clone(), hostname);
        } catch (GaiException gaiException) {
            throw gaiException.rethrowAsUnknownHostException();
        }
    }


    public String getHostName() {
        if ( hostName == null ) {
            try {
                hostName= getHostByAddrImpl(this).hostName;
            } catch (UnknownHostException ex) {
                hostName = getHostAddress();
            }
        }
        return hostName ;
    }


    /**
     * Returns the numeric representation of this IP address (such as "127.0.0.1").
     */
    public String getHostAddress() {
        return Libcore.os.getnameinfo( this , NI_NUMERICHOST); // Can't throw.以数字形式而非名字返回主机地址
    }
}

  • NI_DGRAM: 服务基于数据报而非基于流.
  • NI_NAMEREQD: 如果找不到主机名字, 将其作为一个错误对待.
  • NI_NOFQDN: 对于本地主机, 仅返回完全限定域名的节点名部分.
  • NI_NUMERICHOST: 以数字形式而非名字返回主机地址.
  • NI_NUMERICSERV: 以数字形式而非名字返回服务地址(即端口号).


其中代码段:
226
      socketHost = proxySocketAddress.getHostName();
获取到的结果会是一个通过逆向解析获取ip对应的域名,如果找不到主机名字, 将其作为一个异常抛出.然后使用IP作为host。


按照正常逻辑 从IP反向解析域名能通的话,应该这个域名也能够解析成IP!!!
但是现实生活中不是这样的,可能我们活在中国奇葩的网络中吧。

还好在Andorid 5.0.0以后版本中的OKhttp使用已经切换如下代码了:
246
    // Clear the addresses. Necessary if getAllByName() below throws!
248
 
     
249
    String socketHost;
250
    int socketPort;
251
    if (proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.SOCKS) {
252
      socketHost = address.getUriHost();
253
      socketPort = getEffectivePort(uri);
254
    } else {
255
      SocketAddress proxyAddress = proxy.address();
256
      if (!(proxyAddress instanceof InetSocketAddress)) {
258
            "Proxy.address() is not an " + "InetSocketAddress: " + proxyAddress.getClass());
259
      }
260
      InetSocketAddress proxySocketAddress = (InetSocketAddress) proxyAddress;
261
      socketHost = getHostString(proxySocketAddress);
262
      socketPort = proxySocketAddress.getPort();
263
    }
264
 
     
265
    // Try each address for best behavior in mixed IPv4/IPv6 environments.
266
    for (InetAddress inetAddress : network.resolveInetAddresses(socketHost)) {
267
      inetSocketAddresses.add(new InetSocketAddress(inetAddress, socketPort));
268
    }
270
  }



Obtain a "host" from an  java.net.InetSocketAddress. This returns a string containing either an actual host name or a numeric IP address.
275
 
276
  // Visible for testing
277
  static String getHostString(InetSocketAddress socketAddress) {
278
    InetAddress address = socketAddress.getAddress();
279
    if (address == null) {
280
      // The InetSocketAddress was specified with a string (either a numeric IP or a host name). If
281
      // it is a name, all IPs for that name should be tried. If it is an IP address, only that IP
282
      // address should be tried.
283
      return socketAddress.getHostName();
284
    }
285
    // The InetSocketAddress has a specific address: we should only try that address. Therefore we
286
    // return the address and ignore any host name that may be available.
287
    return address.getHostAddress();
288
  }
这样就能很好的解决这个问题,不存在什么反向解析 然后正向解析的问题了。但是我们Android5.0一下的操作系统怎么办呢????
哈哈 Android 源代码管理的官网上已经将okhttp这个bug 同步过去,bug 号码是#1186
其实我们创建代理的时候使用不解析的方式创建就可以避免上面的问题!!










https://android.googlesource.com/platform/external/okhttp/+log/android-5.1.0_r1/


android /  platform /  external /  okhttp /  android-5.1.0_r1 /  okhttp /  src /  main /  java /  com /  squareup /  okhttp / internal /  http /  RouteSelector.java
tagd3c92892dd20b7362fe5039f99a0c49304425e30 
taggerThe Android Open Source Project <initial-contribution@android.com>Mon Mar 02 08:26:28 2015 -0800
objectbe40c8024c0572878c55d2e032f3cbb6ef85ccd9 
Android 5.1.0 release 1
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iEYEABECAAYFAlT0jzQACgkQ6K0/gZqxDnhW0ACeIsx37SoxX66T/Vx/uThDRCLQ
zCQAoJE9d0p9nbad8JBPOEC8OJ/32quz
=aQm/
-----END PGP SIGNATURE-----
  1. 2ed0095 Avoid a reverse DNS-lookup for a numeric proxy address by Neil Fuller · 1 year ago
  2. cc168fe Be consistent about host names in RouteSelector. by Narayan Kamath · 1 year, 2 months ago
  3. 7c7f22d Allow callers to pass in a custom host resolver implementation. by Lorenzo Colitti · 1 year, 5 months ago
  4. 3c938a3 Update okhttp to a more recent commit. by Neil Fuller · 1 year, 10 months ago
  5. 166772b Update okhttp. by Narayan Kamath · 2 years, 1 month ago
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值