HttpClient,DefaultHttpClient使用详解

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

HttpClient,DefaultHttpClient使⽤详解
HttpClient:是⼀个接⼝
⾸先需要先创建⼀个DefaultHttpClient的实例
HttpClient httpClient=new DefaultHttpClient();
发送GET请求:
先创建⼀个HttpGet对象,传⼊⽬标的⽹络地址,然后调⽤HttpClient的execute()⽅法即可:
httpClient.execute(httpGet);
发送POST请求:
创建⼀个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. 释放连接。

⽆论执⾏⽅法是否成功,都必须释放连接
四、实例
1package com.fico.rma.service;
2
3import java.io.File;
4import java.io.FileInputStream;
5import java.io.IOException;
6import .URL;
7import mons.io.FileUtils;
8import org.apache.http.HttpEntity;
9import org.apache.http.HttpResponse;
10import org.apache.http.HttpStatus;
11import org.apache.http.client.methods.HttpPost;
12import org.apache.http.entity.InputStreamEntity;
13import org.apache.http.impl.client.DefaultHttpClient;
14import com.fico.rma.RmaConfig;
15import com.fico.rma.util.RmaConstants;
16
17public class ReleaseService {
18
19private final static String SEP=File.separator;
20
21public static void createRelease(String releaseName,String projectName) throws IOException{
22 String dir=RmaConfig.getString("basepath");
23 projectName = projectName.toLowerCase();
24 File destAdbFile=new File(dir+SEP+RmaConstants.RELEASE_DIR+SEP+releaseName+SEP+projectName+".adb");
25 File srcAdbFile = null;
26if("dldsunproject".equals(projectName.toLowerCase().toString().trim())){
27 srcAdbFile=new File(dir+SEP+RmaConstants.ADB_DIR+SEP+"DldsUnProject"+SEP+"adb"+SEP+"Pingan.server_service_0.adb");
28 }else {
29 srcAdbFile=new File(dir+SEP+RmaConstants.ADB_DIR+SEP+"PingAnProject"+SEP+"adb"+SEP+"Pingan.server_service_0.adb");
30 }
31 FileUtils.copyFile(srcAdbFile, destAdbFile);
32 }
33
34public static void pulishRelease(String adbFile,String url,String projectName) throws IOException {
35
36 DefaultHttpClient httpclient = new DefaultHttpClient();
37
38 File file = new File(adbFile);
39 InputStreamEntity reqEntity = new InputStreamEntity(new FileInputStream(file), -1);
40 reqEntity.setContentType("binary/octet-stream");
41 reqEntity.setChunked(true);
42
43 HttpPost httppost = new HttpPost(url);
44 httppost.setEntity(reqEntity);
45 httppost.setHeader("projectName", projectName);
46
47 HttpResponse response = httpclient.execute(httppost);
48//System.out.println("response.getStatusLine().getStatusCode():"+response.getStatusLine().getStatusCode());
49
50if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {
51 HttpEntity enty = response.getEntity();
52 }
53 }
54
55
56 }
以上是发送端的代码,下⾯是接受端的代码:
1package com.pingan.res;
2
3import java.io.File;
4import java.io.FileOutputStream;
5import java.io.IOException;
6import java.io.InputStream;
7import java.io.OutputStream;
8import java.io.PrintWriter;
9
10import javax.servlet.ServletContext;
11import javax.servlet.ServletException;
12import javax.servlet.annotation.WebServlet;
13import javax.servlet.http.HttpServlet;
14import javax.servlet.http.HttpServletRequest;
15import javax.servlet.http.HttpServletResponse;
16import com.blazesoft.server.local.NdLocalServerException;
17
18/**
19 * Servlet implementation class ReleaseServlet
20*/
21 @WebServlet("/Release")
22public class ReleaseServlet extends HttpServlet {
23private static final long serialVersionUID = 1L;
24
25/**
26 * @see HttpServlet#HttpServlet()
27*/
28public ReleaseServlet() {
29super();
30// TODO Auto-generated constructor stub
31 }
32
33/**
34 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
35 * response)
36*/
37protected void doPost(HttpServletRequest request,
38 HttpServletResponse response) throws ServletException, IOException {
39
40/*
41 * 功能点:RMA这发布adb包的时候的res调⽤的⼊⼝点。

42 * 功能1:获取到RMA中具体是哪个项⽬
43 * 功能2:根据项⽬把adb包和对应的.server⽂件拷贝到不同的⽬录下(⽬录名称为RMA的项⽬名称的⼩写)
44 * 功能3:重新加载相对应的adb⽂件到对应的server中
45*/
46
47//功能1
48 String projectName ="";
49 projectName =request.getHeader("projectName");
50 projectName = projectName.toLowerCase();
51 String sep=File.separator;
52// String dir=ResConfig.getString("basepath");
53 String dir = "";
54 dir=ResConfig.getProperties().getProperty("basepath");
55
56//功能2
57 String adbFile=dir+sep+"production"+sep+projectName+sep+projectName+".adb";
58 String serverFile = dir+sep+"production"+sep+projectName+sep+projectName+".server";
59 OutputStream out = new FileOutputStream(adbFile);
60
61 InputStream in = request.getInputStream();
62
63int length = 0;
64byte[] buf = new byte[1024];
65
66while ((length = in.read(buf)) != -1) {
67 out.write(buf, 0, length);
68 }
69
70 in.close();
71 out.close();
72
73//功能3:重启server
74 reloadRuleServer(request,projectName,serverFile);
75
76
77 response.setContentType("text/xml");
78 PrintWriter pw=response.getWriter();
79
80 pw.write("<Response><Result>OK</Result></Response>");
81
82 pw.flush();
83
84 }
85
86private void reloadRuleServer(HttpServletRequest req,String projectName,String serverFile) throws ServletException{
87 projectName = projectName.toLowerCase();
88 String projectDefinition1 = "";
89if (!"".equals(projectName)&&projectName.length()>0) {
90 projectDefinition1 = ResConfig.getProperties().getProperty(projectName+"_df");
91 }
92
93 Server server = null;
94 ServletContext context=req.getServletContext();
95 server = (Server)context.getAttribute(projectName+"Server");
96
97try{
98if(server==null){
99 server = (Server)Server.createServer(serverFile);
100 context.setAttribute(projectName+"Server", server);
101 }
102 server.resetService(projectDefinition1);
103 PriorRunner runner=new PriorRunner(server,100);
104 runner.run();
105
106
107 } catch (NdLocalServerException e) {
108 e.printStackTrace();
109throw new ServletException(e.getMessage());
110 }
111
112 }
113
114 }
HttpClient 是 Apache Jakarta Common 下的⼦项⽬,可以⽤来提供⾼效的、最新的、功能丰富的⽀持 HTTP 协议的客户端编程⼯具包,并且它⽀持 HTTP 协议最新的版本和建议。

