主頁 > 後端開發 > Java 網路編程 —— 客戶端協議處理框架

Java 網路編程 —— 客戶端協議處理框架

2023-06-03 07:55:42 後端開發

概述

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 實體的流程如下:

  1. 如果在 URL 快取已經存在這樣的 URLStreamHandler 實體,則無須再創建,否則繼續執行下一步

  2. 如果程式通過 URL 類的靜態 setURLStreamHandlerFactory() 方法設定了 URLStreamHandlerFactory 介面的具體實作類,那么就通過這個工廠類的 createURLStreamHandler() 方法來構造 URLStreamHandler 實體,否則繼續執行下一步

  3. 根據系統屬性 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 類,如果以上操作都失敗,那么繼續執行下一步

  4. 試圖實體化位于 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():回傳回應正文的型別,如果無法獲取回應正文的型別就回傳 null
  • getContentLength():回傳回應正文的長度,如果無法獲取回應正文的長度,就回傳 -1
  • getContentEncoding():回傳回應正文的編碼型別,如果無法獲取回應正文的編碼型別,就回傳 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

上一篇:40基于java的美食菜譜分享系統設計與實作

下一篇:返回列表

標籤雲
其他(160206) Python(38196) JavaScript(25473) Java(18184) C(15236) 區塊鏈(8269) C#(7972) AI(7469) 爪哇(7425) MySQL(7223) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5873) 数组(5741) R(5409) Linux(5346) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4581) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2434) ASP.NET(2403) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1980) 功能(1967) Web開發(1951) HtmlCss(1951) C++(1928) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1879) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Java 網路編程 —— 客戶端協議處理框架

    ## 概述 Java 對客戶程式的通信程序進行了抽象,提供了通用的協議處理框架,該框架封裝了 Socket,主要包括以下類: - URL 類:統一資源定位符,表示客戶程式要訪問的遠程資源 - URLConnection 類:表示客戶程式與遠程服務器的連接,客戶程式可以從 URLConnection ......

    uj5u.com 2023-06-03 07:55:42 more
  • 40基于java的美食菜譜分享系統設計與實作

    基于java的美食菜譜分享系統設計與實作,餐飲分享平臺設計與實作,可用于美食在線分享平臺,作為世界各地愛好美食的人們的橋梁,為其創造一個氛圍好的平臺,促進美食世界的文化交流。該系統是一個供商家或者個人推薦美食的網站,網站不支持交易僅供分享。 ......

    uj5u.com 2023-06-03 07:55:38 more
  • JAVA Socket編程

    aliases: [] tags : " " summary: [基于TCP/IP和UDP協議的Java Socket網路通信編程] author : [yaenli] notekey: [20230512-143738] # Socket 網路模型 Socket編程是在TCP/IP、UDP協議上的 ......

    uj5u.com 2023-06-03 07:50:18 more
  • elment UI + EasyExcel 實作 匯入

    前端組件 <hd-flex> <el-dialog v-model="isUploadDialog" width="50%" lock-scroll=false> <el-upload class="upload-demo" drag :action="url" :on-success="succe ......

    uj5u.com 2023-06-03 07:45:08 more
  • 什么是 Apache ActiveMQ?

    # 什么是 ActiveMQ? ActiveMQ 是一種流行的訊息傳遞服務,可促進企業系統中大規模的不同資料。在本 ActiveMQ 教程中,我們概述了 ActiveMQ、它的優點、它的作業原理以及何時應該使用它。 什么是 ActiveMQ? ActiveMQ 是一種流行的開源訊息傳遞服務,它構建在 ......

    uj5u.com 2023-06-03 07:45:03 more
  • 39基于java的酒店管理系統設計與實作

    基于java的酒店管理系統設計與實作,酒店訂票系統,酒店預訂系統,酒店資訊管理系統,app訂房系統設計與實作; ......

    uj5u.com 2023-06-03 07:44:58 more
  • 談談一致性哈希演算法

    一致性哈希演算法是1997年由麻省理工的幾位學者提出的用于解決分布式快取中的熱點問題。大家有沒有發現,我們之前介紹的例如快排之類的演算法是更早的六七十年代,此時分布式還沒有發展起來,大家往往還在提高單機性能。但是九十年代開始,逐漸需要用分布式集群來解決大型問題,相應的演算法研究也就應運而生。在說到一致性哈 ......

    uj5u.com 2023-06-03 07:44:44 more
  • 38基于java的在線商城設計與實作

    基于java的在線商城設計與實作,在線購物平臺,校園購物商城,商品銷售平臺,基于Java的電商平臺;電商平臺,買家和賣家可以在此平臺上進行銷售和交易,節約了大量的線下時間成本,購物車的功能,校園交易平臺等等; ......

    uj5u.com 2023-06-03 07:44:35 more
  • FastJson轉Java對像欄位不區分大小寫

    昨天遇到引數key大小寫不一致導致校驗簽名失敗的問題,查了很長時間才找到原因。看了一下FastJson原始碼,發現JSON.toObject中轉換成物件的時候會忽略大小寫。 所以,當使用了JSON.toObject將json轉成Java物件后,再用JSON.toObject轉成json,key值就變了 ......

    uj5u.com 2023-06-03 07:44:31 more
  • 簡述泛型的基本使用和作用

    # 前言 在上一篇文章中,給大家講解了泛型的概念、作用、使用場景,以及泛型集合、泛型介面和泛型類的用法,但受限于篇幅,并沒有把泛型的內容講解完畢。所以今天我們會繼續學習泛型方法、泛型擦除,以及通配符等的內容,希望大家繼續做好學習的準備哦。 *** 全文大約【**4600】** 字,不說廢話,只講可以 ......

    uj5u.com 2023-06-03 07:44:27 more