一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

HttpClient 簡介

 pengx 2009-09-16
一、HttpClient入門
      HttpClient 是 Apache Jakarta Common 下的子項目,可以用來提供高效的、最新的、功能豐富的支持 HTTP 協(xié)議的客戶端編程工具包,并且它支持 HTTP 協(xié)議最新的版本和建議。本文首先介紹 HTTPClient,然后根據(jù)作者實(shí)際工作經(jīng)驗給出了一些常見問題的解決方法。
HttpClient 基本功能的使用
(一)、GET 方法
使用 HttpClient 需要以下 6 個步驟: 
      1. 創(chuàng)建 HttpClient 的實(shí)例 
      2. 創(chuàng)建某種連接方法的實(shí)例,在這里是 GetMethod。在 GetMethod 的構(gòu)造函數(shù)中傳入待連接的地址 
      3. 調(diào)用第一步中創(chuàng)建好的實(shí)例的 execute 方法來執(zhí)行第二步中創(chuàng)建好的 method 實(shí)例 
      4. 讀 response 
      5. 釋放連接。無論執(zhí)行方法是否成功,都必須釋放連接 
      6. 對得到后的內(nèi)容進(jìn)行處理
根據(jù)以上步驟,我們來編寫用GET方法來取得某網(wǎng)頁內(nèi)容的代碼。 
      • 大部分情況下 HttpClient 默認(rèn)的構(gòu)造函數(shù)已經(jīng)足夠使用。 
            HttpClient httpClient = new HttpClient(); 
      • 創(chuàng)建GET方法的實(shí)例。在GET方法的構(gòu)造函數(shù)中傳入待連接的地址即可。用GetMethod將會自動處理轉(zhuǎn)發(fā)過程,如果想要把自動處理轉(zhuǎn)發(fā)過程去掉的話,可以調(diào)用方法setFollowRedirects(false)。 
            GetMethod getMethod = new GetMethod("http://www.ibm.com/"); 
      • 調(diào)用實(shí)例httpClient的executeMethod方法來執(zhí)行g(shù)etMethod。由于是執(zhí)行在網(wǎng)絡(luò)上的程序,在運(yùn)行executeMethod方法的時候,需要處理兩個異常,分別是HttpException和IOException。引起第一種異常的原因主要可能是在構(gòu)造getMethod的時候傳入的協(xié)議不對,比如不小心將"http"寫成"htp",或者服務(wù)器端返回的內(nèi)容不正常等,并且該異常發(fā)生是不可恢復(fù)的;第二種異常一般是由于網(wǎng)絡(luò)原因引起的異常,對于這種異常 (IOException),HttpClient會根據(jù)你指定的恢復(fù)策略自動試著重新執(zhí)行executeMethod方法。HttpClient的恢復(fù)策略可以自定義(通過實(shí)現(xiàn)接口HttpMethodRetryHandler來實(shí)現(xiàn))。通過httpClient的方法setParameter設(shè)置你實(shí)現(xiàn)的恢復(fù)策略,本文中使用的是系統(tǒng)提供的默認(rèn)恢復(fù)策略,該策略在碰到第二類異常的時候?qū)⒆詣又卦?次。executeMethod返回值是一個整數(shù),表示了執(zhí)行該方法后服務(wù)器返回的狀態(tài)碼,該狀態(tài)碼能表示出該方法執(zhí)行是否成功、需要認(rèn)證或者頁面發(fā)生了跳轉(zhuǎn)(默認(rèn)狀態(tài)下GetMethod的實(shí)例是自動處理跳轉(zhuǎn)的)等。
//設(shè)置成了默認(rèn)的恢復(fù)策略,在發(fā)生異常時候?qū)⒆詣又卦?次,在這里你也可以設(shè)置成自定義的恢復(fù)策略
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
    new DefaultHttpMethodRetryHandler());
