Apache HTTPClient抛出java.net.SocketException异常:对于许多域名,连接重置。

9

我正在创建一个(行为良好的)网络爬虫,并且注意到一些服务器会导致Apache HttpClient给我返回SocketException错误,具体地说:

java.net.SocketException: Connection reset

导致这个问题的代码是:
// Execute the request
HttpResponse response; 
try {
    response = httpclient.execute(httpget); //httpclient is of type HttpClient
} catch (NullPointerException e) {
    return;//deep down in apache http sometimes throws a null pointer...  
}

对于大多数服务器来说,这是完全可以的。但对于其他一些服务器,它会立即抛出SocketException异常。
导致立即抛出SocketException异常的网站示例:http://www.bhphotovideo.com/ 非常好用(像大多数网站一样):http://www.google.com/ 现在,您可以看到,www.bhphotovideo.com在Web浏览器中加载良好。当我不使用Apache的HTTP客户端时,它也可以正常加载。(例如以下代码:)
 HttpURLConnection c = (HttpURLConnection)url.openConnection();  
 BufferedInputStream in = new BufferedInputStream(c.getInputStream());  
 Reader r = new InputStreamReader(in);     

 int i;  
 while ((i = r.read()) != -1) {  
      source.append((char) i);  
 }  

那么为什么我不直接使用这段代码呢?因为我需要使用Apache HTTP Client中的一些关键功能。

有人知道是什么导致某些服务器引起这个异常吗?

到目前为止的研究:

  • 问题发生在我的本地Mac开发机器和AWS EC2实例上,所以它不是一个本地防火墙的问题。

  • 似乎错误不是由远程机器引起的,因为异常没有说“by peer”。

  • 这个堆栈溢出看起来很相关java.net.SocketException:连接重置但答案并没有说明为什么只有使用Apache HTTP Client而不是其他方法会发生这种情况。

额外的问题:我正在使用这个系统进行相当多的爬取。除了Apache HTTP Client之外,通常是否有更好的Java类可用于此?我发现了许多问题(例如我必须在上面的代码中捕获NullPointerException)。HTTPClient似乎对服务器通信非常挑剔——比我想象的更挑剔,对于一个不能只在服务器行为不良时就退出的爬虫来说,这并不理想。

谢谢大家!

解决方案

老实说,我没有一个完美的解决方案,但它能够工作,所以对我来说已经足够好了。

正如oleg所指出的那样,Bixo创建了一个爬虫,定制了HttpClient,使其对服务器更加宽容。为了“绕过”问题而不是解决它,我只使用了Bixo提供的SimpleHttpFetcher:

(链接已删除- SO认为我是垃圾邮件发送者,所以你需要自己Google它)
SimpleHttpFetcher fetch = new SimpleHttpFetcher(new UserAgent("botname","contact@yourcompany.com","ENTER URL"));
try {
    FetchedResult result = fetch.fetch("ENTER URL");
    System.out.println(new String(result.getContent()));
} catch (BaseFetchException e) {
    e.printStackTrace();
}

这种解决方案的缺点是Bixo有很多依赖项,所以这可能不是每个人都适用的好方法。但是,您可以始终通过使用DefaultHttpClient来解决问题,并查看它们如何实例化它才能使其工作。我选择使用整个类,因为它为我处理了一些东西,例如自动重定向跟随(并报告最终目标URL),这对我很有帮助。
感谢所有人的帮助。
编辑:TinyBixo
大家好。所以,我喜欢Bixo的工作方式,但不喜欢它有很多依赖项(包括所有Hadoop)。因此,我创建了一个大大简化的Bixo,没有所有的依赖关系。如果您遇到上述问题,我建议使用它(如果您想更新,请随时提出拉取请求!)
它在这里可用:https://github.com/juliuss/TinyBixo

你是如何实例化 HttpClient 的? - Buhake Sindi
如果这种情况只是偶尔发生,那么可能需要在使用完响应后关闭它,以确保所有资源都被释放。否则可能会导致连接泄漏,最终引起类似于您遇到的问题。 - NateH06
3个回答

5
首先,回答您的问题:
连接重置是由服务器端的问题引起的。很可能是服务器无法解析请求或无法处理请求,因此在没有返回有效响应的情况下断开了连接。HttpClient生成的HTTP请求中可能有一些导致服务器端逻辑失败的东西,可能是由于服务器端错误。错误消息没有说“由对等方”并不意味着连接重置发生在客户端。
几点说明:
(1) 几个流行的网络爬虫,如bixo http://openbixo.org/,使用HttpClient没有太大问题,但他们几乎都必须调整HttpClient的行为,使其更加宽松地处理常见的HTTP协议违规行为。默认情况下,HttpClient对HTTP协议合规性要求相当严格。
(2) 为什么您没有向HttpClient项目报告NPE问题或任何其他问题?

嗨 - 感谢您指向Bixo。我已经在主问题区域中添加了我是如何解决这个问题的。至于不报告NPE - 您是对的,下次我捕获堆栈跟踪时会这样做! - nostromo

1

这两个设置有时会有帮助:

 client.getParams().setParameter("http.socket.timeout", new Integer(0));
 client.getParams().setParameter("http.connection.stalecheck", new  Boolean(true));

第一个设置套接字超时时间为无限。


3
将套接字的超时设置为无限大不会导致线程阻塞吗?我不确定这是否明智。 - Taylor
1
我不建议将“http.socket.timeout”设置为无限。这肯定会导致您的系统出现可避免的故障(特别是当外部系统没有响应时)。 - Ifesinachi Bryan

0
尝试使用Wireshark获取网络跟踪,并增加HTTPClient的log4j日志记录。这样可以显示连接为什么被重置。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接