java URL協議處理

URLConnection是一個協議處理器中的一個類,它是表示指向URL所指定的資源的活動連接。 主要用於兩個方面,一個是與服務器(特別是HTTP服務器)的交互,可以用來查看服務器發送的首部,設置連接的屬性,設置客戶端的請求的首部等。利用它也 可以實現POST和PUT方法來發送數據。另一個方面是Java的協議處理器機制的一部分。所謂的協議處理器就是將處理協議的細節從處理特定數據類型中分 離出,會涉及到客戶端與服務器端的交互,如生成正確的請求格式,解釋與數據一起返回的首部等。


獲取URLConnection

   根據一個已經創建的URL來通過openConnection來打開生成一個URLConnection,雖然利用url來獲取,其實內部調用的是 URLStreamHandler的openConnection,該方法是一個抽象方法,它返回的URLConnection會根據協議的類型返回,倘 若是一個http url則就會返回一個HTTPURLConnection。

   URL url=new URL("http://www.baidu.com");

   URLConnection uc=url.openConnection();

   此時該uc並沒有連接,本地與遠程主機是無法進行收發數據的,需要調用uc.connect()方法來建立連接。不過在使用getInputStream(),getHeaderField()等其它要求的時候會首先自動調用這個方法。

   利用此URLConnection可以很容易讀取來自服務器端的數據,只要其getInputStream。


URL和URLConnection的區別

URLConnection提供了對於HTTP首部的訪問

URLConnection可以配置客戶端向服務器端發送的請求參數即首部

URLConnection可以利用POST,PUT等請求方法與服務器進行交互

利用URL與URLConnection都可以向服務器發送HTTP請求。

1,創建一個URL,傳遞一個字符串形式的HTTP請求,可以加上查詢字符串,此時默認動作類型是get,也就是查詢,從服務器返回一個資源,再通過getInputStream正式建立客戶端與服務器之間的連接,將該HTTP請求發送到服務器端,建立必要的握手,由於HTTP是全雙工的,此時就可以在這裏面獲取服務器的返回輸出流。

2,通過url的opentConnection獲取一個指向url地址的活動連接,初次獲取的時候是沒有連接的,本地和遠程根本不能收發數據,也就是沒有socket來連接這臺主機。要利用connect()方法來在本地和遠程主機之間建立一個TCP socket連接,不過一般都是在使用getInputstream自動調用這個方法,使用getInputStream纔是真正的進行發送HTTP的請求消息。

讀取服務器響應的首部

對於服務器響應的首部,當然這裏都是以HTTP服務器響應爲準,可以來獲取響應中的首部字段。

Content-type,Content-length,Content-encoding,Date,Last-modified,Expires

可以利用getContentType()來獲取數據的MIME類型,判斷字符編碼,並且以正常的格式顯示出,如

 String encoding="ISO-8859-1";//HTTP默認的編碼方式
 URL u=new URL(url);
 URLConnection uc=u.openConnection();
  String type=uc.getContentType();
//獲取字符編碼方式
int star=type.indexOf("charset=");
if(star!=-1)
          encoding=type.substring(star+8);
   System.out.println("CharSet:"+encoding);
   InputStream raw=uc.getInputStream();
    Reader r=new InputStreamReader(new BufferedInputStream(raw),encoding);

利用getContentLength來獲取二進制文件大小,從而來下載文件

URLConnection uc=url.openConnection();
   String contentType=uc.getContentType();
int contentLength=uc.getContentLength();
if(contentType.startsWith("text/")||contentLength==-1)
   {
thrownew IOException("This is not a binary file");
   }

   InputStream raw=new BufferedInputStream(uc.getInputStream());
byte[] data=newbyte[contentLength];

int bytesRead=0;
int offset=0;
while(offset<contentLength)
   {
     bytesRead=raw.read(data, offset, contentLength);
if(bytesRead==-1)break;
     offset+=bytesRead;
   }

   raw.close();

if(offset!=contentLength)
   {
thrownew IOException("Only read "+offset+ "bytes;Expected "+contentLength+" bytes");
   }

//根據URL中獲取文件路徑的名,將獲取的流寫入到本地
   String file=url.getFile();