//執(zhí)行g(shù)etMethod
int statusCode = client.executeMethod(getMethod);
if (statusCode != HttpStatus.SC_OK) {
  System.err.println("Method failed: " + getMethod.getStatusLine());

      • 在返回的狀態(tài)碼正確后,即可取得內(nèi)容。取得目標(biāo)地址的內(nèi)容有三種方法:第一種,getResponseBody,該方法返回的是目標(biāo)的二進(jìn)制的byte流;第二種,getResponseBodyAsString,這個方法返回的是String類型,值得注意的是該方法返回的String的編碼是根據(jù)系統(tǒng)默認(rèn)的編碼方式,所以返回的String值可能編碼類型有誤,在本文的"字符編碼"部分中將對此做詳細(xì)介紹;第三種,getResponseBodyAsStream,這個方法對于目標(biāo)地址中有大量數(shù)據(jù)需要傳輸是最佳的。在這里我們使用了最簡單的getResponseBody方法。
byte[] responseBody = method.getResponseBody(); 
      • 釋放連接。無論執(zhí)行方法是否成功,都必須釋放連接。
method.releaseConnection(); 
      • 處理內(nèi)容。在這一步中根據(jù)你的需要處理內(nèi)容,在例子中只是簡單的將內(nèi)容打印到控制臺。
System.out.println(new String(responseBody));
下面是程序的完整代碼,這些代碼也可在附件中的test.GetSample中找到。
package test;
import java.io.IOException;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
public class GetSample{
  public static void main(String[] args) {
  //構(gòu)造HttpClient的實(shí)例
  HttpClient httpClient = new HttpClient();
  //創(chuàng)建GET方法的實(shí)例
  GetMethod getMethod = new GetMethod("http://www.ibm.com");
  //使用系統(tǒng)提供的默認(rèn)的恢復(fù)策略
  getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
    new DefaultHttpMethodRetryHandler());
  try {
   //執(zhí)行g(shù)etMethod
   int statusCode = httpClient.executeMethod(getMethod);
   if (statusCode != HttpStatus.SC_OK) {
    System.err.println("Method failed: "
      + getMethod.getStatusLine());
   }
   //讀取內(nèi)容
   byte[] responseBody = getMethod.getResponseBody();
   //處理內(nèi)容
   System.out.println(new String(responseBody));
  } catch (HttpException e) {
   //發(fā)生致命的異常,可能是協(xié)議不對或者返回的內(nèi)容有問題
   System.out.println("Please check your provided http address!");
   e.printStackTrace();
  } catch (IOException e) {
   //發(fā)生網(wǎng)絡(luò)異常
   e.printStackTrace();
  } finally {
   //釋放連接
   getMethod.releaseConnection();
  }
}
}

(二)、POST方法 
      根據(jù)RFC2616,對POST的解釋如下:POST方法用來向目的服務(wù)器發(fā)出請求,要求它接受被附在請求后的實(shí)體,并把它當(dāng)作請求隊列(Request-Line)中請求URI所指定資源的附加新子項。POST被設(shè)計成用統(tǒng)一的方法實(shí)現(xiàn)下列功能: 
      • 對現(xiàn)有資源的注釋(Annotation of existing resources) 
      • 向電子公告欄、新聞組,郵件列表或類似討論組發(fā)送消息 
      • 提交數(shù)據(jù)塊,如將表單的結(jié)果提交給數(shù)據(jù)處理過程 
      • 通過附加操作來擴(kuò)展數(shù)據(jù)庫
調(diào)用HttpClient中的PostMethod與GetMethod類似,除了設(shè)置PostMethod的實(shí)例與GetMethod有些不同之外,剩下的步驟都差不多。在下面的例子中,省去了與GetMethod相同的步驟,只說明與上面不同的地方,并以登錄清華大學(xué)BBS為例子進(jìn)行說明。 
      • 構(gòu)造PostMethod之前的步驟都相同,與GetMethod一樣,構(gòu)造PostMethod也需要一個URI參數(shù),在本例中,登錄的地址是http://www./bbslogin2.php。在創(chuàng)建了PostMethod的實(shí)例之后,需要給method實(shí)例填充表單的值,在BBS的登錄表單中需要有兩個域,第一個是用戶名(域名叫id),第二個是密碼(域名叫passwd)。表單中的域用類NameValuePair來表示,該類的構(gòu)造函數(shù)第一個參數(shù)是域名,第二參數(shù)是該域的值;將表單所有的值設(shè)置到PostMethod中用方法setRequestBody。另外由于BBS登錄成功后會轉(zhuǎn)向另外一個頁面,但是HttpClient對于要求接受后繼服務(wù)的請求,比如POST和PUT,不支持自動轉(zhuǎn)發(fā),因此需要自己對頁面轉(zhuǎn)向做處理。具體的頁面轉(zhuǎn)向處理請參見下面的"自動轉(zhuǎn)向"部分。代碼如下:
