【问题标题】:Can I set the getaddrinfo timeout in Android for DefaultHttpClient?我可以在 Android 中为 DefaultHttpClient 设置 getaddrinfo 超时吗?
【发布时间】:2013-08-15 13:20:16
【问题描述】:

在一个 Android 应用中,我正在尝试测试用户是否有正常的互联网连接。有兴趣可以在上一个问题Detecting limited network connectivity in Android?有一些背景

代码基本上是这样的:

try {
    HttpParams myParams = new BasicHttpParams();
    HttpConnectionParams.setConnectionTimeout(myParams, 10000);
    HttpConnectionParams.setSoTimeout(myParams, 10000);

    httpClient = new DefaultHttpClient(myParams);

    request = new HttpHead(url);
    response = httpClient.execute(request);
    statusCode = response.getStatusLine().getStatusCode();

    if (statusCode != 200)
    {       
        return false;
    }
    return true;

} catch(Exception e) {
    return false;
}

我可以使用 HttpConnectionParams 控制连接和套接字的超时。但是,如果我的设备已连接到 Wifi,但 wifi 无法访问 Internet,我在异常中遇到的错误是:

libcore.io.GaiException:getaddrinfo 失败:EAI_NODATA(无地址 与主机名相关联)

无法解析主机“www.example.com”:没有关联的地址 主机名

这看起来像是在 DNS 查找中超时。我可以控制 DNS 查找的超时时间吗? httpClient.execute 需要大约 45 秒才能失败,但上述异常除外。我希望它早点放弃。

【问题讨论】:

    标签: java android http networking connectivity


    【解决方案1】:

    我做了一些功课,您似乎无法调整 DNS 查找超时。因此,我认为更好的方法是进行显式 DNS 查找,以便我可以控制它(并希望缓存结果以加快下一次尝试)。所以这让我很简单:

    InetAddress addr = InetAddress.getByName(hostname);
    

    但这也有 45 秒的超时。其他人提到有no control of the timeout for getByName()。最后,我偶然发现了一个简单的解决方案,只需在单独的线程中启动查找并管理您自己的超时。 This blog post 很好地展示了这一点。

    private static boolean testDNS(String hostname) {
      try
      {
        DNSResolver dnsRes = new DNSResolver(hostname);
        Thread t = new Thread(dnsRes);
        t.start();
        t.join(1000);
        InetAddress inetAddr = dnsRes.get();            
        return inetAddr != null;
      }
      catch(Exception e)
      { 
        return false;
      }
    }
    
    private static class DNSResolver implements Runnable {
        private String domain;
        private InetAddress inetAddr;
    
        public DNSResolver(String domain) {
            this.domain = domain;
        }
    
        public void run() {
            try {
                InetAddress addr = InetAddress.getByName(domain);
                set(addr);
            } catch (UnknownHostException e) {                
            }
        }
    
        public synchronized void set(InetAddress inetAddr) {
            this.inetAddr = inetAddr;
        }
        public synchronized InetAddress get() {
            return inetAddr;
        }
    }
    

    使用这个我可以先测试设备是否可以解析主机名,然后再测试是否成功。

    【讨论】:

    • 太棒了,谢谢。很惊讶这是关于这个非常重要的用例的唯一帖子。例如,有人在热点上使用我的应用程序(可能超载、速度慢或根本没有连接到互联网)。
    • 导致我们出现问题的情况我们称之为“酒店 WiFi 问题的 CEO”。我们的 CEO 会在酒店里,他的手机会连接到酒店的无线网络。但是,他会忘记运行网络浏览器并实际登录酒店的 wifi 以获得完整的网络访问权限。他将被限制在酒店网络的围墙花园中,当他运行我们的应用程序时,它无法访问我们的服务器。我们进行了这项测试,以提醒我们的 CEO(和我们的用户)当他们无法真正使用互联网访问时。
    • 我将您的方法与 exec(ping) 结合使用,以便在连接到 wifi 但不连接到互联网时避免长时间的 dns 错误延迟。对于那些阅读,请记住 dns 已缓存,因此您需要在第一次 dns 检查后 ping 以检查 Internet (ICMP) 连接。找不到其他方法来避免长硬编码(20 秒)的 dns 超时。请注意,InetAddr.isReachable() 方法执行需要 root 访问权限的低级别 ICMP 调用。 exec(ping) 没有。 (8.8.8.8)
    • @DominicCerisano 并非所有服务器都接受 ICMP 请求
    • @cbreezier 我给出的示例 (8.8.8.8) 是一个 Google DNS 服务器并启用了 ICMP。
    猜你喜欢
    • 2011-01-17
    • 1970-01-01
    • 2012-05-21
    • 2011-02-07
    • 1970-01-01
    • 2017-09-01
    • 2011-07-20
    • 2011-11-05
    • 2013-09-08
    相关资源
    最近更新 更多