int start=file.lastIndexOf("/");
   String filename=file.substring(start+1);

   FileOutputStream fout=new FileOutputStream(filename);
   fout.write(data);
   fout.flush();
   fout.close();

getHeaderField(String name);可以根據指定首部名來不區分大小寫獲取該名對應的值

getHeaderFieldKey(int n)和getHeaderField(int n)依此返回首部中的名和值,很有用

     URL u=new URL(url);
     URLConnection uc=u.openConnection();

for(int j=1;;j++)
     {
       String header=uc.getHeaderField(j);
if(header==null)break;//skip the loop
       System.out.println(uc.getHeaderFieldKey(j)+":"+header);
     }


配置連接

所謂的配置主要就是定義了客戶端如何向服務器做出請求。

一般客戶端在默認情況下doInput爲true,表示可以接受來自服務器端發送的數據,這些必須在URLConnection連接之前設置。如果想要利用POST和PUT進行交互就必須要設置doOutpu爲true,默認是false


配置客戶端發送的HTPP首部

客戶端進行服務器訪問的時候,有些協議需要加上首部,配置一些名值對,可以通過setRequestProperty(name,value)設置,多個值之間利用逗號隔開。


客戶端利用POST來發送數據

package com.urlconnection;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.net.URLConnection;

//模擬表單提交處理
publicclass FormPoster {

private URL url;
private QueryString query=new QueryString();

//必須要保證是http協議
public FormPoster(URL url)
 {
if(!url.getProtocol().toUpperCase().startsWith("HTTP"))  
           {
thrownew IllegalArgumentException("Posting only works for http urls");
           }
this.url=url;
 }

publicvoid add(String name,String value)
 {
   query.add(name, value);
 }

public URL getURL()
 {
returnthis.url;
 }

public InputStream post()throws IOException
 {
   URLConnection uc=url.openConnection();
   uc.setDoOutput(true);
   Writer out=new OutputStreamWriter(uc.getOutputStream(),"ASCII");
//POST行,Content-type和Content-length是由URLConnection發送的
//只需要發送數據即可
   out.write(query.toString());
   out.write("\r\n\r\n");
   out.flush();
   out.close();

return uc.getInputStream();
 }

publicstaticvoid main(String[] args) {
// TODO Auto-generated method stub
   String u="http://www.baidu.com";
   URL url=null;
try{
       url=new URL(u);
   }catch(IOException e)
   {
   }
                FormPoster poster=new FormPoster(url);
                poster.add("hawk""fdafda");
                poster.add("good morning""fdafa");

try{
                   InputStream in=poster.post();

//讀取響應
                   InputStreamReader r=new InputStreamReader(in);
int c;
while((c=r.read())!=-1)
                   {
                    System.out.print((char)c);
                   }
                   in.close();
                }catch(IOException ex)
                {
                   System.err.println(ex);
                }
 }

}


HTTPURLConnection

這個是一個專門操作http URL的類,繼承子URLConnection,主要有7個請求的方法

利用setRequestMethod(String method)設置不同的請求方法,默認是get,區分大小寫

GET從服務器端獲取數據

POST向服務器端提交表單

PUT上傳文件至服務器

HEAD,獲取服務器端響應的頭部

OPTIONS,查詢服務器支持哪些請求方法

DELETE,刪除服務器中的文件

TRACE服務器返回客戶端發送的HTTP首部,用於檢測代理服務器對HTTP首部進行怎樣修改

服務器響應的格式如下:

HTTP/1.1 200 OK

...

這個類添加了兩個方法

getResponseMessage()獲取響應碼對應的消息,如OK

getResponseCode()獲取響應碼,如200


協議處理器

   協議處理器主要就是根據URL中的協議來找到合適的協議處理器來進行客戶端與服務器端的交互。主要涉及四個類,具體類URL,抽象類 URLConnection和URLStreamHandler,以及接口URLStreamHandlerFactory。協議處理器的流處理器總是根 據指定的協議找到最適合的URLConnection

   要想自己建立協議處理器,需要編寫兩個URLConnection和URLStreamHandler的子類,然後創建一個URLStreamHandlerFactory。