String url = "http://www./bbslogin2.php";
PostMethod postMethod = new PostMethod(url);
// 填入各個表單域的值
NameValuePair[] data = { new NameValuePair("id", "youUserName"),
new NameValuePair("passwd", "yourPwd") };
// 將表單的值放入postMethod中
postMethod.setRequestBody(data);
// 執(zhí)行postMethod
int statusCode = httpClient.executeMethod(postMethod);
// HttpClient對于要求接受后繼服務(wù)的請求,象POST和PUT等不能自動處理轉(zhuǎn)發(fā)
// 301或者302
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
    // 從頭中取出轉(zhuǎn)向的地址
    Header locationHeader = postMethod.getResponseHeader("location");
    String location = null;
    if (locationHeader != null) {
     location = locationHeader.getValue();
     System.out.println("The page was redirected to:" + location);
    } else {
     System.err.println("Location field value is null.");
    }
    return;
}
 
二、HttpClient一些問題
下面介紹在使用HttpClient過程中常見的一些問題。
(一)、字符編碼 
      某目標(biāo)頁的編碼可能出現(xiàn)在兩個地方,第一個地方是服務(wù)器返回的http頭中,另外一個地方是得到的html/xml頁面中。 
      • 在http頭的Content-Type字段可能會包含字符編碼信息。例如可能返回的頭會包含這樣子的信息:Content-Type: text/html; charset=UTF-8。這個頭信息表明該頁的編碼是UTF-8,但是服務(wù)器返回的頭信息未必與內(nèi)容能匹配上。比如對于一些雙字節(jié)語言國家,可能服務(wù)器返回的編碼類型是UTF-8,但真正的內(nèi)容卻不是UTF-8編碼的,因此需要在另外的地方去得到頁面的編碼信息;但是如果服務(wù)器返回的編碼不是UTF-8,而是具體的一些編碼,比如gb2312等,那服務(wù)器返回的可能是正確的編碼信息。通過method對象的getResponseCharSet()方法就可以得到http頭中的編碼信息。 
      • 對于象xml或者h(yuǎn)tml這樣的文件,允許作者在頁面中直接指定編碼類型。比如在html中會有<meta http-equiv="Content-Type" content="text/html; charset=gb2312"/>這樣的標(biāo)簽;或者在xml中會有<?xml version="1.0" encoding="gb2312"?>這樣的標(biāo)簽,在這些情況下,可能與http頭中返回的編碼信息沖突,需要用戶自己判斷到底那種編碼類型應(yīng)該是真正的編碼。
編碼問題解決
private static final String CONTENT_CHARSET = "GBK";// httpclient讀取內(nèi)容時使用的字符集  
HttpClient client = new HttpClient();  
client.getParams().setParameter(  
HttpMethodParams.HTTP_CONTENT_CHARSET, CONTENT_CHARSET);
字符串編碼改變的方法:String target = new String(orig.getBytes("ISO-8859-1"),"GBK");

