httpclient 关闭httpclient 关闭= new httpclient怎么关闭

13155人阅读
最近在处理百度知道接口的问题时,遇到了下面的问题。在本机测试一直没有问题,因为测试的时间太短。而放到服务器上面就出现了下面的异常。而且是一直的出现。google 一下,原来是http连接出现了异常没有被关闭导致。写下来备查!
java.net.SocketException: Too many open files
at java.net.Socket.createImpl(Socket.java:397)
at java.net.Socket.&init&(Socket.java:371)
at java.net.Socket.&init&(Socket.java:249)
at mons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:80)
at mons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:122)
at mons.httpclient.HttpConnection.open(HttpConnection.java:707)
at mons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:387)
at mons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
at mons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
at mons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
at com.thhc.mylegist.baidu.BaiDuHttpFactory.getQuestionList(BaiDuHttpFactory.java:284)
at com.thhc.mylegist.baidu.BaiDuTask.run(BaiDuTask.java:21)
at java.util.TimerThread.mainLoop(Timer.java:512)
at java.util.TimerThread.run(Timer.java:462)
下面是转载的内容:
1.HttpClient client = new HttpClient();
2.HttpMethod method = new GetMethod("http://www.apache.org");
client.executeMethod(method);
byte[] responseBody =
responseBody = method.getResponseBody();
9.} catch (HttpException e) {
// TODO Auto-generated catch block
e.printStackTrace();
12.} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
15.}finally{
method.releaseConnection();
HttpClient client = new HttpClient();
HttpMethod method = new GetMethod("http://www.apache.org");
client.executeMethod(method);
byte[] responseBody =
responseBody = method.getResponseBody();
} catch (HttpException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
method.releaseConnection();
大部分人使用HttpClient都是使用类似上面的事例代码,包括Apache官方的例子也是如此。最近我在使用HttpClient是发现一次循环发送大量请求到服务器会导致APACHE服务器的链接被占满,后续的请求便排队等待。
我服务器端APACHE的配置
1.Timeout 30
2.KeepAlive On
#表示服务器端不会主动关闭链接
3.MaxKeepAliveRequests 100
4.KeepAliveTimeout 180
Timeout 30
KeepAlive On
#表示服务器端不会主动关闭链接
MaxKeepAliveRequests 100
KeepAliveTimeout 180
因此这样的配置就会导致每个链接至少要过180S才会被释放,这样在大量请求访问时就必然会造成链接被占满,请求等待的情况。
在通过DEBUH后发现HttpClient在method.releaseConnection()后并没有把链接关闭,这个方法只是将链接返回给connection manager。如果使用HttpClient client = new HttpClient()实例化一个HttpClient connection manager默认实现是使用SimpleHttpConnectionManager。SimpleHttpConnectionManager有个构造函数如下
2. * The connection manager created with this constructor will try to keep the
3. * connection open (alive) between consecutive requests if the alwaysClose
4. * parameter is set to &tt&false&/tt&. Otherwise the connection manager will
5. * always close connections upon release.
7. * @param alwaysClose if set &tt&true&/tt&, the connection manager will always
close connections upon release.
10.public SimpleHttpConnectionManager(boolean alwaysClose) {
this.alwaysClose = alwaysC
* The connection manager created with this constructor will try to keep the
* connection open (alive) between consecutive requests if the alwaysClose
* parameter is set to &tt&false&/tt&. Otherwise the connection manager will
* always close connections upon release.
* @param alwaysClose if set &tt&true&/tt&, the connection manager will always
close connections upon release.
public SimpleHttpConnectionManager(boolean alwaysClose) {
this.alwaysClose = alwaysC
看方法注释我们就可以看到如果alwaysClose设为true在链接释放之后connection manager 就会关闭链。在我们HttpClient client = new HttpClient()这样实例化一个client时connection manager是这样被实例化的
1.this.httpConnectionManager = new SimpleHttpConnectionManager();
this.httpConnectionManager = new SimpleHttpConnectionManager();
因此alwaysClose默认是false,connection是不会被主动关闭的,因此我们就有了一个客户端关闭链接的方法。
把事例代码中的第一行实例化代码改为如下即可,在method.releaseConnection();之后connection manager会关闭connection 。
1.HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) );
HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) );
实例化代码使用:HttpClient client = new HttpClient();
在method.releaseConnection();之后加上
1.((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();
((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();
shutdown源代码很简单,看了一目了然
1.public void shutdown() {
httpConnection.close();
public void shutdown() {
httpConnection.close();
实例化代码使用:HttpClient client = new HttpClient();
在method.releaseConnection();之后加上
client.getHttpConnectionManager().closeIdleConnections(0);此方法源码代码如下:
1.public void closeIdleConnections(long idleTimeout) {
long maxIdleTime = System.currentTimeMillis() - idleT
if (idleStartTime &= maxIdleTime) {
httpConnection.close();
public void closeIdleConnections(long idleTimeout) {
long maxIdleTime = System.currentTimeMillis() - idleT
if (idleStartTime &= maxIdleTime) {
httpConnection.close();
将idleTimeout设为0可以确保链接被关闭。
以上这三种方法都是有客户端主动关闭TCP链接的方法。下面再介绍由服务器端自动关闭链接的方法。
代码实现很简单,所有代码就和最上面的事例代码一样。只需要在HttpMethod method = new GetMethod("http://www.apache.org");加上一行HTTP头的设置即可
1.method.setRequestHeader("Connection", "close");
method.setRequestHeader("Connection", "close");
看一下HTTP协议中关于这个属性的定义:
HTTP/1.1 defines the "close" connection option for the sender to signal that the connection will be closed after completion of the response. For example,
Connection: close
现在再说一下客户端关闭链接和服务器端关闭链接的区别。如果采用客户端关闭链接的方法,在客户端的机器上使用netstat &an命令会看到很多TIME_WAIT的TCP链接。如果服务器端主动关闭链接这中情况就出现在服务器端。
参考WIKI上的说明http://wiki.apache.org/HttpComponents/FrequentlyAskedConnectionManagementQuestions
The TIME_WAIT state is a protection mechanism in TCP. The side that closes a socket connection orderly will keep the connection in state TIME_WAIT for some time, typically between 1 and 4 minutes.
TIME_WAIT的状态会出现在主动关闭链接的这一端。TCP协议中TIME_WAIT状态主要是为了保证数据的完整传输。具体可以参考此文档:
http://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html#ss2.7
另外强调一下使用上面这些方法关闭链接是在我们的应用中明确知道不需要重用链接时可以主动关闭链接来释放资源。如果你的应用是需要重用链接的话就没必要这么做,使用原有的链接还可以提供性能。
8 楼 javatar
官方文档有建议使用空闲连接检查线程:
1.import mons.httpclient.util.IdleConnectionTimeoutT
2.// 创建线程
3.IdleConnectionTimeoutThread thread = new IdleConnectionTimeoutThread();
4.// 注册连接管理器
5.thread.addConnectionManager(httpClient.getHttpConnectionManager());
6.// 启动线程
7.thread.start();
8.// 在最后,关闭线程
9.thread.shutdown();
import mons.httpclient.util.IdleConnectionTimeoutT
// 创建线程
IdleConnectionTimeoutThread thread = new IdleConnectionTimeoutThread();
// 注册连接管理器
thread.addConnectionManager(httpClient.getHttpConnectionManager());
// 启动线程
thread.start();
// 在最后,关闭线程
thread.shutdown();
7 楼 sdh-29
楼上的说法是对的, 但是, 性能来说, 是不合适的.
首先, 我想问你, 既然是轮询, 为什么HttpURLConnection要每次创建.
既然是个持续的过程, 就不应该每次HttpURLConnection对象重新创建, 你应该重复使用这个对象. HttpURLConnection会持有连接的, 如果断开, 我记得他会去尝试连接.
6 楼 SeanHe
但使用netstat发现很多TIME_WAIT,时间长点后会出现端口都被占用的状况 address already in use :connect,使用HttpURLConnection.disconnect()也没有效果。
如果你是应为客户端出现很多的TIME_WAIT造成端口占用,你不妨试一下&方法四&在HTTP请求头上设置"Connection", "close"这样服务器会来主动关闭链接,这样就不会在客户端产生很多的TIME_WAIT
我是google到这里的。。。
您对HttpClient 的关闭分析的很透彻
请教HttpURLConnection和URLConnection怎么关闭链接?
我需要轮询一个网址,但使用netstat发现很多TIME_WAIT,时间长点后会出现端口都被占用的状况 address already in use :connect,使用HttpURLConnection.disconnect()也没有效果。
4 楼 fly_ever
另外强调一下使用上面这些方法关闭链接是在我们的应用中明确知道不需要重用链接时可以主动关闭链接来释放资源。如果你的应用是需要重用链接的话就没必要这么做,使用原有的链接还可以提供性能。
正如你所说,实际上是只有并发量很高的时候才会发生链接占满的情况。
而如果没有这么高的并发量,我们没必要去实现代码来关闭连接。
因为我们在代码中实现主动关闭的功能的同时,也丧失了性能上的优势。
因此,我觉得遇到这种情况,应该把设置中的keepAliveTimeout调低,显得更好一些。
1.1. Timeout 30
2.2. KeepAlive On
#表示服务器端不会主动关闭链接
3.3. MaxKeepAliveRequests 100
4.4. KeepAliveTimeout 180
1. Timeout 30
2. KeepAlive On
#表示服务器端不会主动关闭链接
3. MaxKeepAliveRequests 100
4. KeepAliveTimeout 180
不过,这里提供的HttpClient关闭的详细信息,还是很有价值的。
3 楼 jorsef
受用,非常感谢
2 楼 SeanHe
HttpClient client = new HttpClient(); 如果这样进行实例化,默认使用SimpleHttpConnectionManager作为connection manager,SimpleHttpConnectionManager没有连接池,只管理一个连接
1 楼 JavaTestJava
实例化代码使用:HttpClient client = new HttpClient();
在method.releaseConnection();之后加上
((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();
((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();
shutdown源代码很简单,看了一目了然
public void shutdown() {
httpConnection.close();
public void shutdown() {
httpConnection.close();
方法二中的httpConnection.close();这里没有参数么?
httpConnection为默认全局的那个连接?
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1851502次
积分:22395
积分:22395
排名:第269名
原创:434篇
转载:242篇
评论:413条
(1)(1)(2)(1)(3)(9)(1)(2)(4)(5)(1)(5)(4)(5)(1)(8)(1)(6)(1)(2)(3)(1)(3)(3)(1)(3)(3)(1)(1)(6)(7)(4)(5)(7)(9)(4)(2)(10)(10)(15)(5)(7)(4)(14)(10)(5)(1)(2)(3)(2)(2)(1)(1)(10)(9)(1)(9)(7)(2)(28)(1)(18)(13)(4)(11)(10)(5)(2)(9)(2)(5)(7)(6)(5)(19)(39)(40)(43)(35)(21)(10)(19)(5)(15)(34)(17)HttpClient:是一个接口
首先需要先创建一个DefaultHttpClient的实例
HttpClient httpClient=new DefaultHttpClient();
发送GET请求:
先创建一个HttpGet对象,传入目标的网络地址,然后调用HttpClient的execute()方法即可:
HttpGet HttpGet=new HttpGet(&&);
httpClient.execute(httpGet);
发送POST请求:
创建一个HttpPost对象,传入目标的网络地址:
HttpPost httpPost=new HttpPost(&&);
通过一个NameValuePair集合来存放待提交的参数,并将这个参数集合传入到一个UrlEncodedFormEntity中,然后调用HttpPost的setEntity()方法将构建好的UrlEncodedFormEntity传入:
List&NameValuePair&params=newArrayList&NameValuePair&();
Params.add(new BasicNameValuePair(&username&,&admin&));
Params.add(new BasicNameValuePair(&password&,&123456&));
UrlEncodedFormEntity entity=newUrlEncodedFormEntity(params,&utf-8&);
httpPost.setEntity(entity);
调用HttpClient的execute()方法,并将HttpPost对象传入即可:
HttpClient.execute(HttpPost);
执行execute()方法之后会返回一个HttpResponse对象,服务器所返回的所有信息就保护在HttpResponse里面.
先取出服务器返回的状态码,如果等于200就说明请求和响应都成功了:
If(httpResponse.getStatusLine().getStatusCode()==200){
//请求和响应都成功了
HttpEntityentity=HttpResponse.getEntity();//调用getEntity()方法获取到一个HttpEntity实例
Stringresponse=EntityUtils.toString(entity,&utf-8&);//用EntityUtils.toString()这个静态方法将HttpEntity转换成字符串,防止服务器返回的数据带有中文,所以在转换的时候将字符集指定成utf-8就可以了
Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们再讨论),它不仅是客户端发送Http请求变得容易,而且也方便了开发人员测试接口(基于Http协议的),即提高了开发的效率,也方便提高代码的健壮性。因此熟练掌握HttpClient是很重要的必修内容,掌握HttpClient后,相信对于Http协议的了解会更加深入。
HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。HttpClient已经应用在很多的项目中,比如Apache Jakarta上很著名的另外两个开源项目Cactus和HTMLUnit都使用了HttpClient。
下载地址:&
1. 基于标准、纯净的java语言。实现了Http1.0和Http1.1
2. 以可扩展的面向对象的结构实现了Http全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)。
3. 支持HTTPS协议。
4. 通过Http代理建立透明的连接。
5. 利用CONNECT方法通过Http代理建立隧道的https连接。
6. Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos认证方案。
7. 插件式的自定义认证方案。
8. 便携可靠的套接字工厂使它更容易的使用第三方解决方案。
9. 连接管理器支持多线程应用。支持设置最大连接数,同时支持设置每个主机的最大连接数,发现并关闭过期的连接。
10. 自动处理Set-Cookie中的Cookie。
11. 插件式的自定义Cookie策略。
12. Request的输出流可以避免流中内容直接缓冲到socket服务器。
13. Response的输入流可以有效的从socket服务器直接读取相应内容。
14. 在http1.0和http1.1中利用KeepAlive保持持久连接。
15. 直接获取服务器发送的response code和 headers。
16. 设置连接超时的能力。
17. 实验性的支持http1.1 response caching。
18. 源代码基于Apache License 可免费获取。
三、使用方法
使用HttpClient发送请求、接收响应很简单,一般需要如下几步即可。
1.&创建HttpClient对象。
2.&创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。
3.&如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。
4.&调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse。
5.&调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
6.&释放连接。无论执行方法是否成功,都必须释放连接
1 package com.fico.rma.
3 import java.io.F
4 import java.io.FileInputS
5 import java.io.IOE
6 import java.net.URL;
7 import mons.io.FileU
8 import org.apache.http.HttpE
9 import org.apache.http.HttpR
10 import org.apache.http.HttpS
11 import org.apache.http.client.methods.HttpP
12 import org.apache.http.entity.InputStreamE
13 import org.apache.http.impl.client.DefaultHttpC
14 import com.fico.rma.RmaC
15 import com.fico.rma.util.RmaC
17 public class ReleaseService {
private final static String SEP=File.
public static void createRelease(String releaseName,String projectName) throws IOException{
String dir=RmaConfig.getString("basepath");
projectName = projectName.toLowerCase();
File destAdbFile=new File(dir+SEP+RmaConstants.RELEASE_DIR+SEP+releaseName+SEP+projectName+".adb");
File srcAdbFile = null;
if("dldsunproject".equals(projectName.toLowerCase().toString().trim())){
srcAdbFile=new File(dir+SEP+RmaConstants.ADB_DIR+SEP+"DldsUnProject"+SEP+"adb"+SEP+"Pingan.server_service_0.adb");
srcAdbFile=new File(dir+SEP+RmaConstants.ADB_DIR+SEP+"PingAnProject"+SEP+"adb"+SEP+"Pingan.server_service_0.adb");
FileUtils.copyFile(srcAdbFile, destAdbFile);
public static void pulishRelease(String adbFile,String url,String projectName) throws IOException {
DefaultHttpClient httpclient = new DefaultHttpClient();
File file = new File(adbFile);
InputStreamEntity reqEntity = new InputStreamEntity(new FileInputStream(file), -1);
reqEntity.setContentType("binary/octet-stream");
reqEntity.setChunked(true);
HttpPost httppost = new HttpPost(url);
httppost.setEntity(reqEntity);
httppost.setHeader("projectName", projectName);
HttpResponse response = httpclient.execute(httppost);
//System.out.println("response.getStatusLine().getStatusCode():"+response.getStatusLine().getStatusCode());
if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {
HttpEntity enty = response.getEntity();
&以上是发送端的代码,下面是接受端的代码:
1 package com.pingan.
3 import java.io.F
4 import java.io.FileOutputS
5 import java.io.IOE
6 import java.io.InputS
7 import java.io.OutputS
8 import java.io.PrintW
10 import javax.servlet.ServletC
11 import javax.servlet.ServletE
12 import javax.servlet.annotation.WebS
13 import javax.servlet.http.HttpS
14 import javax.servlet.http.HttpServletR
15 import javax.servlet.http.HttpServletR
16 import com.blazesoft.server.local.NdLocalServerE
* Servlet implementation class ReleaseServlet
21 @WebServlet("/Release")
22 public class ReleaseServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
* @see HttpServlet#HttpServlet()
public ReleaseServlet() {
// TODO Auto-generated constructor stub
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
* 功能点:RMA这发布adb包的时候的res调用的入口点。
功能1:获取到RMA中具体是哪个项目
功能2:根据项目把adb包和对应的.server文件拷贝到不同的目录下(目录名称为RMA的项目名称的小写)
功能3:重新加载相对应的adb文件到对应的server中
String projectName ="";
projectName =request.getHeader("projectName");
projectName = projectName.toLowerCase();
String sep=File.
String dir=ResConfig.getString("basepath");
String dir = "";
dir=ResConfig.getProperties().getProperty("basepath");
String adbFile=dir+sep+"production"+sep+projectName+sep+projectName+".adb";
String serverFile =
dir+sep+"production"+sep+projectName+sep+projectName+".server";
OutputStream out = new FileOutputStream(adbFile);
InputStream in = request.getInputStream();
int length = 0;
byte[] buf = new byte[1024];
while ((length = in.read(buf)) != -1) {
out.write(buf, 0, length);
in.close();
out.close();
//功能3:重启server
reloadRuleServer(request,projectName,serverFile);
response.setContentType("text/xml");
PrintWriter pw=response.getWriter();
pw.write("&Response&&Result&OK&/Result&&/Response&");
pw.flush();
private void reloadRuleServer(HttpServletRequest req,String projectName,String serverFile) throws ServletException{
projectName = projectName.toLowerCase();
String projectDefinition1 = "";
if (!"".equals(projectName)&&projectName.length()&0) {
projectDefinition1 = ResConfig.getProperties().getProperty(projectName+"_df");
Server server = null;
ServletContext context=req.getServletContext();
server = (Server)context.getAttribute(projectName+"Server");
if(server==null){
server = (Server)Server.createServer(serverFile);
context.setAttribute(projectName+"Server", server);
server.resetService(projectDefinition1);
PriorRunner runner=new PriorRunner(server,100);
runner.run();
} catch (NdLocalServerException e) {
e.printStackTrace();
throw new ServletException(e.getMessage());
HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。本文首先介绍 HTTPClient,然后根据作者实际工作经验给出了一些常见问题的解决方法。HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java.net 包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。现在HttpClient最新版本为 HttpClient 4.0-beta2 2.HttpClient 功能介绍   以下列出的是 HttpClient 提供的主要的功能,要知道更多详细的功能可以参见 HttpClient 的主页。   (1)实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)&   (2)支持自动转向&   (3)支持 HTTPS 协议&   (4)支持代理服务器等 3.HttpClient 基本功能的使用   (1) GET方法   使用 HttpClient 需要以下 6 个步骤:   1. 创建 HttpClient 的实例   2. 创建某种连接方法的实例,在这里是 GetMethod。在 GetMethod 的构造函数中传入待连接的地址   3. 调用第一步中创建好的实例的 execute 方法来执行第二步中创建好的 method 实例   4. 读 response   5. 释放连接。无论执行方法是否成功,都必须释放连接   6. 对得到后的内容进行处理   根据以上步骤,我们来编写用GET方法来取得某网页内容的代码。   大部分情况下 HttpClient 默认的构造函数已经足够使用。 HttpClient httpClient = new HttpClient();   创建GET方法的实例。在GET方法的构造函数中传入待连接的地址即可。用GetMethod将会自动处理转发过程,如果想要把自动处理转发过程去掉的话,可以调用方法setFollowRedirects(false)。 GetMethod getMethod = new GetMethod(".....");   调用实例httpClient的executeMethod方法来执行getMethod。由于是执行在网络上的程序,在运行executeMethod方法的时候,需要处理两个异常,分别是HttpException和IOException。引起第一种异常的原因主要可能是在构造getMethod的时候传入的协议不对,比如不小心将"http"写成"htp",或者服务器端返回的内容不正常等,并且该异常发生是不可恢复的;第二种异常一般是由于网络原因引起的异常,对于这种异常 (IOException),HttpClient会根据你指定的恢复策略自动试着重新执行executeMethod方法。HttpClient的恢复策略可以自定义(通过实现接口HttpMethodRetryHandler来实现)。通过httpClient的方法setParameter设置你实现的恢复策略,本文中使用的是系统提供的默认恢复策略,该策略在碰到第二类异常的时候将自动重试3次。executeMethod返回值是一个整数,表示了执行该方法后服务器返回的状态码,该状态码能表示出该方法执行是否成功、需要认证或者页面发生了跳转(默认状态下GetMethod的实例是自动处理跳转的)等。 //设置成了默认的恢复策略,在发生异常时候将自动重试3次,在这里你也可以设置成自定义的恢复策略   
1  getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
2   new DefaultHttpMethodRetryHandler());
3   //执行getMethod
4   int statusCode = client.executeMethod(getMethod);
5   if (statusCode != HttpStatus.SC_OK) {
6   System.err.println("Method failed: " + getMethod.getStatusLine());
  在返回的状态码正确后,即可取得内容。取得目标地址的内容有三种方法:第一种,getResponseBody,该方法返回的是目标的二进制的byte流;第二种,getResponseBodyAsString,这个方法返回的是String类型,值得注意的是该方法返回的String的编码是根据系统默认的编码方式,所以返回的String值可能编码类型有误,在本文的"字符编码"部分中将对此做详细介绍;第三种,getResponseBodyAsStream,这个方法对于目标地址中有大量数据需要传输是最佳的。在这里我们使用了最简单的getResponseBody方法。 byte[] responseBody = method.getResponseBody();   释放连接。无论执行方法是否成功,都必须释放连接。 method.releaseConnection();   处理内容。在这一步中根据你的需要处理内容,在例子中只是简单的将内容打印到控制台。 System.out.println(new String(responseBody));   下面是程序的完整代码:
2 import java.io.IOE
3 import mons.httpclient.*;
4 import mons.httpclient.methods.GetM
5 import mons.httpclient.params.HttpMethodP
6 public class GetSample{
public static void main(String[] args) {
//构造HttpClient的实例
HttpClient httpClient = new HttpClient();
//创建GET方法的实例
GetMethod getMethod = new GetMethod("...");
//使用系统提供的默认的恢复策略
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler());
//执行getMethod
int statusCode = httpClient.executeMethod(getMethod);
if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: "
+ getMethod.getStatusLine());
//读取内容
byte[] responseBody = getMethod.getResponseBody();
//处理内容
System.out.println(new String(responseBody));
} catch (HttpException e) {
//发生致命的异常,可能是协议不对或者返回的内容有问题
System.out.println("Please check your provided http address!");
e.printStackTrace();
} catch (IOException e) {
//发生网络异常
e.printStackTrace();
} finally {
//释放连接
getMethod.releaseConnection();
(2)POST方法   根据RFC2616,对POST的解释如下:POST方法用来向目的服务器发出请求,要求它接受被附在请求后的实体,并把它当作请求队列(Request-Line)中请求URI所指定资源的附加新子项。POST被设计成用统一的方法实现下列功能:   对现有资源的注释(Annotation of existing resources)&   向电子公告栏、新闻组,邮件列表或类似讨论组发送消息&   提交数据块,如将表单的结果提交给数据处理过程&   通过附加操作来扩展数据库&   调用HttpClient中的PostMethod与GetMethod类似,除了设置PostMethod的实例与GetMethod有些不同之外,剩下的步骤都差不多。在下面的例子中,省去了与GetMethod相同的步骤,只说明与上面不同的地方,并以登录清华大学BBS为例子进行说明。   构造PostMethod之前的步骤都相同,与GetMethod一样,构造PostMethod也需要一个URI参数。在创建了PostMethod的实例之后,需要给method实例填充表单的值,在BBS的登录表单中需要有两个域,第一个是用户名(域名叫id),第二个是密码(域名叫passwd)。表单中的域用类NameValuePair来表示,该类的构造函数第一个参数是域名,第二参数是该域的值;将表单所有的值设置到PostMethod中用方法setRequestBody。另外由于BBS登录成功后会转向另外一个页面,但是HttpClient对于要求接受后继服务的请求,比如POST和PUT,不支持自动转发,因此需要自己对页面转向做处理。具体的页面转向处理请参见下面的"自动转向"部分。代码如下:     
1 String url = "....";
PostMethod postMethod = new PostMethod(url);
// 填入各个表单域的值
NameValuePair[] data = { new NameValuePair("id", "youUserName"),
new NameValuePair("passwd", "yourPwd") };
// 将表单的值放入postMethod中
postMethod.setRequestBody(data);
// 执行postMethod
int statusCode = httpClient.executeMethod(postMethod);
// HttpClient对于要求接受后继服务的请求,象POST和PUT等不能自动处理转发
// 301或者302
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
// 从头中取出转向的地址
Header locationHeader = postMethod.getResponseHeader("location");
String location = null;
if (locationHeader != null) {
location = locationHeader.getValue();
System.out.println("The page was redirected to:" + location);
System.err.println("Location field value is null.");
 4 使用HttpClient过程中常见的一些问题   下面介绍在使用HttpClient过程中常见的一些问题。   字符编码   某目标页的编码可能出现在两个地方,第一个地方是服务器返回的http头中,另外一个地方是得到的html/xml页面中。   在http头的Content-Type字段可能会包含字符编码信息。例如可能返回的头会包含这样子的信息:Content-Type: text/ charset=UTF-8。这个头信息表明该页的编码是UTF-8,但是服务器返回的头信息未必与内容能匹配上。比如对于一些双字节语言国家,可能服务器返回的编码类型是UTF-8,但真正的内容却不是UTF-8编码的,因此需要在另外的地方去得到页面的编码信息;但是如果服务器返回的编码不是UTF-8,而是具体的一些编码,比如gb2312等,那服务器返回的可能是正确的编码信息。通过method对象的getResponseCharSet()方法就可以得到http头中的编码信息。&   对于象xml或者html这样的文件,允许作者在页面中直接指定编码类型。比如在html中会有&meta http-equiv="Content-Type" content="text/ charset=gb2312"/&这样的标签;或者在xml中会有&?xml version="1.0" encoding="gb2312"?&这样的标签,在这些情况下,可能与http头中返回的编码信息冲突,需要用户自己判断到底那种编码类型应该是真正的编码。&   自动转向   根据RFC2616中对自动转向的定义,主要有两种:301和302。301表示永久的移走(Moved Permanently),当返回的是301,则表示请求的资源已经被移到一个固定的新地方,任何向该地址发起请求都会被转到新的地址上。302表示暂时的转向,比如在服务器端的servlet程序调用了sendRedirect方法,则在客户端就会得到一个302的代码,这时服务器返回的头信息中location的值就是sendRedirect转向的目标地址。   HttpClient支持自动转向处理,但是象POST和PUT方式这种要求接受后继服务的请求方式,暂时不支持自动转向,因此如果碰到POST方式提交后返回的是301或者302的话需要自己处理。就像刚才在POSTMethod中举的例子:如果想进入登录BBS后的页面,必须重新发起登录的请求,请求的地址可以在头字段location中得到。不过需要注意的是,有时候location返回的可能是相对路径,因此需要对location返回的值做一些处理才可以发起向新地址的请求。   另外除了在头中包含的信息可能使页面发生重定向外,在页面中也有可能会发生页面的重定向。引起页面自动转发的标签是:&meta http-equiv="refresh" content="5; url=...."&。如果你想在程序中也处理这种情况的话得自己分析页面来实现转向。需要注意的是,在上面那个标签中url的值也可以是一个相对地址,如果是这样的话,需要对它做一些处理后才可以转发。   处理HTTPS协议   HttpClient提供了对SSL的支持,在使用SSL之前必须安装JSSE。在Sun提供的1.4以后的版本中,JSSE已经集成到JDK中,如果你使用的是JDK1.4以前的版本则必须安装JSSE。JSSE不同的厂家有不同的实现。下面介绍怎么使用HttpClient来打开Https连接。这里有两种方法可以打开https连接,第一种就是得到服务器颁发的证书,然后导入到本地的keystore中;另外一种办法就是通过扩展HttpClient的类来实现自动接受证书。   方法1,取得证书,并导入本地的keystore:   安装JSSE (如果你使用的JDK版本是1.4或者1.4以上就可以跳过这一步)。本文以IBM的JSSE为例子说明。先到IBM网站上下载JSSE的安装包。然后解压开之后将ibmjsse.jar包拷贝到&java-home&\lib\ext\目录下。&   取得并且导入证书。证书可以通过IE来获得:&   1. 用IE打开需要连接的https网址,会弹出如下对话框:   2. 单击"View Certificate",在弹出的对话框中选择"Details",然后再单击"Copy to File",根据提供的向导生成待访问网页的证书文件   3. 向导第一步,欢迎界面,直接单击"Next",   4. 向导第二步,选择导出的文件格式,默认,单击"Next",   5. 向导第三步,输入导出的文件名,输入后,单击"Next",   6. 向导第四步,单击"Finish",完成向导   7. 最后弹出一个对话框,显示导出成功   用keytool工具把刚才导出的证书倒入本地keystore。Keytool命令在&java-home&\bin\下,打开命令行窗口,并到&java-home&\lib\security\目录下,运行下面的命令:   keytool -import -noprompt -keystore cacerts -storepass changeit -alias yourEntry1 -file your.cer   其中参数alias后跟的值是当前证书在keystore中的唯一标识符,但是大小写不区分;参数file后跟的是刚才通过IE导出的证书所在的路径和文件名;如果你想删除刚才导入到keystore的证书,可以用命令:   keytool -delete -keystore cacerts -storepass changeit -alias yourEntry1   写程序访问https地址。如果想测试是否能连上https,只需要稍改一下GetSample例子,把请求的目标变成一个https地址。&   GetMethod getMethod = new GetMethod("your url");   运行该程序可能出现的问题:   1. 抛出异常java.net.SocketException: Algorithm SSL not available。出现这个异常可能是因为没有加JSSEProvider,如果用的是IBM的JSSE Provider,在程序中加入这样的一行:   if(Security.getProvider("com.ibm.jsse.IBMJSSEProvider") == null)   Security.addProvider(new IBMJSSEProvider());   或者也可以打开&java-home&\lib\security\java.security,在行   security.provider.1=sun.security.provider.Sun   security.provider.2=com.ibm.crypto.provider.IBMJCE   后面加入security.provider.3=com.ibm.jsse.IBMJSSEProvider   2. 抛出异常java.net.SocketException: SSL implementation not available。出现这个异常可能是你没有把ibmjsse.jar拷贝到&java-home&\lib\ext\目录下。   3. 抛出异常javax.net.ssl.SSLHandshakeException: unknown certificate。出现这个异常表明你的JSSE应该已经安装正确,但是可能因为你没有把证书导入到当前运行JRE的keystore中,请按照前面介绍的步骤来导入你的证书。   方法2,扩展HttpClient类实现自动接受证书   因为这种方法自动接收所有证书,因此存在一定的安全问题,所以在使用这种方法前请仔细考虑您的系统的安全需求。具体的步骤如下:   提供一个自定义的socket factory(test.MySecureProtocolSocketFactory)。这个自定义的类必须实现接口mons.httpclient.protocol.SecureProtocolSocketFactory,在实现接口的类中调用自定义的X509TrustManager(test.MyX509TrustManager),这两个类可以在随本文带的附件中得到&   创建一个mons.httpclient.protocol.Protocol的实例,指定协议名称和默认的端口号 Protocol myhttps = new Protocol("https", new MySecureProtocolSocketFactory (), 443);   注册刚才创建的https协议对象 Protocol.registerProtocol("https ", myhttps);   然后按照普通编程方式打开https的目标地址,代码请参见test.NoCertificationHttpsGetSample [编辑本段]5 处理代理服务器   HttpClient中使用代理服务器非常简单,调用HttpClient中setProxy方法就可以,方法的第一个参数是代理服务器地址,第二个参数是端口号。另外HttpClient也支持SOCKS代理。   httpClient.getHostConfiguration().setProxy(hostName,port); 一般的情况下我们都是使用IE或者Navigator浏览器来访问一个WEB服务器,用来浏览页面查看信息或者提交一些数据等等。所访问的这些页面有的仅仅是一些普通的页面,有的需要用户登录后方可使用,或者需要认证以及是一些通过加密方式传输,例如HTTPS。目前我们使用的浏览器处理这些情况都不会构成问题。不过你可能在某些时候需要通过程序来访问这样的一些页面,比如从别人的网页中&偷&一些数据;利用某些站点提供的页面来完成某种功能,例如说我们想知道某个手机号码的归属地而我们自己又没有这样的数据,因此只好借助其他公司已有的网站来完成这个功能,这个时候我们需要向网页提交手机号码并从返回的页面中解析出我们想要的数据来。如果对方仅仅是一个很简单的页面,那我们的程序会很简单,本文也就没有必要大张旗鼓的在这里浪费口舌。但是考虑到一些服务授权的问题,很多公司提供的页面往往并不是可以通过一个简单的URL就可以访问的,而必须经过注册然后登录后方可使用提供服务的页面,这个时候就涉及到COOKIE问题的处理。我们知道目前流行的动态网页技术例如ASP、JSP无不是通过COOKIE来处理会话信息的。为了使我们的程序能使用别人所提供的服务页面,就要求程序首先登录后再访问服务页面,这过程就需要自行处理cookie,想想当你用java.net.HttpURLConnection来完成这些功能时是多么恐怖的事情啊!况且这仅仅是我们所说的顽固的WEB服务器中的一个很常见的&顽固&!再有如通过HTTP来上传文件呢?不需要头疼,这些问题有了&它&就很容易解决了!& 我们不可能列举所有可能的顽固,我们会针对几种最常见的问题进行处理。当然了,正如前面说到的,如果我们自己使用java.net.HttpURLConnection来搞定这些问题是很恐怖的事情,因此在开始之前我们先要介绍一下一个开放源码的项目,这个项目就是Apache开源组织中的httpclient,它隶属于Jakarta的commons项目,目前的版本是2.0RC2。commons下本来已经有一个net的子项目,但是又把httpclient单独提出来,可见http服务器的访问绝非易事。& Commons-httpclient项目就是专门设计来简化HTTP客户端与服务器进行各种通讯编程。通过它可以让原来很头疼的事情现在轻松的解决,例如你不再管是HTTP或者HTTPS的通讯方式,告诉它你想使用HTTPS方式,剩下的事情交给httpclient替你完成。本文会针对我们在编写HTTP客户端程序时经常碰到的几个问题进行分别介绍如何使用httpclient来解决它们,为了让读者更快的熟悉这个项目我们最开始先给出一个简单的例子来读取一个网页的内容,然后循序渐进解决掉前进中的所有问题。&
阅读(...) 评论()

我要回帖

更多关于 new httpsolrclient 的文章

 

随机推荐