[HttpClient] HttpClient 使用
Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们再讨论),它不仅是客户端发送Http请求变得容易,而且也方便了开发人员测试接口(基于Http协议的),即提高了开发的效率,也方便提高代码的健壮性。因此熟练掌握HttpClient是很重要的必修内容,掌握HttpClient后,相信对于Http协议的了解会更加深入。
一、简介
HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。HttpClient已经应用在很多的项目中,比如Apache Jakarta上很著名的另外两个开源项目Cactus和HTMLUnit都使用了HttpClient。
下载地址: http://hc.apache.org/downloads.cgi
二、特性
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作为访问Http服务的客户端访问程序已经被广泛使用,对于HttpClient的使用方法也有很多blog进行介绍,本文简明扼要的介绍HttpClient的两种使用方式——简单连接管理的HttpClient(BasicClientConnectionManager)和池化的HttpClient(PoolingClientConnectionManager)。
HttpClient从4.2开始抛弃了先前的SingleClientConnManager和ThreadSafeConnManger,取而代之的是BasicClientConnectionManager和PoolingClientConnectionManager。
BasicClientConnectionManager内部只维护一个活动的connection,尽管这个类是线程安全的,但是最好在一个单独的线程中重复使用它。如果在同一个BasicClientConnectionManager对象中,多次执行http请求,后继请求与先前请求是同一个route,那么BasicClientConnectionManager会使用同一个连接完成后续请求,否则,BasicClientConnectionManager会将先前的connection关闭,然后为后续请求创建一个新的连接。换句话说,BasicClientConnectionManager会尽力复用先前的连接(注意:创建连接和销毁连接都是不小的开销),因此,如果对同一个service有多个连续请求,应该尽量使用同一个BasicClientConnectionManager完成。
PoolingClientConnectionManager可以在多线程中使用,连接按照route被缓存(pooled),当后续的请求route已经在pool中存在,就会使用pool中先前使用的connection获取请求结果。PoolingClientConnectionManager对每个router维护的connection数目有上限要求,默认情况下,每个router最多维护两个并发线程的connection连接,整个pool最多容纳20个并发的connections。当然可以通过设置来修改这些限制。
案例一、
使用最简单的方式创建的httpclient内部使用的是BasicClientConnectionManager管理连接,并且使用HttpClient提供的EntityUtils将消息体转换成String输出。httpClient对象可以被重复使用,执行多个http请求,当你真正不需要他的时候,记得调用shutdown,关闭并且释放占用的资源。
package com.wa.dataprocess.test; import java.io.IOException; import java.nio.charset.Charset; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; public class HttpClientTest { public static void main(String[] args) { HttpClient client = new DefaultHttpClient(); //创建get请求实例 HttpGet get = new HttpGet("http://www.baidu.com"); System.out.println("请求的uri为:"+get.getURI()); try { // 客户端执行get请求 返回响应实体 HttpResponse response = client.execute(get); //获取请求状态行 System.out.println("请求状态行为:"+response.getStatusLine()); //获取所有的请求头 Header[] headers=response.getAllHeaders(); for (Header header :headers){ //遍历获取所有请求头的名称和值 System.out.println(header.getName()+" :--: "+header.getValue()); } System.out.println("-----------------------------------------------"); //获取响应的实体 HttpEntity entity =response.getEntity(); if (entity!=null){ System.out.println(EntityUtils.toString(entity,Charset.forName("UTF-8"))); System.out.println("================================="); System.out.println("内容长度为:"+entity.getContentLength()); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ //释放连接 client.getConnectionManager().shutdown(); } } }
最基本的使用,输出信息为:
请求的uri为:http://www.baidu.com
请求状态行为:HTTP/1.1 200 OK
Date :--: Thu, 19 Mar 2015 08:54:00 GMT
Content-Type :--: text/html; charset=utf-8
Transfer-Encoding :--: chunked
Connection :--: Keep-Alive
Vary :--: Accept-Encoding
Set-Cookie :--: BAIDUID=EAC4016D4235E694E4EEBF4327D19E44:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie :--: BIDUPSID=EAC4016D4235E694E4EEBF4327D19E44; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie :--: BDSVRTM=0; path=/
Set-Cookie :--: BD_HOME=0; path=/
Set-Cookie :--: H_PS_PSSID=11327_1468_12788_12882_12824_12574_12691_12694_12901_12849_12723_12797_12737_12782_8498; path=/; domain=.baidu.com
P3P :--: CP=" OTI DSP COR IVA OUR IND COM "
Cache-Control :--: private
Cxy_all :--: baidu+0a4b635a28477f8a9ebd7fb1cdbeb840
Expires :--: Thu, 19 Mar 2015 08:53:08 GMT
X-Powered-By :--: HPHP
Server :--: BWS/1.1
BDPAGETYPE :--: 1
BDQID :--: 0xc6a9b12700007937
BDUSERID :--: 0
-----------------------------------------------
/*这里是百度首页的源代码,好大一坨,就留这么多吧.....*/
<!DOCTYPE html><!--STATUS OK--><html><head><meta http-equiv="content-type" content="text/html;charset=utf-8"><meta http-equiv="X-UA-Compatible" content="IE=Edge"><meta content="always" name="referrer"><link rel="dns-prefetch" href="//s1.bdstatic.com"/><link rel="dns-<script>if(navigator.cookieEnabled){document.cookie="NOJS=;expires=Sat, 01 Jan 2000 00:00:00 GMT";}</script></body></html>
=================================
内容长度为:-1
案例二、
PoolingClientConnectionManager的创建过程要比BasicClientConnectionManager复杂的多,当然,我们为了举例子,将PoolingClientConnectionManager可能涉及到的多个方面属性都列出来,可以根据需要设置或者使用默认的连接属性。首先我们创建了SchemeRegistry,之后注册了两种http访问方式http和https,创建PoolingClientConnectionManager,指定每个router最多保持20个活动的connection,这个pool最多保持200个活动的connection,对本地的访问可以保持80个活动的connection。最后创建httpClient,设置SO_TIMEOUT和CONNECTION_TIMEOUT,执行请求。在httpClient不再使用时,关闭connectionManager。
package com.wa.dataprocess.test; import java.nio.charset.Charset; import javax.net.ssl.SSLContext; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.PoolingClientConnectionManager; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.util.EntityUtils; /** * 基于 * @author Administrator * */ public class PoolHttpClient { public static void main(String[] args) { //创建schema SchemeRegistry registry = new SchemeRegistry(); HttpClient httpClient =null; try { //https类型的消息访问 SSLContext context = SSLContext.getInstance("SSL"); context.init(null,null,null); SSLSocketFactory sslFactory = new SSLSocketFactory(context,SSLSocketFactory.STRICT_HOSTNAME_VERIFIER); registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));//http 80 端口 registry.register(new Scheme("https", 443, sslFactory));//https 443端口 PoolingClientConnectionManager cm =new PoolingClientConnectionManager(registry);//创建connectionManager cm.setDefaultMaxPerRoute(20);//对每个指定连接的服务器(指定的ip)可以创建并发20 socket进行访问 cm.setMaxTotal(200);//创建socket的上线是200 HttpHost localhost = new HttpHost("locahost", 80); cm.setMaxPerRoute(new HttpRoute(localhost), 80);//对本机80端口的socket连接上限是80 httpClient = new DefaultHttpClient(cm);//使用连接池创建连接 HttpParams params = httpClient.getParams(); HttpConnectionParams.setSoTimeout(params, 60*1000);//设定连接等待时间 HttpConnectionParams.setConnectionTimeout(params, 60*1000);//设定超时时间 HttpGet httpGet = new HttpGet("http://m.weather.com.cn/data/101010100.html"); HttpResponse response = httpClient.execute(httpGet); String result = EntityUtils.toString(response.getEntity(), Charset.forName("utf-8")); System.out.println(result); } catch (Exception e) { e.printStackTrace(); }finally{ httpClient.getConnectionManager().shutdown(); } } }
返回内容:
<html>
<head>
</head>
<body>
<script type="text/javascript">
window.onload = function() {
window.open("/","_self");
};
</script>
<!-- START WRating v1.0 -->
<script type="text/javascript" src="http://c.wrating.com/a1.js">
</script>
<script type="text/javascript">
var vjAcc="860010-2151010100";
var wrUrl="http://c.wrating.com/";
vjTrack("");
</script>
<noscript><img src="http://c.wrating.com/a.gif?a=&c=860010-2151010100" width="1" height="1"/></noscript>
<!-- END WRating v1.0 -->
</body>
</html>
参考文章:
http://blog.csdn.net/wangpeng047/article/details/19624529

浙公网安备 33010602011771号