(二)、認(rèn)證
HttpClient三種不同的認(rèn)證方案: Basic, Digest and NTLM. 這些方案可用于服務(wù)器或代理對客戶端的認(rèn)證,簡稱服務(wù)器認(rèn)證或代理認(rèn)證。
1) 服務(wù)器認(rèn)證(Server Authentication)
HttpClient處理服務(wù)器認(rèn)證幾乎是透明的,僅需要開發(fā)人員提供登錄信息(login credentials)。登錄信息保存在HttpState類的實(shí)例中,可以通過 setCredentials(String realm, Credentials cred)和getCredentials(String realm)來獲取或設(shè)置。注意,設(shè)定對非特定站點(diǎn)訪問所需要的登錄信息,將realm參數(shù)置為null. HttpClient內(nèi)建的自動認(rèn)證,可以通過HttpMethod類的setDoAuthentication(boolean doAuthentication)方法關(guān)閉,而且這次關(guān)閉只影響HttpMethod當(dāng)前的實(shí)例。
搶先認(rèn)證(Preemptive Authentication)可以通過下述方法打開.
client.getState().setAuthenticationPreemptive(true);
在這種模式時,HttpClient會主動將basic認(rèn)證應(yīng)答信息傳給服務(wù)器,即使在某種情況下服務(wù)器可能返回認(rèn)證失敗的應(yīng)答,這樣做主要是為了減少連接的建立。為使每個新建的 HttpState實(shí)例都實(shí)行搶先認(rèn)證,可以如下設(shè)置系統(tǒng)屬性。
setSystemProperty(Authenticator.PREEMPTIVE_PROPERTY, "true");
Httpclient實(shí)現(xiàn)的搶先認(rèn)證遵循rfc2617.
2)代理認(rèn)證(proxy authentication)
除了登錄信息需單獨(dú)存放以外,代理認(rèn)證與服務(wù)器認(rèn)證幾乎一致。用 setProxyCredentials(String realm, Credentials cred)和 getProxyCredentials(String realm)設(shè)、取登錄信息。
3) 認(rèn)證方案(authentication schemes)
Basic
是HTTP中規(guī)定最早的也是最兼容(?)的方案,遺憾的是也是最不安全的一個方案,因為它以明碼傳送用戶名和密碼。它要求一個UsernamePasswordCredentials實(shí)例,可以指定服務(wù)器端的訪問空間或采用默認(rèn)的登錄信息。
Digest
是在HTTP1.1中增加的一個方案,雖然不如Basic得到的軟件支持多,但還是有廣泛的使用。Digest方案比Basic方案安全得多,因它根本就不通過網(wǎng)絡(luò)傳送實(shí)際的密碼,傳送的是利用這個密碼對從服務(wù)器傳來的一個隨機(jī)數(shù)(nonce)的加密串。它要求一個UsernamePasswordCredentials實(shí)例,可以指定服務(wù)器端的訪問空間或采用默認(rèn)的登錄信息。
NTLM
這是HttpClient支持的最復(fù)雜的認(rèn)證協(xié)議。它M$設(shè)計的一個私有協(xié)議,沒有公開的規(guī)范說明。一開始由于設(shè)計的缺陷,NTLM的安全性比Digest差,后來經(jīng)過一個ServicePack補(bǔ)丁后,安全性則比較Digest高。NTLM需要一個NTCredentials實(shí)例. 注意,由于NTLM不使用訪問空間(realms)的概念,HttpClient利用服務(wù)器的域名作訪問空間的名字。還需要注意,提供給NTCredentials的用戶名,不要用域名的前綴 - 如: "adrian" 是正確的,而 "DOMAIN\adrian" 則是錯的. NTLM認(rèn)證的工作機(jī)制與basic和digest有很大的差別。這些差別一般由HttpClient處理,但理解這些差別有助避免在使用NTLM認(rèn)證時出現(xiàn)錯誤。
從HttpClientAPI的角度來看,NTLM與其它認(rèn)證方式一樣的工作,差別是需要提供'NTCredentials'實(shí)例而不是'UsernamePasswordCredentials'(其實(shí),前者只是擴(kuò)展了后者)對NTLM認(rèn)證,訪問空間是連接到的機(jī)器的域名,這對多域名主機(jī)會有一些麻煩.只有HttpClient連接中指定的域名才是認(rèn)證用的域名。建議將realm設(shè)為null以使用默認(rèn)的設(shè)置。NTLM只是認(rèn)證了一個連接而不是一請求,所以每當(dāng)一個新的連接建立就要進(jìn)行一次認(rèn)證,且在認(rèn)證的過程中保持連接是非常重要的。 因此,NTLM不能同時用于代理認(rèn)證和服務(wù)器認(rèn)證,也不能用于http1.0連接或服務(wù)器不支持持久連接的情況。
(三)、重定向
由于技術(shù)限制,以及為保證2.0發(fā)布版API的穩(wěn)定,HttpClient還不能自動處重定向,但對重定向到同一主機(jī)、同一端口且采用同一協(xié)議的情況HttpClient可以支持。不能自動的處理的情況,包括需要人工交互的情況,或超出httpclient的能力。
當(dāng)服務(wù)器重定向指令指到不同的主機(jī)時,HttpClient只是簡單地將重定向狀態(tài)碼作為應(yīng)答狀態(tài)。所有的300到399(包含兩端)的返回碼,都表示是重定向應(yīng)答。常見的有:
301 永久移動. HttpStatus.SC_MOVED_PERMANENTLY
302 臨時移動. HttpStatus.SC_MOVED_TEMPORARILY
303 See Other. HttpStatus.SC_SEE_OTHER
307 臨時重定向. HttpStatus.SC_TEMPORARY_REDIRECT  
當(dāng)收到簡單的重定向時,程序應(yīng)從HttpMethod對象中抽取新的URL并將其下載。另外,限制一下重定向次數(shù)是個好的主意,這可以避免遞歸循環(huán)。新的URL可以從頭字段Location中抽取,如下:
String redirectLocation;
Header locationHeader = method.getResponseHeader("location");
if (locationHeader != null) {
redirectLocation = locationHeader.getValue();
} else {
// The response is invalid and did not provide the new location for
// the resource. Report an error or possibly handle the response
// like a 404 Not Found error.
}
特殊重定向:
300 多重選擇. HttpStatus.SC_MULTIPLE_CHOICES
304 沒有改動. HttpStatus.SC_NO T_MODIFIED
305 使用代理. HttpStatus.SC_USE_PROXY
 