URLConnection子類主要處理與服務器交互,將服務器發送的數據轉換爲InputStream,將客戶端發送的所有數據轉換爲OutputStream。URLStreamHandler子類主要將URL的字符串表示解析爲各個部分,用這些部分來設置URL對象的各個部分,並且創建一個理解次URL協議的URLConnection。


協議處理器的基本流程如下

1 程序先利用字符串構建某一個協議的URL對象,在創建URL模式中,只會驗證是否識別URL模式,而不會對其格式的正確性進行檢查

2 構造函數利用所傳遞的參數來確定URL的協議部分,如http

3 URL()構造函數以如下方式嘗試進行找到給定的URLStreamHandler

  a 如果以前使用過此協議,從緩存中獲取URLStreamHandler

  b 否則,若設置了URLStreamHandlerFactory,將此字符串傳遞給工廠

  c 若兩個都沒有,則嘗試實例化一個位於java.protocol.handler.pkgs屬性列出的                  protocol.Handler的URLStreamHandler

  d 如果實例化失敗,就嘗試實例化一個位於sun.net.www.protocol中的protocol.Handler            的URLStreamHandler

  e 如果其中一個成功了,則設置屬性字段handler字段。如果都不成功,則拋                        出MalformedURLException異常

4 程序調用URL對象的openConnection方法

5 URL會根據協議讓URLStreamHandler返回一個適合於此URL的URLConnection

6 使用URLConnection進行與遠程資源的交互


URLStreamHandler解析URL中字段的過程

URL(String)-->URL(URL,String)-->URL(URL,String,String)-->URLStreamHandler.parseURL()

-->URLStreamHandler.setURL()-->URL.set();

要新建立一個URLStreamHandler一般只需要修改openConnection方法即可,根據需要來修改parseURL

爲每一個URLConnection子類重寫一個connect方法,該方法包含了Socket的具體建立步驟。

再利用URLStreamHandlerFactory來註冊這些流處理器

在信息交互系統設計中,不乏有自定義通訊協議設計。本章會介紹如何利用 java.net.URL 類來自定義協議。

       一般而言, URL 的格式是: protocol://[authority]hostname:port /resource?queryString 。 URL 類能夠解析出 protocol、 hostname 、 port 等信 息。 Protocol 決定了交互規範,通用的協議,比如 HTTP 、 File 、 FTP 等協議, JDK 自帶了默認的通訊實現。當然,自定 義實現是允許的。 Hostname 和 port 一般用於 Socket 或者基於 Socket 其他協議通訊方式。Resource 即資源上下 文。可能讀者利用 URL ,通過指定協議( protocol )來獲取指定資源的讀寫,比如 JDK 內置了HTTP 、 File 、 FTP 等 協議的處理方法。哪麼 URL 的工作原理到底是怎麼樣的呢?

      在成功地構造 URL 實例之後, URL API 中定義了一個 openConnection() 方法,返回一個 java.net.URLConnection 抽象類型的實例。不過,這 裏 URL 對象是代理對象(組合了 其 對象),實際調用的是, java.net.URLStreamHandler 對象 的 openConnection() 方法。

      java.net.URLStreamHandler 對象可以有兩條途徑得到:實現 java.net.URLStreamHandler ,或者實現java.net.URLStreamHandlerFactory 。

      java.net.URLStreamHandler 是 一個工廠類,通過 openConnection(java.net.URL) 方法來創建 java.net.URLConnection的實 例。 java.net.URLStreamHandler 實現靈活度很大,既可以通過不同 protocol 的 URL 實例,產生 java.net.URLConnection 對象。還可以通過相同 protocol 的多個 URL 對象,來產生對象。通用性實現,一種協議對應 一個java.net.URLStreamHandler 實現。比如,在 SUN JDK 中 sun.net.www.protocol 子包下面的多個 Handler 類就是很好的例子。如果讀者有興趣,可以去看看相關實現。

      1. 通過 java.net.URLStreamHandlerFactory 機制

      java.net.URLStreamHandlerFactory , 顧名思義,它是 java.net.URLStreamHandler 的工廠,即抽類工廠接口。通過調 用 createURLStreamHandler(String protocol) 來創建 java.net.URLStreamHandler 對象。因此,建議 java.net.URLStreamHandlerFactory 實現類應該採用 one protocol one hander 的模式, SUN JDK 也採用該模式。

     大致解了他們關係之後,再通過 UML Class diagram 來熟悉下:    

 

