HttpClient和HttpURLConnection的使用和区别

原创
2017/11/17 13:41
阅读数 1.5K

相关介绍

  • TCP/IP、Socket、HTTP简要介绍
  1. TCP/IP中文名为传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成。
  2. Socket是支持TCP/IP协议的网络通信基本操作单元,许多操作系统为应用程序提供了一套调用接口(API),方便开发者开发网络程序。注意,socket本身并不是协议,只是提供一个针对TCP或UDP的编程接口。
  3. HTTP协议是一个web服务器和客户端通信的超文本传送协议,是建立在TCP协议上的一个应用层协议。
  4. HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。
  • HTTP1.0 客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。
  • HTTP1.1 可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。

一、HttpClient的用法

1、执行请求

HttpClient最重要的功能是执行HTTP方法。一个HTTP方法的执行包含一个或多个HTTP请求/HTTP响应交流,通常由HttpClient的内部来处理。用户提供一个请求对象,HttpClient发送请求到目标服务器,希望服务器返回一个相应的响应对象,或者抛出一个异常(执行失败)。一个简单的请求执行过程的示例:

HttpClient httpclient = new DefaultHttpClient();  
HttpGet httpget = new HttpGet("http://localhost/");  
HttpResponse response = httpclient.execute(httpget);  
HttpEntity entity = response.getEntity();  
if (entity != null) {  
    InputStream instream = entity.getContent();  
    int l;  
    byte[] tmp = new byte[2048];  
    while ((l = instream.read(tmp)) != -1) {  
    }  
} 

2、用工具封装Get请求

对比下面两段代码,发现用httpClient提供的方法更具可读性

HttpGet httpget = new HttpGet(  
"http://www.google.com/search?hl=en&q=httpclient&btnG=Google+Search&aq=f&oq="); 

HttpClient提供很多工具方法来简化创建和修改执行URI,例如:

List<NameValuePair> qparams = new ArrayList<NameValuePair>();  
qparams.add(new BasicNameValuePair("q", "httpclient"));  
qparams.add(new BasicNameValuePair("btnG", "Google Search"));  
qparams.add(new BasicNameValuePair("aq", "f"));  
qparams.add(new BasicNameValuePair("oq", null));  
URI uri = URIUtils.createURI("http", "www.google.com", -1, "/search",  
URLEncodedUtils.format(qparams, "UTF-8"), null);  
HttpGet httpget = new HttpGet(uri);

3、处理头部消息

一个HTTP消息可以包含一系列头部描述消息的属性。例如:内容长度、内容类型等。HttpClient提供方法检索、添加、删除、枚举头部信息。

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,HttpStatus.SC_OK, "OK");  
response.addHeader("Set-Cookie","c1=a; path=/; domain=localhost");  
response.addHeader("Set-Cookie","c2=b; path=/; domain=localhost");  
//获取header的方法1  
Header h1 = response.getFirstHeader("Set-Cookie");  
Header h2 = response.getLastHeader("Set-Cookie");  
//获取header的方法2  
HeaderIterator it = response.headerIterator("Set-Cookie");  
while (it.hasNext()) {  
    System.out.println(it.next());  
}  
//获取header的方法3  
Header[] headers = response.getAllHeaders();  
for (Header header : headers) {  
    System.out.println(header.getName() + " | " + header.getValue());  
}  

4、HTTP实体

HTTP规范定义了两种包装实体的方法:POST和PUT。响应通常附上内容实体。

HttpClient根据其内容出自何处区分三种类型的实体:

streamed(流式):内容从流中获得,或者在运行中产生。流式实体是不可重复生成的。

self-contained(自我包装式):内容在内存中或通过独立的连接或其它实体中获得。自我包装式的实体是可以重复生成和读取的。经常用于封装HTTP请求实体。(像ByteArrayEntity或StringEntity)。

wrapping(包裹式):内容从另外一个实体中获得。

如果从一个HTTP响应中获取流式内容,这个区别对于连接管理很重要。

如果是应用程序创建并用于发送的请求实体,这个类型区别就没那么重要了。

5、使用HTTP实体

使用实体常用的一些API,得到内容类型、内容长度、内容编码、内容转换成String或ByteArray、设置分块(HTTP1.1)。但是,EntityUtils的使用是不鼓励的,除非实体响应来自一个可信赖的HTTP服务器和已知的有限长度。

myEntity.getContentType()  
myEntity.getContentLength()  
EntityUtils.getContentCharSet(myEntity)  
EntityUtils.toString(myEntity)  
EntityUtils.toByteArray(myEntity)  
myEntity.setChunked(true); 

6、得到实体内容

HttpGet httpget = new HttpGet("http://localhost/");  
HttpResponse response = httpclient.execute(httpget);  
HttpEntity entity = response.getEntity();  
if (entity != null) {  
    long len = entity.getContentLength();  
    if (len != -1 && len < 2048) {  
        System.out.println(EntityUtils.toString(entity));  
    } else {  
        InputStream instream = entity.getContent();  
        int byteOne = instream.read();  
        int byteTwo = instream.read();  
        //终止请求,该连接将不被重用,保证底层的资源被正确释放  
        httpget.abort();  
    }  
} 