(四)、自動轉(zhuǎn)向
根據(jù)RFC2616中對自動轉(zhuǎn)向的定義,主要有兩種:301和302。301表示永久的移走(Moved Permanently),當(dāng)返回的是301,則表示請求的資源已經(jīng)被移到一個固定的新地方,任何向該地址發(fā)起請求都會被轉(zhuǎn)到新的地址上。302表示暫時的轉(zhuǎn)向,比如在服務(wù)器端的servlet程序調(diào)用了sendRedirect方法,則在客戶端就會得到一個302的代碼,這時服務(wù)器返回的頭信息中l(wèi)ocation的值就是sendRedirect轉(zhuǎn)向的目標(biāo)地址。
HttpClient支持自動轉(zhuǎn)向處理,但是象POST和PUT方式這種要求接受后繼服務(wù)的請求方式,暫時不支持自動轉(zhuǎn)向,因此如果碰到POST方式提交后返回的是301或者302的話需要自己處理。就像剛才在POSTMethod中舉的例子:如果想進(jìn)入登錄BBS后的頁面,必須重新發(fā)起登錄的請求,請求的地址可以在頭字段location中得到。不過需要注意的是,有時候location返回的可能是相對路徑,因此需要對location返回的值做一些處理才可以發(fā)起向新地址的請求。
另外除了在頭中包含的信息可能使頁面發(fā)生重定向外,在頁面中也有可能會發(fā)生頁面的重定向。引起頁面自動轉(zhuǎn)發(fā)的標(biāo)簽是:<meta http-equiv="refresh" content="5; url=http://www.ibm.com/us">。如果你想在程序中也處理這種情況的話得自己分析頁面來實(shí)現(xiàn)轉(zhuǎn)向。需要注意的是,在上面那個標(biāo)簽中url的值也可以是一個相對地址,如果是這樣的話,需要對它做一些處理后才可以轉(zhuǎn)發(fā)。
 
(五)、Cookies
   HttpClient能自動管理cookie,包括允許服務(wù)器設(shè)置cookie并在需要的時候自動將cookie返回服務(wù)器,它也支持手工設(shè)置cookie后發(fā)送到服務(wù)器端。不幸的是,對如何處理cookie,有幾個規(guī)范互相沖突:Netscape Cookie 草案, RFC2109, RFC2965,而且還有很大數(shù)量的軟件商的cookie實(shí)現(xiàn)不遵循任何規(guī)范. 為了處理這種狀況,HttpClient提供了策略驅(qū)動的cookie管理方式。HttpClient支持的cookie規(guī)范有: Netscape cookie草案,是最早的cookie規(guī)范,基于rfc2109。盡管這個規(guī)范與rc2109有較大的差別,這樣做可以與一些服務(wù)器兼容。rfc2109,是w3c發(fā)布的第一個官方cookie規(guī)范。理論上講,所有的服務(wù)器在處理cookie(版本1)時,都要遵循此規(guī)范,正因如此,HttpClient將其設(shè)為默認(rèn)的規(guī)范。遺憾的是,這個規(guī)范太嚴(yán)格了,以致很多服務(wù)器不正確的實(shí)施了該規(guī)范或仍在作用Netscape規(guī)范。在這種情況下,應(yīng)使用兼容規(guī)范。兼容性規(guī)范,設(shè)計用來兼容盡可能多的服務(wù)器,即使它們并沒有遵循標(biāo)準(zhǔn)規(guī)范。當(dāng)解析cookie出現(xiàn)問題時,應(yīng)考慮采用兼容性規(guī)范。
   RFC2965規(guī)范暫時沒有被HttpClient支持(在以后的版本為會加上),它定義了cookie版本2,并說明了版本1cookie的不足,RFC2965有意有久取代rfc2109.