本⽂⾸先介绍 HTTPClient,然后根据作者实际⼯作经验给出了⼀些常见问题的解决⽅法。

HTTP 协议可能是现在 Internet 上使⽤得最多、最重要的协议了,越来越多的 Java 应⽤程序需要直接通过
HTTP 协议来访问⽹络资源。

虽然在 JDK 的 包中已经提供了访问 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());
7 }
在返回的状态码正确后,即可取得内容。

取得⽬标地址的内容有三种⽅法:第⼀种,getResponseBody,该⽅法返回的是⽬标的⼆进制的byte流;第⼆种,getResponseBodyAsString,这个⽅法返回的是String类型,值得注意的是该⽅法返回的String的编码是根据系统默认的编码⽅式,所以返回的String值可能编码类型有误,在本⽂的"字符编码"部分中将对此做详细介绍;第三种,getResponseBodyAsStream,这个⽅法对于⽬标地址中有⼤量数据需要传输是最佳的。

在这⾥我们使⽤了最简单的getResponseBody⽅法。

byte[] responseBody = method.getResponseBody();
释放连接。

⽆论执⾏⽅法是否成功,都必须释放连接。

method.releaseConnection();
处理内容。

在这⼀步中根据你的需要处理内容,在例⼦中只是简单的将内容打印到控制台。