7、生成实体内容

HttpClient提供了常见的数据容器,比如字符串,字节数组,输入流和文件:StringEntity,ByteArrayEntity,InputStreamEntity和FileEntity。

File file = new File("somefile.txt");  
FileEntity entity = new FileEntity(file, "text/plain; charset=\"UTF-8\"");  
HttpPost httppost = new HttpPost("http://localhost/action.do");  
httppost.setEntity(entity);  

8、HTML表单(UrlEncodedFormEntity)

许多应用程序需要频繁模拟提交一个HTML表单的过程,比如,为了来记录一个Web应用程序或提交输出数据。HttpClient提供了特殊的实体类UrlEncodedFormEntity来这个满足过程。

List<NameValuePair> formparams = new ArrayList<NameValuePair>();  
formparams.add(new BasicNameValuePair("param1", "value1"));  
formparams.add(new BasicNameValuePair("param2", "value2"));  
//生成的实体内容:param1=value1¶m2=value2  
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, "UTF-8");  
HttpPost httppost = new HttpPost("http://localhost/handler.do");  
httppost.setEntity(entity); 

9、响应处理器(ResponseHandler)

ResponseHandler能够保证在任何情况下都会将底层的HTTP连接释放回连接管理器。用户不必担心HttpClient连接占用系统资源,可以把注意力集中在处理HTTP响应内容。

HttpClient httpclient = new DefaultHttpClient();  
HttpGet httpget = new HttpGet("http://localhost/");  
ResponseHandler<byte[]> handler = new ResponseHandler<byte[]>() {  
    public byte[] handleResponse(HttpResponse response) throws ClientProtocolException, IOException {  
        HttpEntity entity = response.getEntity();  
        if (entity != null) {  
            return EntityUtils.toByteArray(entity);  
        } else {  
            return null;  
        }  
    }  
};  
byte[] response = httpclient.execute(httpget, handler); 

HttpClient还提供了很多高级的特性,如:连接管理、状态管理、认证管理、客户服务等。这里只介绍了一些基础的用法,有时间好好研究一下。

二、HttpURLConnection的用法

1、创建HttpURLConnection对象

URL url = new URL("http://localhost:8080/TestHttpURLConnectionPro/index.jsp");   
URLConnection urlConnection = url.openConnection();  
HttpURLConnection httpUrlConnection = (HttpURLConnection) urlConnection; 

这个创建方法大家应该都很熟悉了,可能会认为下文中使用的HttpURLConnection的API的实现就在HttpURLConnection中了。这里要注意一下,其实这个HttpURLConnection是一个抽象类。也就是说里面很多API函数都不在这儿实现的,那在哪实现呢。翻开java源码,可以看到,URL.openConnection()的实现是在java.net.URL类中。

//java.net.URL类里面的openConnection方法:    
public URLConnection openConnection(Proxy proxy){    
   ...
   return handler.openConnection(this, proxy);   
} 

这里的handler又是什么呢,跟进去,发现Handler是sun.net.www.protocol.http.Handler这个java类,继承java.net.URLStreamHandler类,是用来处理http连接请求响应的。 继续跟踪代码

//Handler的方法  
protected java.net.URLConnection openConnection(URL u, Proxy p) throws IOException {    
    return new HttpURLConnection(u, p, this);  
}  

HttpURLConnection参数设置最终发现只是简单的生成了HttpURLConnection对象,其实最重要的HttpURLConnection就在这里了,这个是sun.net.www.protocl.http.HttpURLConnection类的对象,继承java.net.HttpURLConnection。也就是说我们之后所用的API实现都在sun.net.www.protocl.http.HttpURLConnection这个类里面。所以大家想要看HttpURLConnection的源码实现的话,需要到这个类中去查看。

好了,说了这么多,下面还是介绍HttpURLConnection常用的API的使用吧。

1、设置是否向httpUrlConnection输出,默认是false。使用httpUrlConnection.getOutputStream(),把内容输出到远程服务器上。

    httpUrlConnection.setDoOutput(true);  

2、设置是否从httpUrlConnection读入,默认是true。使用httpUrlConnection.getInputStream(),从远程服务器上得到响应的内容。

    httpUrlConnection.setDoInput(true);  

3、是否使用缓存。

    httpUrlConnection.setUseCaches(false);

4、设定传送的内容类型是可序列化的java对象 (如果不设此项,在传送序列化对象时,当WEB服务默认的不是这种类型时可能抛java.io.EOFException)。

    httpUrlConnection.setRequestProperty("Content-type", "application/x-java-serialized-object");

5、设定请求的方法为”POST”,默认是GET 。

    httpUrlConnection.setRequestMethod("POST");

2、HttpURLConnection连接

httpUrlConnection.connect();
//此处getOutputStream会隐含的进行connect,所以在开发中不调用上述的connect()也可以
OutputStream outStrm = httpUrlConnection.getOutputStream();

