概述
Java 對客戶程式的通信程序進行了抽象,提供了通用的協議處理框架,該框架封裝了 Socket,主要包括以下類:
- URL 類:統一資源定位符,表示客戶程式要訪問的遠程資源
- URLConnection 類:表示客戶程式與遠程服務器的連接,客戶程式可以從 URLConnection 獲得資料輸入流和輸出流
- URLStreamHandler 類:協議處理器,主要負責創建與協議相關的 URLConnection 物件
- ContentHandler 類:內容處理器,負責決議服務器發送的資料,把它轉換為相應的 Java 物件
以上類都位于 java.net 包,除 URL 類為具體類,其余的都是抽象類,對于一種具體的協議,需要創建相應的具體子類,Oracle 公司為協議處理框架提供了基于 HTTP 的實作,它們都位于 JDK 類別庫的 sun.net.www 包或者其子包
URL 類的用法
下例的 HtpClient 類利用 URL 類創建了一個簡單的 HTTP 客戶程式,先創建了一個 URL 物件,然后通過它的 openStream() 方法獲得一個輸入流,接下來就從這個輸入流中讀取服務器發送的回應結果
public class HttpClient {
public static void main(String args[]) throws IOException {
//http是協議符號
URI url = new URL("http://www.javathinker.net/hello.htm");
//接收回應結果
InputStream in = url.openStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
bytel] buff = new byte[1024];
int len = -l;
while((len = in.read(buff)) != -1) {
buffer.write(buff, 0, len);
}
//把位元組陣列轉換為字串
System.out.println(new String(buffer.toByteArray()));
}
}
URL 類的構造方法創建 URLStreamHandler 實體的流程如下:
-
如果在 URL 快取已經存在這樣的
URLStreamHandler實體,則無須再創建,否則繼續執行下一步 -
如果程式通過 URL 類的靜態
setURLStreamHandlerFactory()方法設定了URLStreamHandlerFactory介面的具體實作類,那么就通過這個工廠類的createURLStreamHandler()方法來構造URLStreamHandler實體,否則繼續執行下一步 -
根據系統屬性
java.prolocol.handler.pkgs來決定URLStreamHandler具體子類的名字,然后對其實體化,假定運行 HttpClient 的命令為:java -Djava.protocol.handler.pkgs=com.abc.net.www | net.javathinker.protocols HttpClient以上命令中的 -D 選項設定系統屬性,會先查找并試圖實體化
com.abc.net.www.http.Handler類,如果失敗,再試圖實體化net.javathinkerprotocols.http.Handler類,如果以上操作都失敗,那么繼續執行下一步 -
試圖實體化位于
sun.net.www.prolocol包的sun.netwww.protocol.協議名.Handler類,如果失敗,URL 構造方法就會拋出 MalforedURLException,在本例協議名是 http,會試圖實體化sun.net.www.protocol.http.Handler類
URL 類具有以下方法:
openConnection():創建并回傳一個URLConnection物件,這個openConnection()方法實際上是通過呼叫URLStreamHandler類的openConnection()方法,來創建URLConnection物件openStream():回傳用于讀取服務器發送資料的輸入流,該方法實際上通過呼叫URLConnection類的getInputStream()方法來獲得輸入流getContent():回傳包裝了服務器發送資料的 Java 物件,該方法實際上呼叫URLConnection類的getContent)方法,而URLConnection類的getContent()方法又呼叫了ContentHandler類的getContent()方法
URLConnection 類的用法
URLConnection 類表示客戶程式與遠程服務器的連接,URLConnection 有兩個 boolean 型別的屬性以及相應的 get 和 set 方法:
- dolnput:如果取值為 true,表示允許獲得輸入流,讀取遠程服務器發送的資料該屬性的默認值為 true,程式可通過 getDolnput() 和 setDolnput() 方法來讀取和設定該屬性
- doOutput:如果取值為 true,表示允許獲得輸出流,向遠程服務器發送資料該屬性的默認值為 false,程式可通過 getDoOutput() 和 setDoOutput() 方法來讀取和設定該屬性
URLConnection 類提供了讀取遠程服務器的回應資料的一系列方法:
getHeaderField(String name):回傳回應頭中引數 name 指定的屬性的值getContentType():回傳回應正文的型別,如果無法獲取回應正文的型別就回傳 nullgetContentLength():回傳回應正文的長度,如果無法獲取回應正文的長度,就回傳 -1getContentEncoding():回傳回應正文的編碼型別,如果無法獲取回應正文的編碼型別,就回傳 null
下例的 HtpClient 類利用 URLConnection 類來讀取服務器的回應結果
public class HttpClient {
public static void main(String args[]) throws IOException {
URL url = new URL("http://www,javathinkernet/hello.htm");
URLConnection connection = url.openConnection();
//接收回應結果
System.out.printIn("正文型別:" + connection.getContentType());
System.out.printIn("正文長度:" + connection.getContentLength());
//讀取回應正文
InputStream in = connection.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len = -l;
while((len = in.read(buff)) != -1) {
buffer.write(buff, 0, len);
}
//把位元組陣列轉換為字串
System.out.println(new String(buffer.toByteArray()));
}
}
實作協議處理框架
本節將為用戶自定義的 ECHO 協議實作處理框架,共創建了以下類:
- EchoURLConnection 類:繼承自 URLConnection 類
- EchoURLStreamHandler 類:繼承自 URLStreamHandler 類
- EchoURLStreamHandlerFactory 類:實作 URLStreamHandlerFactory 介面
- EchoContentHandler 類:繼承自 ContentHandler 類
- EchoContentHandlerFactory 類:實作 ContentHandlerFactory 介面
1. 創建 EchoURLConnection 類
EchoURLConnection 類封裝了一個 Socket,在 connect() 方法中創建與遠程服務器連接的 Socket 物件
public class EchoURLConnection extends URLConnection {
private Socket connection = null;
public final static int DEFAULT PORT = 8000;
public EchoURLConnection(URL url) {
super(url);
}
public synchronized InputStream getInputStream() throws IOException {
if(!connected) connect();
return connection.getInputStream();
}
public synchronized OutputStream getOutputStream() throws IOException {
if(!connected) connect();
return connection.getOutputStream();
}
public String getContentType() {
return "text/plain";
}
public synchronized void connect() throws IOException {
if(!connected) {
int port = url.getPort();
if(port < 0 || port > 65535) port = DEFAULT_PORT;
this.connection = new Socket(url.getHost(), port);
this.connected = true;
}
}
public synchronized void disconnect() throws IOException {
if(connected) {
//斷開連接
this.connection.close();
this.connected = false;
}
}
}
2. 創建 EchoURLStreamHandler 及工廠類
EchoURLStreamHandler 類的 openConnection() 方法負責創建一個 EchoURLConnection 物件
public class EchoURLStreamHandler extends URLStreamHandler {
public int getDefaultPort() {
return 8000;
}
protected URLConnection openConnection(URL url) throws IOException {
return new EchoURLConnection(url);
}
}
EchoURLStreamHandlerFactory 類的 createURLStreamHandle() 方法負責構造 EchoURLStreamHandler 實體
public class EchoURLStreamHandlerFactory implements URLStreamhandlerFactory {
public URLStreamHandler createURLStreamHandler(String protocol) {
if(protocol.equals("echo"))
return new EchoURLStreamHandler();
else
return null;
}
}
在客戶程式中,可以通過以下方式設定 EchoURLStreamHandlerFactory
URL.setURLStreamHandlerFactory(new EchoURLStreamHandlerFactory());
URL url=new URL("echo://localhost:8000");
3. 創建 EchoContentHandler 類及工廠類
URLConnection 類還提供了 getContent() 方法,它有兩種多載形式:
public Object getContent();
public Object getContent(Class[] classes);
第二個 getContent() 方法把服務器發送的資料優先轉換為 classes 陣列第一個元素指定的型別,如果轉換失敗,再嘗試轉換第二個元素指定的型別,以此類推
下例 HttpClient 演示處理服務器發送的資料
public class HttpClient {
public static void main(String args[]) throws IOException {
URL url = new URL("http://www,javathinker.net/hello.htm");
URlConnection connection = url.openConnection();
//接收回應結果
InputStream in = connection.getInputStream();
Class[] types = {String.class, InputStream.class};
Object obj = connection.getContent(types);
if(obj instanceof String) {
System.out.println(obj);
} else if(obj instanceof InputStream) {
in = (InputStream) obj;
FileOutputStream file = new FileOutputStream("data");
byte[] buff = new byte[1024];
int len = -l;
while((len = in.read(buff)) != -1) {
file.write(buff, 0 ,len);
}
System.out.println("正文保存完畢");
} else {
System.out.println("未知的回應正文型別");
}
}
}
EchoContentHandler 類負責處理 EchoServer 服務器發送的資料
public class EchoContentHandler extends ContentHandler {
/** 讀取服務器發送的一行資料,把它轉換為字串物件 */
public Object getContent(URLConnection connection) throws IOException {
InputStream in = connection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
return br.readLine();
}
public Object getContent(URLConnection connection, Class[] classes) throws IOException {
InputStream in = connection.getInputStream();
for(int i = 0; i < classes.length; i++) {
if(classes[i] == InputStream.class) {
return in;
} else if(classes[i] == String.class) {
return getContent(connection);
}
}
return null;
}
}
第二個 getContent() 方法依次遍歷 classes 引數中的元素,判斷元素是否為 InputSuream 型別或 String 型別,如果是,就回傳相應型別的物件,它包含了服務器發送的資料,如果 classes 引數中的元素都不是 InputStream 型別或 String 型別,就回傳 null
EchoContentHandlerFactory 類的 createContentHandler() 方法負責創建一個EchoContentHandler 物件
public class EchoContentHandlerFactory implements ContentHandlerFactory {
public ContentHandler createContentHandler(String mimetype) {
if(mimetype.equals("text/plain")) {
return new EchoContentHandler();
} else {
return null;
}
}
}
在客戶程式中,可以通過以下方式設定 EchoContentHandlerFactory
URLConnection.setContentHandlerFactory(new EchoContentHandlerFactory());
URL url = new URL("echo://localhost:8000");
EchoURLConnection connection = (EchoURLConnection)url.openConnection();
...
//讀取服務器回傳的資料,它被包裝為一個字串物件
String echoMsg = (String)connection.getContent();
4. 在 EchoClient 類運用 ECHO 協議處理框架
public class EchoClient {
public static void main(String args[]) throws IOException {
//設定URLStreamHandlerFactory
URL.setURLStreamHandlerFactory(new EchoURLStreamHandlerFactory());
//設定ContentHandlerFactory
URLConnection.setContentHandlerFactory(new EchoContentHandlerFactory());
URL url = new URL("echo://localhost:8000");
EchoURLConnection connection = (EchoURlConnection) url.openConnection();
//允許獲得輸出流
connection.setDoOutput(true);
//獲得輸出流
PrintWriter pw = new PrintWriter(connection.getOutputStream(), true);
while(true) {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String msg = br.readLine();
//向服務器發送訊息
pw.println(msg);
//讀取服務器回傳的訊息
String echoMsg = (String) connection.getContent();
System.out.println(echoMsg);
if(echoMsg.equals("echo:bye")) {
//斷開連接
connection.disconnect();
break;
}
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/554129.html
標籤:Java
下一篇:返回列表