在HttpClient中,有兩種方法來指定cookie規(guī)范的使用,
HttpClient client = new HttpClient();
client.getState().setCookiePolicy(CookiePolicy.COMPATIBILITY);
這種方法設(shè)置的規(guī)范只對當(dāng)前的HttpState有效,參數(shù)可取值CookiePolicy.COMPATIBILITY,CookiePolicy.NETSCAPE_DRAFT或CookiePolicy.RFC2109。
System.setProperty("apache.commons.httpclient.cookiespec", "COMPATIBILITY");
此法指的規(guī)范,對以后每個新建立的HttpState對象都有效,參數(shù)可取值"COMPATIBILITY","NETSCAPE_DRAFT"或"RFC2109"。
常有不能解析cookie的問題,但更換到兼容規(guī)范大都能解決。

(六)、使用HttpClient遇到問題怎么辦?
用一個瀏覽器訪問服務(wù)器,以確認(rèn)服務(wù)器應(yīng)答正常如果在使代理,關(guān)掉代理試試另找一個服務(wù)器來試試(如果運(yùn)行著不同的服務(wù)器軟件更好)檢查代碼是否按教程中講的思路編寫設(shè)置log級別為debug,找出問題出現(xiàn)的原因打開wiretrace,來追蹤客戶端與服務(wù)器的通信,以確實(shí)問題出現(xiàn)在什么地方用telnet或netcat手工將信息發(fā)送到服務(wù)器,適合于猜測已經(jīng)找到了原因而進(jìn)行試驗時將netcat以監(jiān)聽方式運(yùn)行,用作服務(wù)器以檢查httpclient如何處理應(yīng)答的。利用最新的httpclient試試,bug可能在最新的版本中修復(fù)了向郵件列表求幫助向bugzilla報告bug.

(七)、SSL
借助Java Secure Socket Extension (JSSE),HttpClient全面支持Secure Sockets Layer (SSL)或IETF Transport Layer Security (TLS)協(xié)議上的HTTP。JSSE已經(jīng)jre1.4及以后的版本中,以前的版本則需要手工安裝設(shè)置,具體過程參見Sun網(wǎng)站或本學(xué)習(xí)筆記。
HttpClient中使用SSL非常簡單,參考下面兩個例子:
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www./");
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine().toString());
,如果通過需要授權(quán)的代理,則如下:
HttpClient httpclient = new HttpClient();
httpclient.getHostConfiguration().setProxy("myproxyhost", 8080);
httpclient.getState().setProxyCredentials("my-proxy-realm", " myproxyhost",
new UsernamePasswordCredentials("my-proxy-username", "my-proxy-password"));
GetMethod httpget = new GetMethod("https://www./");
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine().toString());
在HttpClient中定制SSL的步驟如下:
提供了一個實(shí)現(xiàn)了org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory接口的socket factory。這個 socket factory負(fù)責(zé)打一個到服務(wù)器的端口,使用標(biāo)準(zhǔn)的或第三方的SSL函數(shù)庫,并進(jìn)行象連接握手等初始化操作。通常情況下,這個初始化操作在端口被創(chuàng)建時自動進(jìn)行的。實(shí)例化一個org.apache.commons.httpclient.protocol.Protocol對象。創(chuàng)建這個實(shí)例時,需要一個合法的協(xié)議類型(如https),一個定制的socket factory,和一個默認(rèn)的端中號(如https的443端口). Protocol myhttps = new Protocol("https", new MySSLSocketFactory(), 443);然后,這個實(shí)例可被設(shè)置為協(xié)議的處理器。
HttpClient httpclient = new HttpClient();
httpclient.getHostConfiguration().setHost("www.", 443, myhttps);
GetMethod httpget = new GetMethod("/");
httpclient.executeMethod(httpget);
通過調(diào)用Protocol.registerProtocol方法,將此定制的實(shí)例,注冊為某一特定協(xié)議的默認(rèn)的處理器。由此,可以很方便地定制自己的協(xié)議類型(如myhttps)。
Protocol.registerProtocol("myhttps",
new Protocol("https", new MySSLSocketFactory(), 9443));
...
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("myhttps://www./");
httpclient.executeMethod(httpget);
如果想用自己定制的處理器取代https默認(rèn)的處理器,只需要將其注冊為"https"即可。
Protocol.registerProtocol("https",
new Protocol("https", new MySSLSocketFactory(), 443));
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www./");
httpclient.executeMethod(httpget);
已知的限制和問題持續(xù)的SSL連接在Sun的低于1.4JVM上不能工作,這是由于JVM的bug造成。通過代理訪問服務(wù)器時,非搶先認(rèn)證( Non-preemptive authentication)會失敗,這是由于HttpClient的設(shè)計缺陷造成的,以后的版本中會修改。
遇到問題的處理
很多問題,特別是在jvm低于1.4時,是由jsse的安裝造成的。
下面的代碼,可作為最終的檢測手段。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.Socket;
import javax.net.ssl.SSLSocketFactory;
public class Test {
public static final String TARGET_HTTPS_SERVER = "www.";
public static final int TARGET_HTTPS_PORT = 443;
public static void main(String[] args) throws Exception {
Socket socket = SSLSocketFactory.getDefault().
createSocket(TARGET_HTTPS_SERVER, TARGET_HTTPS_PORT);
try {
Writer out = new OutputStreamWriter(
socket.getOutputStream(), "ISO-8859-1");
out.write("GET / HTTP/1.1\r\n");
out.write("Host: " + TARGET_HTTPS_SERVER + ":" +
TARGET_HTTPS_PORT + "\r\n");
out.write("Agent: SSL-TEST\r\n");
out.write("\r\n");
out.flush();
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream(), "ISO-8859-1"));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
} finally {
socket.close();
}
}
}