3、HttpURLConnection写数据与发送数据

// 现在通过输出流对象构建对象输出流对象,以实现输出可序列化的对象。   
ObjectOutputStream objOutputStrm = new ObjectOutputStream(outStrm);   
// 向对象输出流写出数据,这些数据将存到内存缓冲区中   
objOutputStrm.writeObject(new String("我是测试数据"));   
// 刷新对象输出流,将任何字节都写入潜在的流中(些处为ObjectOutputStream)   
objOutputStm.flush();   
// 关闭流对象。此时,不能再向对象输出流写入任何数据,先前写入的数据存在于内存缓冲区中,   
// 在调用下边的getInputStream()函数时才把准备好的http请求正式发送到服务器   
objOutputStm.close();   
// 调用HttpURLConnection连接对象的getInputStream()函数,   
// 将内存缓冲区中封装好的完整的HTTP请求电文发送到服务端。  
// <===注意,实际发送请求的代码段就在这里  
InputStream inStrm = httpConn.getInputStream(); 

4、HttpURLConnection注意事项

1. HttpURLConnection的connect()函数,实际上只是建立了一个与服务器的tcp连接,并没有实际发送http请求。无论是post还是get请求,http请求实际上直到HttpURLConnection的getInputStream()这个函数里面才正式发送出去。所以对outputStream的写操作,必须要在inputStream的读操作之前。 

2. 在用POST方式发送URL请求时,URL请求参数的设定顺序是重中之重,对connection对象的一切配置(那一堆set函数)都必须要在connect()函数执行之前完成。

三、HTTPClient 与 HttpURLConnection区别

HttpClient 是Apache公司提供的库,提供高效的、最新的、功能丰富的支持HTTP协议工具包,支持HTTP协议最新的版本和建议,是个很不错的开源框架,封装了http的请求,参数,内容体,响应等,拥有众多API。

HttpURLConnection 是Sun公司提供的库,也是Java的标准类库java.net中的一员,但这个类什么都没封装,用起来很原始,若需要高级功能,则会显得不太方便,比如重访问的自定义,会话和cookie等一些高级功能。

这两种方式都支持HTTPS协议,以流的形式进行上传和下载,配置超时时间,IPV6,连接池等功能。

1、功能用法对比

  • 从功能上对比,HttpClient库要丰富很多,提供了很多工具,封装了http的请求头,参数,内容体,响应,还有一些高级功能,代理、COOKIE、鉴权、压缩、连接池的处理。
  • HttpClient高级功能代码写起来比较复杂,对开发人员的要求会高一些,而HttpURLConnection对大部分工作进行了包装,屏蔽了不需要的细节,适合开发人员直接调用。
  • 另外,HttpURLConnection在2.3版本增加了一些HTTPS方面的改进,4.0版本增加一些响应的缓存。

2、性能对比

  • HttpUrlConnection直接支持GZIP压缩;HttpClient也支持,但要自己写代码处理。
  • HttpUrlConnection直接支持系统级连接池,即打开的连接不会直接关闭,在一段时间内所有程序可共用;HttpClient当然也能做到,但毕竟不如官方直接系统底层支持好。
  • HttpUrlConnection直接在系统层面做了缓存策略处理(4.0版本以上),加快了重复请求的速度。
  • 这篇文章对两者的速度做了一个对比,做法是两个类都使用默认的方法去请求百度的网页内容,测试结果是使用httpurlconnection耗时47ms,使用httpclient耗时641ms。httpURLConnection在速度有比较明显的优势,当然这跟压缩内容和缓存都有直接关系。

3、未来发展

  • HttpClient 适用于 web browsers, 他们是可扩展的,并且拥有大量的稳定APIs。但是,在不破坏其兼容性的前提下很难对如此多的APIs做修改。因此,Android 团队对修改优化Apache HTTP Client表现的并不积极。
  • HttpURLConnect 是一个通用的、适合大多数应用的轻量级组件。这个类起步比较晚,很容易在主要API上做稳步的改善。但是HttpURLConnection在在Android 2.2及以下版本上存在一些令人厌烦的bug,尤其是在读取 InputStream时调用 close()方法,就有可能会导致连接池失效了。
  • Android团队未来的工作会将更多的时间放在优化HttpURLConnection上,它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。

4、选用建议

  • 如果一个Android应用需要向指定页面发送请求,但该页面并不是一个简单的页面,只有当用户已经登录,而且登录用户的用户名有效时才可访问该页面。如果使用HttpURLConnection来访问这个被保护的页面,那么需要处理的细节就太复杂了。这种情况建议使用HttpClient。
  • Android2.3及以上版本建议选用HttpURLConnection,2.2及以下版本建议选用HttpClient。新的应用都建议使用HttpURLConnection。

原文链接:

http://www.cnblogs.com/liushuibufu/p/4140914.html

http://www.cnblogs.com/liushuibufu/p/4140913.html

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部