System.out.println(new
String(responseBody));
下⾯是程序的完整代码:
1package test;
2import java.io.IOException;
3import mons.httpclient.*;
4import mons.httpclient.methods.GetMethod;
5import mons.httpclient.params.HttpMethodParams;
6public class GetSample{
7public static void main(String[] args) {
8//构造HttpClient的实例
9 HttpClient httpClient = new HttpClient();
10//创建GET⽅法的实例
11 GetMethod getMethod = new GetMethod("...");
12//使⽤系统提供的默认的恢复策略
13 getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
14new DefaultHttpMethodRetryHandler());
15try {
16//执⾏getMethod
17int statusCode = httpClient.executeMethod(getMethod);
18if (statusCode != HttpStatus.SC_OK) {
19 System.err.println("Method failed: "
20 + getMethod.getStatusLine());
21 }
22//读取内容
23byte[] responseBody = getMethod.getResponseBody();
24//处理内容
25 System.out.println(new String(responseBody));
26 } catch (HttpException e) {
27//发⽣致命的异常,可能是协议不对或者返回的内容有问题
28 System.out.println("Please check your provided http address!");
29 e.printStackTrace();
30 } catch (IOException e) {
31//发⽣⽹络异常
32 e.printStackTrace();
33 } finally {
34//释放连接
35 getMethod.releaseConnection();
36 }
37 }
38 }
(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 = "....";
2 PostMethod postMethod = new PostMethod(url);
3// 填⼊各个表单域的值
4 NameValuePair[] data = { new NameValuePair("id", "youUserName"),
5new NameValuePair("passwd", "yourPwd") };
6// 将表单的值放⼊postMethod中
7 postMethod.setRequestBody(data);
8// 执⾏postMethod
9int statusCode = httpClient.executeMethod(postMethod);
10// HttpClient对于要求接受后继服务的请求,象POST和PUT等不能⾃动处理转发
11// 301或者302
12if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
13 statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
14// 从头中取出转向的地址
15 Header locationHeader = postMethod.getResponseHeader("location");
16 String location = null;
17if (locationHeader != null) {
18 location = locationHeader.getValue();
19 System.out.println("The page was redirected to:" + location);
20 } else {
21 System.err.println("Location field value is null.");
22 }
23return;
 4 使⽤HttpClient过程中常见的⼀些问题
下⾯介绍在使⽤HttpClient过程中常见的⼀些问题。

字符编码
某⽬标页的编码可能出现在两个地⽅,第⼀个地⽅是服务器返回的http头中,另外⼀个地⽅是得到的html/xml页⾯中。

在http头的Content-Type字段可能会包含字符编码信息。

例如可能返回的头会包含这样⼦的信息:Content-Type: text/html;
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/html; 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. 抛出异常.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. 抛出异常.SocketException: SSL implementation not available。

出现这个异常可能是你没有把ibmjsse.jar拷贝到<java-home>\lib\ext\⽬录下。

3. 抛出异常.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,想想当你⽤.HttpURLConnection来完成这些功能时是多么恐怖的事情啊!况且这仅仅是我们所说的顽固的WEB服务器中的⼀个很常见的“顽固”!再有如通过HTTP来上传⽂件呢?不需要头疼,这些问题有了“它”就很容易解决了!
我们不可能列举所有可能的顽固,我们会针对⼏种最常见的问题进⾏处理。

当然了,正如前⾯说到的,如果我们⾃⼰使⽤
.HttpURLConnection来搞定这些问题是很恐怖的事情,因此在开始之前我们先要介绍⼀下⼀个开放源码的项⽬,这个项⽬就是Apache开源组织中的httpclient,它⾪属于Jakarta的commons项⽬,⽬前的版本是2.0RC2。

commons下本来已经有⼀个net的⼦项⽬,但是⼜把httpclient单独提出来,可见http服务器的访问绝⾮易事。

Commons-httpclient项⽬就是专门设计来简化HTTP客户端与服务器进⾏各种通讯编程。

通过它可以让原来很头疼的事情现在轻松的解决,例如你不再管是HTTP或者HTTPS的通讯⽅式,告诉它你想使⽤HTTPS⽅式,剩下的事情交给httpclient替你完成。

本⽂会针对我们在编写HTTP客户端程序时经常碰到的⼏个问题进⾏分别介绍如何使⽤httpclient来解决它们,为了让读者更快的熟悉这个项⽬我们最开始先给出⼀个简单的例⼦来读取⼀个⽹页的内容,然后循序渐进解决掉前进中的所有问题。

相关文档
最新文档