Sealyu

          --- 博客已遷移至: http://www.sealyu.com/blog

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            618 隨筆 :: 87 文章 :: 225 評(píng)論 :: 0 Trackbacks
          <2010年12月>
          2829301234
          567891011
          12131415161718
          19202122232425
          2627282930311
          2345678

          常用鏈接

          留言簿(14)

          隨筆分類

          隨筆檔案

          友情鏈接

          搜索

          積分與排名

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          HttpClient 是 Apache Jakarta Common 下的子項(xiàng)目,可以用來(lái)提供高效的、最新的、功能豐富的支持 HTTP 協(xié)議的客戶端編程工具包,并且它支持 HTTP 協(xié)議最新的版本和建議。

          HttpClient 功能介紹

          以下列出的是 HttpClient 提供的主要的功能,要知道更多詳細(xì)的功能可以參見 HttpClient 的主頁(yè)。

          • 實(shí)現(xiàn)了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
          • 支持自動(dòng)轉(zhuǎn)向
          • 支持 HTTPS 協(xié)議
          • 支持代理服務(wù)器等

          下面將逐一介紹怎樣使用這些功能。首先,我們必須安裝好 HttpClient。

          下面是個(gè)我寫的程序:

          package com.newpalm.unicomfetch.threads;
          import java.util.Vector;
          import org.apache.commons.httpclient.Cookie;
          import org.apache.commons.httpclient.HttpClient;
          import org.apache.commons.httpclient.NameValuePair;
          import org.apache.commons.httpclient.cookie.CookiePolicy;
          import org.apache.commons.httpclient.cookie.CookieSpec;
          import org.apache.commons.httpclient.methods.GetMethod;
          import org.apache.commons.httpclient.methods.PostMethod;
          import org.htmlparser.Node;
          import org.htmlparser.Parser;
          import org.htmlparser.tags.TableColumn;
          import org.htmlparser.tags.TableRow;
          import org.htmlparser.tags.TableTag;
          import org.htmlparser.visitors.ObjectFindingVisitor;
          /**
          * 用來(lái)演示登錄表單的示例
          * @author Liudong
          */

          public class FormLoginDemo {
           
          public static void main(String[] args) throws Exception{
          Parser parser = null;
          ObjectFindingVisitor visitor = null;
          HttpClient client = new HttpClient();
           
          //模擬登錄頁(yè)面
          PostMethod post = new PostMethod("<a href="http://211.90.119.58:9999/SPLogin.aspx"><span style="color: #78ba00;">http://211.90.119.58:9999/SPLogin.aspx</span></a>");
          NameValuePair name = new NameValuePair("txtHandsetNumber", "123456");
          NameValuePair pass = new NameValuePair("txtPassword", "123456");
          NameValuePair __VIEWSTATE = new NameValuePair("__VIEWSTATE", "dDwtMTUxMzkxNTQ0Mzt0PDtsPGk8MT47PjtsPHQ8O2w8aTwxPjs+O2w8dDxwPGw8VGV4dDs+O2w8XDxtYXJxdWVlIERpcmVjdGlvbj0idXAiIGJlaGF2aW9yPSJzY3JvbGwiICBoZWlnaHQ9JzE1MCcgc2Nyb2xsZGVsYXk9MzAwIG9ubW91c2VvdmVyPSd0aGlzLnN0b3AoKScgb25tb3VzZW91dD0ndGhpcy5zdGFydCgpJ1w+XDxpbWcgc3JjPSJpbWFnZS9kZWZhdWx0X2NhbGxfaWNvbi5naWYiIHdpZHRoPSI0IiBoZWlnaHQ9IjciXD4mbmJzcFw7Jm5ic3BcO1w8YSBocmVmID0iIyIgb25jbGljaz0ib3BlbldpbmRvdygnL0J1bGxldGluLmFzcHg/QnVsbGV0aW5JZD0yMycpIlw+c3DkuJrliqHmjqXlhaXms6jmhI/kuovpoblcPC9hXD4KXDxiclw+XDxiclw+XDwvbWFycXVlZVw+Oz4+Ozs+Oz4+Oz4+O2w8YnRuTG9naW47Pj4pKEgdIvj1JmRdgJaS7sGzD39jZg==");
          NameValuePair btnLoginx = new NameValuePair("btnLogin.x", "0");
          NameValuePair btnLoginy = new NameValuePair("btnLogin.y", "5");
          post.setRequestBody(new NameValuePair[]{name,pass,__VIEWSTATE,btnLoginx,btnLoginy});
          int status = client.executeMethod(post);
           
          post.releaseConnection();
          //查看cookie信息
          CookieSpec cookiespec = CookiePolicy.getDefaultSpec();
          Cookie[] cookies = client.getState().getCookies();
          if (cookies.length == 0) {
          System.out.println("None");
          } else {
          for (int i = 0; i &lt; cookies.length; i++) {
          System.out.println(cookies[i].toString());
          }
          }
           
          //訪問(wèn)所需的頁(yè)面
          GetMethod get = new GetMethod("<a href="http://211.90.119.58:9999/PhoneSearch/PhoneSearch.aspx"><span style="color: #78ba00;">http://211.90.119.58:9999/PhoneSearch/PhoneSearch.aspx</span></a>");
          client.executeMethod(get);
          visitor = new ObjectFindingVisitor(TableTag.class);
          parser = new Parser();
          parser.setInputHTML(get.getResponseBodyAsString());
          parser.setEncoding("GBK");
          parser.visitAllNodesWith(visitor);
           
          //取得要解析的頁(yè)面數(shù)
          Node[] tables = visitor.getTags();
          TableTag tableTag = (TableTag) tables[tables.length-1];
          TableRow[] rows = tableTag.getRows();
          TableRow row = rows[0];
          TableColumn[] col = row.getColumns();
          int pageNumber = Integer.parseInt(col[0].getChildrenHTML().substring(25, 29));
          get.releaseConnection();
           
          for(int i=1;i&lt;pageNumber;i++){
          PostMethod pt = new PostMethod("<a href="http://211.90.119.58:9999/PhoneSearch/PhoneSearch.aspx"><span style="color: #78ba00;">http://211.90.119.58:9999/PhoneSearch/PhoneSearch.aspx</span></a>");
          NameValuePair txtPage = new NameValuePair("txtPage",Integer.toString(i));
          __VIEWSTATE = new NameValuePair("__VIEWSTATE""")
          NameValuePair __EVENTTARGET = new NameValuePair("__EVENTTARGET", "");
          NameValuePair __EVENTARGUMENT = new NameValuePair("__EVENTARGUMENT", "");
          NameValuePair TBMDN = new NameValuePair("TBMDN", "");
          NameValuePair TBServiceType = new NameValuePair("TBServiceType", "");
          NameValuePair TBStartTime = new NameValuePair("TBStartTime", "");
          NameValuePair TBEndTime = new NameValuePair("TBEndTime", "");
          NameValuePair btnGotox = new NameValuePair("btnGoto.x", "26");
          NameValuePair btnGotoy = new NameValuePair("btnGoto.y", "13");
          pt.setRequestBody(new NameValuePair[]{__EVENTTARGET,__EVENTARGUMENT,__VIEWSTATE,TBMDN,TBServiceType,TBStartTime,TBEndTime,txtPage,btnGotox,btnGotoy});
          int a = client.executeMethod(pt);
           
           
          parser.setInputHTML(pt.getResponseBodyAsString());
          parser.setEncoding("GBK");
          parser.visitAllNodesWith(visitor);
           
          tables = visitor.getTags();
          tableTag = (TableTag) tables[tables.length-3];
           
          rows = tableTag.getRows();
          row = rows[1];
          col = row.getColumns();
          System.out.println(col[4].getChildrenHTML().toString());
          get.releaseConnection();
          }
           
          }
          }

          使用HttpClient過(guò)程中常見的一些問(wèn)題

          下面介紹在使用HttpClient過(guò)程中常見的一些問(wèn)題。

          字符編碼

          某目標(biāo)頁(yè)的編碼可能出現(xiàn)在兩個(gè)地方,第一個(gè)地方是服務(wù)器返回的http頭中,另外一個(gè)地方是得到的html/xml頁(yè)面中。

          • 在http頭的Content-Type字段可能會(huì)包含字符編碼信息。例如可能返回的頭會(huì)包含這樣子的信息:Content-Type: text/html; charset=UTF-8。這個(gè)頭信息表明該頁(yè)的編碼是UTF-8,但是服務(wù)器返回的頭信息未必與內(nèi)容能匹配上。比如對(duì)于一些雙字節(jié)語(yǔ)言國(guó)家,可能服務(wù) 器返回的編碼類型是UTF-8,但真正的內(nèi)容卻不是UTF-8編碼的,因此需要在另外的地方去得到頁(yè)面的編碼信息;但是如果服務(wù)器返回的編碼不是UTF- 8,而是具體的一些編碼,比如gb2312等,那服務(wù)器返回的可能是正確的編碼信息。通過(guò)method對(duì)象的getResponseCharSet()方 法就可以得到http頭中的編碼信息。
          • 對(duì)于象xml或者h(yuǎn)tml這樣的文件,允許作者在頁(yè)面中直接指定編碼類型。比如在html中會(huì)有<meta http-equiv=”Content-Type” content=”text/html; charset=gb2312″/>這樣的標(biāo)簽;或者在xml中會(huì)有<?xml version=”1.0″ encoding=”gb2312″?>這樣的標(biāo)簽,在這些情況下,可能與http頭中返回的編碼信息沖突,需要用戶自己判斷到底那種編碼類型應(yīng)該 是真正的編碼。

          自動(dòng)轉(zhuǎn)向

          根據(jù)RFC2616中對(duì)自動(dòng)轉(zhuǎn)向的定義,主要有兩種:301和302。301表示永久的移走(Moved Permanently),當(dāng)返回的是301,則表示請(qǐng)求的資源已經(jīng)被移到一個(gè)固定的新地方,任何向該地址發(fā)起請(qǐng)求都會(huì)被轉(zhuǎn)到新的地址上。302表示暫時(shí) 的轉(zhuǎn)向,比如在服務(wù)器端的servlet程序調(diào)用了sendRedirect方法,則在客戶端就會(huì)得到一個(gè)302的代碼,這時(shí)服務(wù)器返回的頭信息中 location的值就是sendRedirect轉(zhuǎn)向的目標(biāo)地址。

          HttpClient支持自動(dòng)轉(zhuǎn)向處理,但是象POST和PUT方式這種要求接受后繼服務(wù)的請(qǐng)求方式,暫時(shí)不支持自動(dòng)轉(zhuǎn)向,因此如果碰到POST方 式提交后返回的是301或者302的話需要自己處理。就像剛才在POSTMethod中舉的例子:如果想進(jìn)入登錄BBS后的頁(yè)面,必須重新發(fā)起登錄的請(qǐng) 求,請(qǐng)求的地址可以在頭字段location中得到。不過(guò)需要注意的是,有時(shí)候location返回的可能是相對(duì)路徑,因此需要對(duì)location返回的 值做一些處理才可以發(fā)起向新地址的請(qǐng)求。

          另外除了在頭中包含的信息可能使頁(yè)面發(fā)生重定向外,在頁(yè)面中也有可能會(huì)發(fā)生頁(yè)面的重定向。引起頁(yè)面自動(dòng)轉(zhuǎn)發(fā)的標(biāo)簽是:<meta http-equiv=”refresh” content=”5; url=http://www.ibm.com/us”>。如果你想在程序中也處理這種情況的話得自己分析頁(yè)面來(lái)實(shí)現(xiàn)轉(zhuǎn)向。需要注意的是,在上面那 個(gè)標(biāo)簽中url的值也可以是一個(gè)相對(duì)地址,如果是這樣的話,需要對(duì)它做一些處理后才可以轉(zhuǎn)發(fā)。

          處理HTTPS協(xié)議

          HttpClient提供了對(duì)SSL的支持,在使用SSL之前必須安裝JSSE。在Sun提供的1.4以后的版本中,JSSE已經(jīng)集成到JDK中, 如果你使用的是JDK1.4以前的版本則必須安裝JSSE。JSSE不同的廠家有不同的實(shí)現(xiàn)。下面介紹怎么使用HttpClient來(lái)打開Https連 接。這里有兩種方法可以打開https連接,第一種就是得到服務(wù)器頒發(fā)的證書,然后導(dǎo)入到本地的keystore中;另外一種辦法就是通過(guò)擴(kuò)展 HttpClient的類來(lái)實(shí)現(xiàn)自動(dòng)接受證書。

          方法1,取得證書,并導(dǎo)入本地的keystore:

          • 安裝JSSE (如果你使用的JDK版本是1.4或者1.4以上就可以跳過(guò)這一步)。本文以IBM的JSSE為例子說(shuō)明。先到IBM網(wǎng)站上下載JSSE的安裝包。然后解 壓開之后將ibmjsse.jar包拷貝到<java-home>"lib"ext"目錄下。
          • 取得并且導(dǎo)入證書。證書可以通過(guò)IE來(lái)獲得:1. 用IE打開需要連接的https網(wǎng)址,會(huì)彈出如下對(duì)話框:

            2. 單擊”View Certificate”,在彈出的對(duì)話框中選擇”Details”,然后再單擊”Copy to File”,根據(jù)提供的向?qū)纱L問(wèn)網(wǎng)頁(yè)的證書文件

            3. 向?qū)У谝徊?,歡迎界面,直接單擊”Next”,

            4. 向?qū)У诙?,選擇導(dǎo)出的文件格式,默認(rèn),單擊”Next”,

            5. 向?qū)У谌剑斎雽?dǎo)出的文件名,輸入后,單擊”Next”,

            6. 向?qū)У谒牟?,單?#8221;Finish”,完成向?qū)?br />

            7. 最后彈出一個(gè)對(duì)話框,顯示導(dǎo)出成功

          • 用keytool工具把剛才導(dǎo)出的證書倒入本地keystore。Keytool命令在<java-home>"bin"下,打開命令行窗口,并到<java-home>"lib"security"目錄下,運(yùn)行下面的命令:

            keytool -import -noprompt -keystore cacerts -storepass changeit -alias yourEntry1 -file your.cer

            其中參數(shù)alias后跟的值是當(dāng)前證書在keystore中的唯一標(biāo)識(shí)符,但是大小寫不區(qū)分;參數(shù)file后跟的是剛才通過(guò)IE導(dǎo)出的證書所在的路徑和文件名;如果你想刪除剛才導(dǎo)入到keystore的證書,可以用命令:


            keytool -delete -keystore cacerts -storepass changeit -alias yourEntry1
          • 寫程序訪問(wèn)https地址。如果想測(cè)試是否能連上https,只需要稍改一下GetSample例子,把請(qǐng)求的目標(biāo)變成一個(gè)https地址。

            GetMethod getMethod = new GetMethod("https://www.yourdomain.com");

            運(yùn)行該程序可能出現(xiàn)的問(wèn)題:

            1. 拋出異常java.net.SocketException: Algorithm SSL not available。出現(xiàn)這個(gè)異常可能是因?yàn)闆](méi)有加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. 拋出異常java.net.SocketException: SSL implementation not available。出現(xiàn)這個(gè)異常可能是你沒(méi)有把ibmjsse.jar拷貝到<java-home>"lib"ext"目錄下。

            3. 拋出異常javax.net.ssl.SSLHandshakeException: unknown certificate。出現(xiàn)這個(gè)異常表明你的JSSE應(yīng)該已經(jīng)安裝正確,但是可能因?yàn)槟銢](méi)有把證書導(dǎo)入到當(dāng)前運(yùn)行JRE的keystore中,請(qǐng)按照前 面介紹的步驟來(lái)導(dǎo)入你的證書。

          方法2,擴(kuò)展HttpClient類實(shí)現(xiàn)自動(dòng)接受證書

          因?yàn)檫@種方法自動(dòng)接收所有證書,因此存在一定的安全問(wèn)題,所以在使用這種方法前請(qǐng)仔細(xì)考慮您的系統(tǒng)的安全需求。具體的步驟如下:

          • 提供一個(gè)自定義的socket factory(test.MySecureProtocolSocketFactory)。這個(gè)自定義的類必須實(shí)現(xiàn)接口 org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory,在實(shí)現(xiàn)接口 的類中調(diào)用自定義的X509TrustManager(test.MyX509TrustManager),這兩個(gè)類可以在隨本文帶的附件中得到
          • 創(chuàng)建一個(gè)org.apache.commons.httpclient.protocol.Protocol的實(shí)例,指定協(xié)議名稱和默認(rèn)的端口號(hào)

            Protocol myhttps = new Protocol("https", new MySecureProtocolSocketFactory (), 443);
          • 注冊(cè)剛才創(chuàng)建的https協(xié)議對(duì)象

            Protocol.registerProtocol("https ", myhttps);
          • 然后按照普通編程方式打開https的目標(biāo)地址,代碼請(qǐng)參見test.NoCertificationHttpsGetSample

          處理代理服務(wù)器

          HttpClient中使用代理服務(wù)器非常簡(jiǎn)單,調(diào)用HttpClient中setProxy方法就可以,方法的第一個(gè)參數(shù)是代理服務(wù)器地址,第二個(gè)參數(shù)是端口號(hào)。另外HttpClient也支持SOCKS代理。

          posted on 2010-12-28 09:05 seal 閱讀(3019) 評(píng)論(0)  編輯  收藏 所屬分類: web
          主站蜘蛛池模板: 安福县| 广宗县| 榕江县| 同德县| 宁乡县| 红安县| 中江县| 临城县| 时尚| 河北区| 彰化市| 泰来县| 大埔区| 阳信县| 元谋县| 当雄县| 峨眉山市| 桂林市| 泸西县| 兴宁市| 蓝山县| 大英县| 霞浦县| 北川| 江永县| 鄂托克前旗| 陆良县| 长汀县| 达日县| 涟水县| 太康县| 莱西市| 防城港市| 五原县| 华安县| 自贡市| 德昌县| 湟源县| 松江区| 文安县| 安图县|