原文出處--------http://hi.baidu.com/wader2006/blog/item/7e7b3a291f7801fd99250a91.html
雖然用telnet這樣的程序都可把頁面取回來,但是在與web服務(wù)器的交互中,如果涉及cookie或https或ssl等內(nèi)容,一般功能相對(duì)完備的http客戶端還是非常必要的。IE或NetScape等瀏覽器確實(shí)不錯(cuò),可是如果為實(shí)現(xiàn)持續(xù)互動(dòng)而在程序調(diào)用瀏覽器,我個(gè)人認(rèn)為其中的工作量還是不小的,這還沒考慮版權(quán)問題。最好的辦法,就是能有一個(gè)開源的包,能實(shí)現(xiàn)http客戶端的功能,供我們開發(fā)的程序調(diào)用。httpclient就是這么一個(gè)包,我相信可能有比它的實(shí)現(xiàn)更好的,但目前我只關(guān)注這個(gè)。:)
下面是nogoop做的功能比較表:
Features |
nogoop |
Sun JRE < 1.4.2 |
Sun JRE 1.4.2 |
Innovation |
Apache/Jakarta |
cookies |
|
|
|
X |
X |
plug compatible |
X |
X |
X |
X |
[partial] |
true request output stream |
|
|
|
X |
X |
true response input stream |
X |
|
|
X |
X |
connection keep alive |
X |
X |
X |
X |
X |
connection pool throttling |
X |
|
|
|
X |
connection/request timeout |
X |
|
X [uns] |
X |
X |
idle connection timeout |
X |
|
|
|
X |
pipelining of requests |
|
|
|
X |
|
alternate DNS resolution (dnsjava) |
X |
|
|
|
|
SSL |
X |
X |
X |
X |
X |
basic authentication |
X |
X |
X |
X |
X |
digest authentication |
X |
X |
X |
X |
X |
NTLM authentication |
X |
|
[Windows only] |
|
X |
proxy authentication |
X |
X |
X |
X |
X |
minimum JRE version |
1.2 |
1 |
01年4月2日 |
1.2 |
1.2 |
price |
$499 |
free |
free |
free |
free |
source available |
X |
|
|
X |
X |
diagnostic tracing |
X |
|
|
X |
X |
actively supported |
X |
X |
X |
|
X |
fix turnaround |
fast |
slow |
slow |
none |
medium |
license |
purchase |
Sun JRE |
Sun JRE |
LGPL |
Apache |
1、HttpClient的功能
- 基于標(biāo)準(zhǔn),純正java,實(shí)現(xiàn)了http1.0和1.1。
- 在一個(gè)可擴(kuò)展的OO框架內(nèi),實(shí)現(xiàn)了HTTP的全部方法(GET, POST,
PUT, DELETE, HEAD, OPTIONS, and TRACE)
- 支持HTTPS(ssl上的HTTP)的加密操作
- 透明地穿過HTTP代理建立連接
- 通過CONNECT方法,利用通過建立穿過HTTP代理的HTTPS連接
- 利用本地Java socket,透明地穿過SOCKS(版本5和4)代理建立連接
- 支持利用Basic、Digest和NTLM加密的認(rèn)證
- 支持用于上傳大文件的Multi-Part表單POST方法
- 插件式安全socket實(shí)現(xiàn),易于使用第三方的解決方案
- 連接管理,支持多線程應(yīng)用,支持設(shè)定單個(gè)主機(jī)總連接和最高連接數(shù)量,自動(dòng)檢測(cè)和關(guān)閉失效連接
- 直接將請(qǐng)求信息流送到服務(wù)器的端口
- 直接讀取從服務(wù)器的端口送出的應(yīng)答信息
- 支持HTTP/1.0中用KeepAlive和HTTP/1.1中用persistance設(shè)置的持久連接
- 直接訪問由服務(wù)器送出的應(yīng)答代碼和頭部信息
- 可設(shè)置連接超時(shí)時(shí)間
- HttpMethods 實(shí)現(xiàn)Command Pattern,以允許并行請(qǐng)求或高效連接復(fù)用
- 遵循the Apache Software License協(xié)議,源碼免費(fèi)可得
2、預(yù)備工作
對(duì)jre1.3.*,如果要HttpClient支持https,則需要下載并安裝jsse和jce.安裝的步驟如下:
1)下載jsse和jce.
2)檢查CLASSPATH中沒有與jsse和jce相關(guān)的jar包
3)將 US_export_policy.jar、local_policy.jar、jsse.jar、jnet.jar、jce1_2_x.jar、sunjce_provider.jar、jcert.jar復(fù)制到目錄:
UNIX:$JDK_HOME/jre/lib/ext
Windows:%JDK_HOME%\jre\lib\ext
4)修改下述目錄下的java.security文件。
UNIX:$JDK_HOME/jre/lib/security/
Windows:%JDK_HOME%\jre\lib\security\
5)
將
#
# List of providers and their preference orders:
#
security.provider.1=sun.security.provider.Sun
security.provider.2=com.sun.rsajca.Provider
改為:
#
# List of providers and their preference orders:
#
security.provider.1=com.sun.crypto.provider.SunJCE
security.provider.2=sun.security.provider.Sun
security.provider.3=com.sun.rsajca.Provider
security.provider.4=com.sun.net.ssl.internal.ssl.Provider
HttpClient還要求安裝commons-logging,下面跟httpclient一塊安裝。
3、取得源碼
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic login
password: anoncvs
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout jakarta-commons/logging
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout jakarta-commons/httpclient
編譯:
cd jakarta-commons/logging
ant dist
cp dis/*.jar ../httpclient/lib/
cd ../httpclient
ant dist
4、使用HttpClient編程的基本步聚
- 創(chuàng)建 HttpClient 的一個(gè)實(shí)例.
- 創(chuàng)建某個(gè)方法(DeleteMethod,EntityEnclosingMethod,ExpectContinueMethod,GetMethod,HeadMethod,MultipartPostMethod,OptionsMethod,PostMethod,PutMethod,TraceMethod)的一個(gè)實(shí)例,一般可用要目標(biāo)URL為參數(shù)。
- 讓 HttpClient 執(zhí)行這個(gè)方法.
- 讀取應(yīng)答信息.
- 釋放連接.
- 處理應(yīng)答.
在執(zhí)行方法的過程中,有兩種異常,一種是HttpRecoverableException,表示偶然性錯(cuò)誤發(fā)生,一般再試可能成功,另一種是IOException,嚴(yán)重錯(cuò)誤。
這兒有這個(gè)教程中的一個(gè)例程,可以下載。
5、認(rèn)證
HttpClient三種不同的認(rèn)證方案: Basic, Digest and NTLM. 這些方案可用于服務(wù)器或代理對(duì)客戶端的認(rèn)證,簡(jiǎ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è)定對(duì)非特定站點(diǎn)訪問所需要的登錄信息,將realm參數(shù)置為null. HttpClient內(nèi)建的自動(dòng)認(rèn)證,可以通過HttpMethod類的setDoAuthentication(boolean doAuthentication)方法關(guān)閉,而且這次關(guān)閉只影響HttpMethod當(dāng)前的實(shí)例。
搶先認(rèn)證(Preemptive Authentication)可以通過下述方法打開.
client.getState().setAuthenticationPreemptive(true);
在這種模式時(shí),HttpClient會(huì)主動(dòng)將basic認(rèn)證應(yīng)答信息傳給服務(wù)器,即使在某種情況下服務(wù)器可能返回認(rèn)證失敗的應(yīng)答,這樣做主要是為了減少連接的建立。為使每個(gè)新建的 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ī)定最早的也是最兼容(?)的方案,遺憾的是也是最不安全的一個(gè)方案,因?yàn)樗悦鞔a傳送用戶名和密碼。它要求一個(gè)UsernamePasswordCredentials實(shí)例,可以指定服務(wù)器端的訪問空間或采用默認(rèn)的登錄信息。
Digest
是在HTTP1.1中增加的一個(gè)方案,雖然不如Basic得到的軟件支持多,但還是有廣泛的使用。Digest方案比Basic方案安全得多,因它根本就不通過網(wǎng)絡(luò)傳送實(shí)際的密碼,傳送的是利用這個(gè)密碼對(duì)從服務(wù)器傳來的一個(gè)隨機(jī)數(shù)(nonce)的加密串。它要求一個(gè)UsernamePasswordCredentials實(shí)例,可以指定服務(wù)器端的訪問空間或采用默認(rèn)的登錄信息。
NTLM
這是HttpClient支持的最復(fù)雜的認(rèn)證協(xié)議。它M$設(shè)計(jì)的一個(gè)私有協(xié)議,沒有公開的規(guī)范說明。一開始由于設(shè)計(jì)的缺陷,NTLM的安全性比Digest差,后來經(jīng)過一個(gè)ServicePack補(bǔ)丁后,安全性則比較Digest高。NTLM需要一個(gè)NTCredentials實(shí)例. 注意,由于NTLM不使用訪問空間(realms)的概念,HttpClient利用服務(wù)器的域名作訪問空間的名字。還需要注意,提供給NTCredentials的用戶名,不要用域名的前綴 - 如: "adrian" 是正確的,而 "DOMAIN\adrian" 則是錯(cuò)的.
NTLM認(rèn)證的工作機(jī)制與basic和digest有很大的差別。這些差別一般由HttpClient處理,但理解這些差別有助避免在使用NTLM認(rèn)證時(shí)出現(xiàn)錯(cuò)誤。
- 從HttpClientAPI的角度來看,NTLM與其它認(rèn)證方式一樣的工作,差別是需要提供'NTCredentials'實(shí)例而不是'UsernamePasswordCredentials'(其實(shí),前者只是擴(kuò)展了后者)
- 對(duì)NTLM認(rèn)證,訪問空間是連接到的機(jī)器的域名,這對(duì)多域名主機(jī)會(huì)有一些麻煩.只有HttpClient連接中指定的域名才是認(rèn)證用的域名。建議將realm設(shè)為null以使用默認(rèn)的設(shè)置。
- NTLM只是認(rèn)證了一個(gè)連接而不是一請(qǐng)求,所以每當(dāng)一個(gè)新的連接建立就要進(jìn)行一次認(rèn)證,且在認(rèn)證的過程中保持連接是非常重要的。 因此,NTLM不能同時(shí)用于代理認(rèn)證和服務(wù)器認(rèn)證,也不能用于http1.0連接或服務(wù)器不支持持久連接的情況。
6、重定向
由于技術(shù)限制,以及為保證2.0發(fā)布版API的穩(wěn)定,HttpClient還不能自動(dòng)處重定向,但對(duì)重定向到同一主機(jī)、同一端口且采用同一協(xié)議的情況HttpClient可以支持。不能自動(dòng)的處理的情況,包括需要人工交互的情況,或超出httpclient的能力。
當(dāng)服務(wù)器重定向指令指到不同的主機(jī)時(shí),HttpClient只是簡(jiǎn)單地將重定向狀態(tài)碼作為應(yīng)答狀態(tài)。所有的300到399(包含兩端)的返回碼,都表示是重定向應(yīng)答。常見的有:
- 301 永久移動(dòng). HttpStatus.SC_MOVED_PERMANENTLY
- 302 臨時(shí)移動(dòng). HttpStatus.SC_MOVED_TEMPORARILY
- 303 See Other. HttpStatus.SC_SEE_OTHER
- 307 臨時(shí)重定向. HttpStatus.SC_TEMPORARY_REDIRECT
當(dāng)收到簡(jiǎn)單的重定向時(shí),程序應(yīng)從HttpMethod對(duì)象中抽取新的URL并將其下載。另外,限制一下重定向次數(shù)是個(gè)好的主意,這可以避免遞歸循環(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 沒有改動(dòng). HttpStatus.SC_NO T_MODIFIED
- 305 使用代理. HttpStatus.SC_USE_PROXY
7、字符編碼(character encoding)
一個(gè)HTTP協(xié)議的請(qǐng)求或應(yīng)答的頭部(在http協(xié)議中,數(shù)據(jù)包分為兩部分,一部分是頭部,由一些名值對(duì)構(gòu)成,一部分是主體(body),是真正傳辦理的數(shù)據(jù)(如HTML頁面等)),必須以US-ASCII編碼,這是因?yàn)轭^部不傳數(shù)據(jù)而只描述被要傳輸?shù)臄?shù)據(jù)的一些信息,一個(gè)例外是cookie,它是數(shù)據(jù)但是通過頭部進(jìn)行傳輸?shù)模运惨肬S-ASCII編碼。
HTTP數(shù)據(jù)包的主體部分,可以用任何一種方式進(jìn)行編碼,默認(rèn)是ISO-8859-1,具體可以用頭部字段Content-Type指定。可以利用 addRequestHeader方法,設(shè)定編碼方式;用 getResponseCharSet取得編碼方式。對(duì)HTML或XML等類型的文檔,它們的本身的Content-Type也可以指定編碼方式,主要區(qū)分兩者的作用范圍以得到正確實(shí)的解碼。
URL的編碼標(biāo)準(zhǔn),由RFC1738指定為,只能是由可打印8位/字節(jié)的us-ascii字符組成,80-ff不是us-ascii字符,而00-1F是控制字符,這兩個(gè)區(qū)域中用的字符都須加以編碼(encoded)。
8、Cookies
HttpClient能自動(dòng)管理cookie,包括允許服務(wù)器設(shè)置cookie并在需要的時(shí)候自動(dòng)將cookie返回服務(wù)器,它也支持手工設(shè)置cookie后發(fā)送到服務(wù)器端。不幸的是,對(duì)如何處理cookie,有幾個(gè)規(guī)范互相沖突:Netscape Cookie 草案, RFC2109, RFC2965,而且還有很大數(shù)量的軟件商的cookie實(shí)現(xiàn)不遵循任何規(guī)范. 為了處理這種狀況,HttpClient提供了策略驅(qū)動(dòng)的cookie管理方式。HttpClient支持的cookie規(guī)范有:
- Netscape cookie草案,是最早的cookie規(guī)范,基于rfc2109。盡管這個(gè)規(guī)范與rc2109有較大的差別,這樣做可以與一些服務(wù)器兼容。
- rfc2109,是w3c發(fā)布的第一個(gè)官方cookie規(guī)范。理論上講,所有的服務(wù)器在處理cookie(版本1)時(shí),都要遵循此規(guī)范,正因如此,HttpClient將其設(shè)為默認(rèn)的規(guī)范。遺憾的是,這個(gè)規(guī)范太嚴(yán)格了,以致很多服務(wù)器不正確的實(shí)施了該規(guī)范或仍在作用Netscape規(guī)范。在這種情況下,應(yīng)使用兼容規(guī)范。
- 兼容性規(guī)范,設(shè)計(jì)用來兼容盡可能多的服務(wù)器,即使它們并沒有遵循標(biāo)準(zhǔn)規(guī)范。當(dāng)解析cookie出現(xiàn)問題時(shí),應(yīng)考慮采用兼容性規(guī)范。
RFC2965規(guī)范暫時(shí)沒有被HttpClient支持(在以后的版本為會(huì)加上),它定義了cookie版本2,并說明了版本1cookie的不足,RFC2965有意有久取代rfc2109.
在HttpClient中,有兩種方法來指定cookie規(guī)范的使用,
-
HttpClient client = new HttpClient();
client.getState().setCookiePolicy(CookiePolicy.COMPATIBILITY);
這種方法設(shè)置的規(guī)范只對(duì)當(dāng)前的HttpState有效,參數(shù)可取值CookiePolicy.COMPATIBILITY,CookiePolicy.NETSCAPE_DRAFT或CookiePolicy.RFC2109。
-
System.setProperty("apache.commons.httpclient.cookiespec", "COMPATIBILITY");
此法指的規(guī)范,對(duì)以后每個(gè)新建立的HttpState對(duì)象都有效,參數(shù)可取值"COMPATIBILITY","NETSCAPE_DRAFT"或"RFC2109"。
常有不能解析cookie的問題,但更換到兼容規(guī)范大都能解決。
9、使用HttpClient遇到問題怎么辦?
- 用一個(gè)瀏覽器訪問服務(wù)器,以確認(rèn)服務(wù)器應(yīng)答正常
- 如果在使代理,關(guān)掉代理試試
- 另找一個(gè)服務(wù)器來試試(如果運(yùn)行著不同的服務(wù)器軟件更好)
- 檢查代碼是否按教程中講的思路編寫
- 設(shè)置log級(jí)別為debug,找出問題出現(xiàn)的原因
- 打開wiretrace,來追蹤客戶端與服務(wù)器的通信,以確實(shí)問題出現(xiàn)在什么地方
- 用telnet或netcat手工將信息發(fā)送到服務(wù)器,適合于猜測(cè)已經(jīng)找到了原因而進(jìn)行試驗(yàn)時(shí)
- 將netcat以監(jiān)聽方式運(yùn)行,用作服務(wù)器以檢查httpclient如何處理應(yīng)答的。
- 利用最新的httpclient試試,bug可能在最新的版本中修復(fù)了
- 向郵件列表求幫助
- 向bugzilla報(bào)告bug.
10、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非常簡(jiǎn)單,參考下面兩個(gè)例子:
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.verisign.com/");
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.verisign.com/");
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine().toString());
在HttpClient中定制SSL的步驟如下:
- 提供了一個(gè)實(shí)現(xiàn)了org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory接口的socket factory。這個(gè) socket factory負(fù)責(zé)打一個(gè)到服務(wù)器的端口,使用標(biāo)準(zhǔn)的或第三方的SSL函數(shù)庫(kù),并進(jìn)行象連接握手等初始化操作。通常情況下,這個(gè)初始化操作在端口被創(chuàng)建時(shí)自動(dòng)進(jìn)行的。
- 實(shí)例化一個(gè)org.apache.commons.httpclient.protocol.Protocol對(duì)象。創(chuàng)建這個(gè)實(shí)例時(shí),需要一個(gè)合法的協(xié)議類型(如https),一個(gè)定制的socket factory,和一個(gè)默認(rèn)的端中號(hào)(如https的443端口).
Protocol myhttps = new Protocol("https", new MySSLSocketFactory(), 443);
然后,這個(gè)實(shí)例可被設(shè)置為協(xié)議的處理器。
HttpClient httpclient = new HttpClient();
httpclient.getHostConfiguration().setHost("www.whatever.com", 443, myhttps);
GetMethod httpget = new GetMethod("/");
httpclient.executeMethod(httpget);
- 通過調(diào)用Protocol.registerProtocol方法,將此定制的實(shí)例,注冊(cè)為某一特定協(xié)議的默認(rèn)的處理器。由此,可以很方便地定制自己的協(xié)議類型(如myhttps)。
Protocol.registerProtocol("myhttps",
new Protocol("https", new MySSLSocketFactory(), 9443));
...
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("myhttps://www.whatever.com/");
httpclient.executeMethod(httpget);
如果想用自己定制的處理器取代https默認(rèn)的處理器,只需要將其注冊(cè)為"https"即可。
Protocol.registerProtocol("https",
new Protocol("https", new MySSLSocketFactory(), 443));
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.whatever.com/");
httpclient.executeMethod(httpget);
已知的限制和問題
- 持續(xù)的SSL連接在Sun的低于1.4JVM上不能工作,這是由于JVM的bug造成。
- 通過代理訪問服務(wù)器時(shí),非搶先認(rèn)證( Non-preemptive authentication)會(huì)失敗,這是由于HttpClient的設(shè)計(jì)缺陷造成的,以后的版本中會(huì)修改。
遇到問題的處理
很多問題,特別是在jvm低于1.4時(shí),是由jsse的安裝造成的。
下面的代碼,可作為最終的檢測(cè)手段。
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.verisign.com";
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();
}
}
}
1、httpclient的多線程處理
使用多線程的主要目的,是為了實(shí)現(xiàn)并行的下載。在httpclient運(yùn)行的過程中,每個(gè)http協(xié)議的方法,使用一個(gè)HttpConnection實(shí)例。由于連接是一種有限的資源,每個(gè)連接在某一時(shí)刻只能供一個(gè)線程和方法使用,所以需要確保在需要時(shí)正確地分配連接。HttpClient采用了一種類似jdbc連接池的方法來管理連接,這個(gè)管理工作由 MultiThreadedHttpConnectionManager完成。
MultiThreadedHttpConnectionManager connectionManager =
new MultiThreadedHttpConnectionManager();
HttpClient client = new HttpClient(connectionManager);
此是,client可以在多個(gè)線程中被用來執(zhí)行多個(gè)方法。每次調(diào)用HttpClient.executeMethod() 方法,都會(huì)去鏈接管理器申請(qǐng)一個(gè)連接實(shí)例,申請(qǐng)成功這個(gè)鏈接實(shí)例被簽出(checkout),隨之在鏈接使用完后必須歸還管理器。管理器支持兩個(gè)設(shè)置:
maxConnectionsPerHost |
每個(gè)主機(jī)的最大并行鏈接數(shù),默認(rèn)為2 |
maxTotalConnections |
客戶端總并行鏈接最大數(shù),默認(rèn)為20 |
管理器重新利用鏈接時(shí),采取早歸還者先重用的方式(least recently used approach)。
由于是使用HttpClient的程序而不是HttpClient本身來讀取應(yīng)答包的主體,所以HttpClient無法決定什么時(shí)間連接不再使用了,這也就要求在讀完應(yīng)答包的主體后必須手工顯式地調(diào)用releaseConnection()來釋放申請(qǐng)的鏈接。
MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
HttpClient client = new HttpClient(connectionManager);
...
// 在某個(gè)線程中。
GetMethod get = new GetMethod("http://jakarta.apache.org/");
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();
}
對(duì)每一個(gè)HttpClient.executeMethod須有一個(gè)method.releaseConnection()與之匹配.
12、HTTP方法
HttpClient支持的HTTP方法有8種,下面分述之。
1、Options
HTTP方法Options用來向服務(wù)器發(fā)送請(qǐng)求,希望獲得針對(duì)由請(qǐng)求URL(request url)標(biāo)志的資源在請(qǐng)求/應(yīng)答的通信過程可以使用的功能選項(xiàng)。通過這個(gè)方法,客戶端可以在采取具體行動(dòng)之前,就可對(duì)某一資源決定采取什么動(dòng)作和/或以及一些必要條件,或者了解服務(wù)器提供的功能。這個(gè)方法最典型的應(yīng)用,就是用來獲取服務(wù)器支持哪些HTTP方法。
HttpClient中有一個(gè)類叫OptionsMethod,來支持這個(gè)HTTP方法,利用這個(gè)類的getAllowedMethods方法,就可以很簡(jiǎn)單地實(shí)現(xiàn)上述的典型應(yīng)用。
OptionsMethod options = new OptionsMethod("http://jakarta.apache.org");
// 執(zhí)行方法并做相應(yīng)的異常處理
...
Enumeration allowedMethods = options.getAllowedMethods();
options.releaseConnection();
2、Get
HTTP方法GET用來取回請(qǐng)求URI(request-URI)標(biāo)志的任何信息(以實(shí)體(entity)的形式),"get"這個(gè)單詞本意就是”獲取“的意思。如果請(qǐng)求URI指向的一個(gè)數(shù)據(jù)處理過程,那這個(gè)過程生成的數(shù)據(jù),在應(yīng)答中以實(shí)體的形式被返回,而不是將這個(gè)過程的代碼的返回。
如果HTTP包中含有If-ModifiedSince, If-Unmodified-Since, If-Match, If-None-Match, 或 If-Range等頭字段,則GET也就變成了”條件GET“,即只有滿足上述字段描述的條件的實(shí)體才被取回,這樣可以減少一些非必需的網(wǎng)絡(luò)傳輸,或者減少為獲取某一資源的多次請(qǐng)求(如第一次檢查,第二次下載)。(一般的瀏覽器,都有一個(gè)臨時(shí)目錄,用來緩存一些網(wǎng)頁信息,當(dāng)再次瀏覽某個(gè)頁面的時(shí)候,只下載那些修改過的內(nèi)容,以加快瀏覽速度,就是這個(gè)道理。至于檢查,則常用比GET更好的方法HEAD來實(shí)現(xiàn)。)如果HTTP包中含有Range頭字段,那么請(qǐng)求URI指定的實(shí)體中,只有決定范圍條件的那部分才被取回來。(用過多線程下載工具的朋友,可能比較容易理解這一點(diǎn))
這個(gè)方法的典型應(yīng)用,用來從web服務(wù)器下載文檔。HttpClient定義了一個(gè)類叫GetMethod來支持這個(gè)方法,用GetMethod類中g(shù)etResponseBody, getResponseBodyAsStream 或 getResponseBodyAsString函數(shù)就可以取到應(yīng)答包包體中的文檔(如HTML頁面)信息。這這三個(gè)函數(shù)中,getResponseBodyAsStream通常是最好的方法,主要是因?yàn)樗梢员苊庠谔幚硐螺d的文檔之前緩存所有的下載的數(shù)據(jù)。
GetMethod get = new GetMethod("http://jakarta.apache.org");
// 執(zhí)行方法,并處理失敗的請(qǐng)求.
...
InputStream in = get.getResponseBodyAsStream();
// 利用輸入流來處理信息。
get.releaseConnection();
對(duì)GetMethod的最常見的不正確的使用,是沒有將全部的應(yīng)答主體的數(shù)據(jù)讀出來。還有,必須注意要手工明確地將鏈接釋放。
3、Head
HTTP的Head方法,與Get方法完全一致,唯一的差別是服務(wù)器不能在應(yīng)答包中包含主體(message-body),而且一定不能包含主體。使用這個(gè)方法,可以使得客戶無需將資源下載回就可就以得到一些關(guān)于它的基本信息。這個(gè)方法常用來檢查超鏈的可訪問性以及資源最近有沒有被修改。
HTTP的head方法最典型的應(yīng)用,是獲取資源的基本信息。HttpClient定義了HeadMethod類支持這個(gè)方法,HeadMethod類與其它*Method類一樣,用 getResponseHeaders()取回頭部信息,而沒有自己的特殊方法。
HeadMethod head = new HeadMethod("http://jakarta.apache.org");
// 執(zhí)行方法,并處理失敗的請(qǐng)求.
...
// 取回應(yīng)答包的頭字段信息.
Header[] headers = head.getResponseHeaders(); // 只取回最后修改日期字段的信息.
String lastModified = head.getResponseHeader("last-modified").getValue();
4、Post
Post在英文有“派駐”的意思,HTTP方法POST就是要求服務(wù)器接受請(qǐng)求包中的實(shí)體,并將其作為請(qǐng)求URI的下屬資源。從本質(zhì)上說,這意味著服務(wù)器要保存這個(gè)實(shí)體信息,而且通常由服務(wù)器端的程序進(jìn)行處理。Post方法的設(shè)計(jì)意圖,是要以一種統(tǒng)一的方式實(shí)現(xiàn)下列功能:
- 對(duì)已有的資源做評(píng)注
- 將信息發(fā)布到BBS、新聞組、郵件列表,或類似的文章組中
- 將一塊數(shù)據(jù),提交給數(shù)據(jù)處理進(jìn)程
- 通過追加操作,來擴(kuò)展一個(gè)數(shù)據(jù)庫(kù)
這些都操作期待著在服務(wù)器端產(chǎn)生一定的“副作用”,如修改了數(shù)據(jù)庫(kù)等。
HttpClient定義PostMethod類以支持該HTTP方法,在httpclient中,使用post方法有兩個(gè)基本的步驟:為請(qǐng)求包準(zhǔn)備數(shù)據(jù),然后讀取服務(wù)器來的應(yīng)答包的信息。通過調(diào)用 setRequestBody()函數(shù),來為請(qǐng)求包提供數(shù)據(jù),它可以接收三類參數(shù):輸入流、名值對(duì)數(shù)組或字符串。至于讀取應(yīng)答包需要調(diào)用 getResponseBody* 那一系列的方法,與GET方法處理應(yīng)答包的方法相同。
常見問題是,沒有將全部應(yīng)答讀取(無論它對(duì)程序是否有用),或沒有釋放鏈接資源。