天天看點

Java網絡程式設計之URLConnection協定處理器

   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可以配置用戶端向伺服器端發送的請求參數即首部

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

   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");

                   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來注冊這些流處理器