(八)、httpclient的多線程處理
使用多線程的主要目的,是為了實(shí)現(xiàn)并行的下載。在httpclient運(yùn)行的過程中,每個http協(xié)議的方法,使用一個HttpConnection實(shí)例。由于連接是一種有限的資源,每個連接在某一時刻只能供一個線程和方法使用,所以需要確保在需要時正確地分配連接。HttpClient采用了一種類似jdbc連接池的方法來管理連接,這個管理工作由 MultiThreadedHttpConnectionManager完成。
MultiThreadedHttpConnectionManager connectionManager =
new MultiThreadedHttpConnectionManager();
HttpClient client = new HttpClient(connectionManager);
此是,client可以在多個線程中被用來執(zhí)行多個方法。每次調(diào)用HttpClient.executeMethod() 方法,都會去鏈接管理器申請一個連接實(shí)例,申請成功這個鏈接實(shí)例被簽出(checkout),隨之在鏈接使用完后必須歸還管理器。管理器支持兩個設(shè)置:maxConnectionsPerHost 每個主機(jī)的最大并行鏈接數(shù),默認(rèn)為2
maxTotalConnections 客戶端總并行鏈接最大數(shù),默認(rèn)為20
管理器重新利用鏈接時,采取早歸還者先重用的方式(least recently used approach)。  由于是使用HttpClient的程序而不是HttpClient本身來讀取應(yīng)答包的主體,所以HttpClient無法決定什么時間連接不再使用了,這也就要求在讀完應(yīng)答包的主體后必須手工顯式地調(diào)用releaseConnection()來釋放申請的鏈接。
MultiThreadedHttpConnectionManager connectionManager = new
MultiThreadedHttpConnectionManager();
HttpClient client = new HttpClient(connectionManager);
...
// 在某個線程中。
GetMethod get = new GetMethod("http://jakarta./");
try {
client.executeMethod(get);
// print response to stdout
System.out.println(get.getResponseBodyAsStream());
} finally {
// be sure the connection is released back to the connection
// manager
get.releaseConnection();
}
對每一個HttpClient.executeMethod須有一個method.releaseConnection()與之匹配.
 