(圖 1 

 

     圖 1 所 示, URL 包含了名爲 factory 的 URLStreamHandlerFactory 類對象 和 handler 的 URLStreamHandler 的實例對象。對於 URL 而言, handler 對象是必須的,因爲前面說到實際處 理 openConnection() 方法是 handler 對象,而 factory並不是必須的。接下來,來分析這兩個對象是如何和 URL 交 互的。

     在 URL 的 構造方法中,暫時不用關心協議字符串等參數,更多的關注於 URL context 和 URLStreamHandler 參數。URL 實例能夠依賴於 URL context ,當 URLStreamHandler 參數爲空的情況下,當前 URL 實例將會採用 URL context 的URLStreamHandler 成員對象。當 Context 和 URLStreamHandler 參數都爲空的 時。 URL 會調用 getURLStreamHandler( String) 方法,從而根據協議 (protocol) 獲得協 議 URLStreamHandler 對象。

     在 URL 底層實現中,最初會初始化一個 protocol 和 hander 鍵值關係的 Map 映射。如果找到已有的映射關係,立即返回 URLStreamHandler 對象(第一次是取不到 URLStreamHandler 對象的)。

     如果 找不到的話,並且 URL 類中的類成員 URLStreamHandlerFactory 實例不爲空的情況下,這個實例通過 URL#setURLStreamHandlerFactory 方法來註冊。 getURLStreamHandler 方法會調用這個類成員的 createURLStreamHandler(String) 方法來創建 URLStreamHandler 實例。

 

 

 

Java代碼  收藏代碼

  1. URL.setURLStreamHandlerFactory(new MyURLStreamHandlerFactory());  

(代碼 1 

Java代碼  收藏代碼

  1. class MyURLStreamHandlerFactory implements URLStreamHandlerFactory{  

  2.         @Override  

  3.         public URLStreamHandler createURLStreamHandler(String protocol) {  

  4.             return null;  

  5.         }   

  6.           

  7. }  

(代碼 2 

 

 

 

      當 createURLStreamHandler 方法返回 null 的時候, URL 的 getURLStreamHandler 方法會採用 URLStreamHandler處理機制。

 

 

      2. 通過 java.net.URLStreamHandler 機制

      2.1. 實現類包路徑定義

        通過 JVM 啓動參數 -D java.protocol.handler.pkgs 來設置 URLStreamHandler 實現類的包路徑,例如 -Djava.protocol.handler.pkgs=com.acme.protocol , 代表處理實現類皆在這個包下。如果需要多個包的話,那麼使用“ |” 分割。比如 -D java.protocol.handler.pkgs=com.acme.protocol|com.acme.protocol2 。 SUN 的 JDK 內部實現類均是在sun.net.www.protocol. 包下,不必設置。 路徑下的協議實現類,採用先定義先選擇的原則 。

      2.2. 實現類的命名模式

      類的命名模式爲 [package_path]. [protocol].Handler ,比如默認實現” sun.net.www.protocol.[protocol].Handler”, 比如 HTTP 協議的對應的處理類名爲 -sun.net. www.protocol.http.Handler 。同樣,自定義實現的處理類,例 如, JDNI 協議實現類命名 com.acme.protocol.jndi.Handler 。

      2.3. 實現類必須又默認構造器。

      結合代碼分析,如下:

Java代碼  收藏代碼

  1. URL httpURL = new URL(null"http://www.google.com",URLStreamHandler)null);  

  2. URLConnection urlConn = httpURL.openConnection();  

 

(代碼 3 

 

      代碼 3 中沒有配置 URLStreamHandler 的類,並且代碼 2 中工廠類沒有實現了 HTTP 協議。這樣, URL 會獲取默認的 HTTP 處理類 sun.net. www.protocol.http.Handler 

      Java 1.5 開始支持網絡代理的操作,因此 URLStreamHandler 實現類儘量覆蓋 openConnection(URL) openConnection(URL,Proxy) 兩個方法。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章