(九)、HttpClient的連接釋放問題
在method.releaseConnection()后并沒有把鏈接關(guān)閉,這個方法只是將鏈接返回給connection manager。
如果使用HttpClient client = new HttpClient()實(shí)例化一個HttpClient connection manager默認(rèn)實(shí)現(xiàn)是使用SimpleHttpConnectionManager。
SimpleHttpConnectionManager有個構(gòu)造函數(shù)如下
public SimpleHttpConnectionManager(boolean alwaysClose) {  
    super();  
    this.alwaysClose = alwaysClose;  

alwaysClose設(shè)為true在鏈接釋放之后connection manager 就會關(guān)閉鏈。在我們HttpClient client = new HttpClient()這樣實(shí)例化一個client時connection manager是這樣被實(shí)例化的
this.httpConnectionManager = new SimpleHttpConnectionManager(); 
因此alwaysClose默認(rèn)是false,connection是不會被主動關(guān)閉的,因此我們就有了一個客戶端關(guān)閉鏈接的方法。
解決方法:
1、把事例代碼中的第一行實(shí)例化代碼改為如下即可,在method.releaseConnection();之后connection manager會關(guān)閉connection 。
HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) ); 
2、實(shí)例化代碼使用:HttpClient client = new HttpClient(); 在method.releaseConnection();之后加上 ((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();
其實(shí)shutdown源代碼是 
public void shutdown() {  
    httpConnection.close();  

3、實(shí)例化代碼使用:HttpClient client = new HttpClient();
在method.releaseConnection();之后加上
client.getHttpConnectionManager().closeIdleConnections(0);此方法源碼代碼如下:
public void closeIdleConnections(long idleTimeout) {  
    long maxIdleTime = System.currentTimeMillis() - idleTimeout;  
    if (idleStartTime <= maxIdleTime) {  
        httpConnection.close();  
    }  
}
4、代碼實(shí)現(xiàn)很簡單,所有代碼就和最上面的事例代碼一樣。只需要在HttpMethod method = new GetMethod("http://www.");加上一行HTTP頭的設(shè)置即可method.setRequestHeader("Connection", "close");

(十)、HttpClient的一個應(yīng)用的例子(圖片下載)
1. import java.io.File;  
2. import java.io.FileOutputStream;  
3. import java.io.IOException;  
4. import org.apache.commons.httpclient.HttpClient;  
5. import org.apache.commons.httpclient.methods.GetMethod;  
6.   
7. /** 
8. * 用HttpClient下載圖片 
9. * @author wei 
10. */  
11. public class TestDownImage {  
12.       
13.     public static void main(String[] args) throws IOException{  
14.         HttpClient client = new HttpClient();  
15.         GetMethod get = new GetMethod("http://images.sohu.com/uiue/sohu_logo/beijing2008/2008sohu.gif");  
16.         client.executeMethod(get);  
17.         File storeFile = new File("c:/2008sohu.gif");  
18.         FileOutputStream output = new FileOutputStream(storeFile);  
19.         //得到網(wǎng)絡(luò)資源的字節(jié)數(shù)組,并寫入文件  
20.         output.write(get.getResponseBody());  
21.         output.close();  
22.     }  
23. }

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    人妻少妇系列中文字幕| 日韩精品综合福利在线观看| 亚洲精品国产第一区二区多人| 久久人妻人人澡人人妻| 中日韩免费一区二区三区| 国产永久免费高清在线精品| 五月天六月激情联盟网| 亚洲中文字幕视频在线观看| 久久精品国产亚洲av麻豆尤物| 国产高清精品福利私拍| 千仞雪下面好爽好紧好湿全文| 99久免费精品视频在线观| 久久国产亚洲精品赲碰热| 国产一区日韩二区欧美| 久久精品亚洲精品国产欧美| 久久精品国产亚洲av久按摩| 国产欧美日产中文一区| 一本色道久久综合狠狠躁| 中文字幕亚洲在线一区| 精品国产91亚洲一区二区三区| 一区二区三区人妻在线| 精品精品国产欧美在线| 99久久国产精品成人观看| 中文字字幕在线中文乱码二区| 开心久久综合激情五月天| 日本在线不卡高清欧美| 日韩午夜老司机免费视频| 日韩精品成区中文字幕| 欧美小黄片在线一级观看| 91精品国产综合久久精品| 黄色国产精品一区二区三区| 欧美日韩中黄片免费看| 日韩欧美第一页在线观看| 在线免费看国产精品黄片| 一区中文字幕人妻少妇| 99精品国产一区二区青青 | 91人妻人人澡人人人人精品| 亚洲人午夜精品射精日韩| 色哟哟在线免费一区二区三区| 国产精品蜜桃久久一区二区| 日本在线 一区 二区|