Java的網路程式設計

By | 2016-05-11

一個只能夠在手機上運作的應用程式是不夠的,與外部溝通、互動是現今常見的應用,一個應用程式能夠連線、對戰、同盟、成績與級數,都需要網路程式設計。本篇將介紹Java的網路程式設計方法與類別,使用Java類別進行HTTP協定實作。

輸出入套件

Java 語言提供許多類別做為 I/O(Input/Output)輸入與輸出,這些類別都放置在 java.io 類別庫中,讀者可以將輸出入的類別想像成「管路」,不同的管路可供處理不同的需求功能,而某些類別還可以串接起來,成為更有效率的處理機制。

輸出入類別

  • InputStream

java.io.InputStream 是以 byte 為單位的輸入串流(stream)類別,用來處理輸入的資料通道,事實上 InputStream 是「抽象類別(abstract class)」,它訂立了一個類別群組的方法規格,讓同類型的類別繼承它,以遵循既定的規範來設計該群組的其他的類別。

  • OutputStream

java.io.OutputStream 是 以 byte為單位的輸出串流(stream)類 別, 用 來 處 理 輸 出 的 資料 通 道,OutputStream 和InputStream一樣,都是「抽象類別(abstract class)」。

  • Reader

java.io.Reader 是以 char 為單位的輸入串流(stream)類別,用來處理輸入的資料通道,Reader 是「抽象類別(abstract class)」,定義了輸入類別應使用的方法規範。最常用的類別是 BufferedReader,它提供了具緩衝效果的輸出功能,其中最常用的方法是 readLine(),可有效率地一次讀取一行資料。

  • Writer

java.io.Writer 是以 char 為單位的輸出串流(stream)類別,用來處理輸出的資料通道,Writer 是「抽象類別(abstract class)」,定義了輸出類別應使用的方法規範。

byte轉換為char

在處理輸出入或網路之類的程式開發過程,有時會遇到只能取得 byte 串流物件的情形,這類情形尤其會在網路程式設計時出現。讀者經過本章前面幾節的說明瞭解到 byte 串流的方法所提供的功能有限,當想要較有效率的處理資料流時,如讀取一行資料,得到字串時,可將 byte 串流(stream)轉換為 char 類型的 Reader 或 Writer。

InputStreamReader 與 OutputStreamWriter 類別屬於「由 byte 轉成 char」的中繼接頭。舉例來說,在處理資料輸入時,資料來源的 InputStream 像是小口徑的水管,而我們希望能將小水管轉換為大口徑的 BufferedReader 這類大水管時,可利用 InputStreamReader 這類轉換器,將 InputStream 轉換為 Reader,如下圖:

io1

假 設 資 料 來 源 是 FileInputStream(byte 串 流 ), 可 將 串 流 物 件 給 予InputStreamReader 的建構子,得到 Reader 物件:

再 將 InputStreamReader 物 件(Reader) 交 給 BufferedReader 類 別 的 建 構子,產生較有效率的 BufferedReader 物件 in:

這是常見的轉換方法,雖然多出二行程式碼,但後續的處理也比單純使用InputStream 來處理資料方便得多。

Java網路套件

Java 網路相關的類別都配置於 java.net 套件中,利用這些 JDK 所提供的類別,開發人員得以不用花時間實作或撰寫基礎的網路類別,就可以快速設計出具有網路、通訊功能的程式。

  • Socket類別

java.net.Socket 類別最常用來建立 Client/Server 型的連線,為程式建立與遠端主機的 TCP 連線,建立連線後就可以利用輸出入串流物件進行資料傳輸,因此 TCP 程式設計是先利用 Socket 類別,再利用串流物件。

1. 建立Socket物件

最常使用的建構子是給予主機名稱字串與遠端主機的埠號(port),由於主機名稱不一定正確,或是遠端主機可能有無法連線的情況,因此使用 Socket 建構子時,必須為其處理 UnknownHostException 與 IOException 例外,當連線失敗時將會顯示「主機連線失敗」,如下以 try…catch 處理之:

2. 取得輸出入串流

當 Socket 物件成功建立後,客戶端(程式)與伺服器端(遠端主機)可開始進行資料的傳輸,此時要利用 java.io 套件庫內的串流類別(Stream),如簡單的 InputStream/OutputStream 或支援亞洲字元的 Reader/Writer 類別。Socket 類別可比喻為插頭,而串流類別則是由插頭衍生出來的電線。

由 Socket 物件取得串流物件的方法為 getInputStream 與 getOutputStream,範例如下:

3. 傳輸資料

取得輸出入串流物件後,便可利用串流物件提供的方法,進行資料的接收與傳送,資料接收以 InputStream 的 read() 方法,另以 OutputStream 的 write()方法將資料傳送至遠端主機。

接收遠端主機傳來的一個 byte 值,收到的資料以整數 int 的方式:

傳送一個 byte 值,以整數 int 資料型態傳送出去,並利用 flush() 方法確認資料不會留存於本地端的緩衝區,送達到遠端主機:

4. 關閉連線

在設計網路連線相關程式時,最好利用串流物件與通道物件的 close() 方法,主動地關閉其資源並斷線,以免遠端主機花費多餘的資源在等待斷線訊號。呼叫 InputStream 物件 in 的方法 close(),關閉輸入串流:

呼叫 OutputStream 物件 out 的方法 close(),關閉輸出串流:

呼叫 Socket 物件 ss 的方法 close(),關閉本機與遠端主機的連線:

HTTP協定

由於網際網路的發達,HTTP 通訊協定廣泛被應用在網路環境,網頁提供了最快速與最方便取得資訊的管道,除了常見的瀏覽器存取網頁伺服器的資料外,程式開發人員需要設計出也能存取網頁資訊的應用程式。存取網頁功能包括了讀取特定網址資源與送出參數資料至網頁伺服器,Java 網路套件中,提供了許多類別可以快速達成這些工作,本章將介紹 HTTP 通訊協定的規範與知識,進而學習如何操作這些 HTTP 協定相關的類別,達成常見的需求與功能。

HTTP 協定是由客戶端與伺服器建立一個 TCP 連線之後,客戶端發出HTTP 請求與伺服器建立可靠的連線,伺服器在收到請求後,將一個狀態資訊發送回客戶端,此資訊稱為回應。回應可能是一個客戶端請求的檔案或是錯誤訊息。

HTTP要求與回應

HTTP 通訊協定是由 WWW 聯盟(World wide web Consortium)與 IETF 所協調制定的,由 RFC 2616 號要求意見書訂定出初版的國際標準。HTTP 協定是以客戶端發出要求(或請求,request),而伺服端針對該要求的內容進行處理後再送出回應(response)為溝通方式。一個 request 封包皆應該對應一個 response封包,實作 HTTP 客戶端的通常是網頁瀏覽器,實作 HTTP 伺服端的是網頁伺服器。

HTTP 通訊協定使用 HTTP 標準封包,而封包有其規範,HTTP 封包內含封包標頭與 HTTP 訊息等資料,而標頭資料定義了數十個欄位,每個欄位有其意義。封包標頭資料是提供給伺服端與客戶端進行初期溝通的,標頭資訊就像信封上的資訊,擁有寄件人、收件人、住址等資料的集合,讓對方可以不打開信件內容即可得到基本的資訊。

客戶端可以利用 request 封包送出資料至伺服器端,HTTP 協定中定義了 12種方法可供資料傳送,分別為 (GET, POST, HEAD, PUT, PATCH, COPY, MOVE, DELETE, LINK, UNLINK, OPTION),常使用的方法為 GET 與 POST,POST 方法主要目的是提供客戶端傳送資訊至伺服器,因此 POST 方法封包內的資料區會放置要傳送至伺服器的資料,例如欲查詢的帳號或產品編號等,待伺服器收到封包後,解讀該封包內的資料區內容,得到查詢的關鍵字資料。下圖是 POST方法的示意圖:

http-post

GET 方法簡單地向伺服器要求一個資源,所有的資訊都在封包的標頭裏,因此封包內的資料區並無任何資料,但是 GET 方法仍可以將欲傳送的資料放在標頭的網址內容的後段,伺服器亦會解讀網址欄位之後,得到客戶端所傳遞的資料。下圖是 GET 方法的示意圖:

http-get

在使用以上兩種方法時應注意到 GET 方法傳遞資料時,有長度的限制而無法傳遞大量的資料(例如上傳檔案等),而且有些瀏覽器會因為兩次傳送的要求封包的網址是一樣的,以快取(cache)直接回應給使用者,而不發出第兩次的要求封包。

URLConnection類別

java.net.URLConnection 類 別 提 供 程 式 本 身 與 某 個 網 頁 資 源 的 連 線, 再由此連線取得輸出入資源流,再以輸入資料流物件(InputStream)讀取該遠端資源。而 URLConnection 物件並不由建構子來產生的,而是利用 java.net.URL 類 別 的 方 法 openConnection() 得 到 連 線 物 件, 而 URL 類 別 的 建 構子 與 openConnection()方法皆需要處理例外(MalformedURLException 與IOException):

讀取網頁資料

傳送參數-GET

在常見的網頁系統應用中,伺服器經常需要瀏覽器(客戶端)的使用者提供資料,例如使用者點擊了其中一項商品時,會傳送至網頁伺服器該商品的編號,伺服器接收到客戶端傳來的資料後,再查詢資料庫中符合該商品編號的資訊。

在 HTTP 標準中,客戶端發出的要求(request)封包的標頭中可以夾帶參數,一起送入伺服器,此方法可稱為「GET」,另一種則是利用客戶端的輸出資料流傳送參數資料至伺服器端,可稱為「POST」。GET 方法較不安全,敏感性的參數不適合使用 GET 方法傳送,參數資料是外顯在網址裏,例如:

http://atm.snpy.org/get?id=119

網址的問號之後的即是參數與該參數的值 id=119,若傳輸的是身份證字號或密碼,則不適合以 GET 傳送。要注意的是,參數的編碼必需採用 UTF-8 格式才能被伺服器端正確地讀取,參數傳送的規範是:

網址 ? 參數 1= 值 & 參數 2= 值 & 參數 3= 值 …

筆者在網頁伺服器準備了一個網頁程式,它會將傳送的參數(id與func)顯示給使用者,如果在瀏覽器上輸入網址,將得到以下的結果:

http-get-2

若是以程式連至該網址,可直接將參數資料依照規格加在網址後,即可以GET 方法送出參數,範例程式如下:

執行結果伺服器端的get.php收到參數後,動態產生的網頁原始碼顯示如下

讀取資料的編碼

由於網址的編碼標準為 UTF-8,因此在網址加入參數傳送時的值應該經過編碼(Encoding),例如當參數 id 的內容「id= 王小明」,就得先將參數的值「王小明」轉換為 UTF-8 編碼,再傳送出去。java.net.URLEncoding 類別的靜態方法 encode 可將字串轉換為目的編碼格式,使用此方法必需處理 java.io.UnsupportedEncodingException。

以下是將中文字串轉換為為 UTF-8 格式(第3行),再以 HTTP GET 方法送至伺服器,再由伺服端傳回參數資料的網頁:

執行結果伺服器端的get程式收到參數後,動態產生的網頁原始碼顯示如下

參數值的編碼

由於網址的編碼標準為 UTF-8,因此在網址加入參數傳送時的值應該經過編碼(Encoding),例如當參數 id 的內容「id= 王小明」,就得先將參數的值「王小明」轉換為 UTF-8 編碼,再傳送出去。java.net.URLEncoding 類別的靜態方法 encode 可將字串轉換為目的編碼格式,使用此方法必需處理 java.io.UnsupportedEncodingException。

以下是將中文字串轉換為為 UTF-8 格式(第 3 行),再以 HTTP GET 方法送至伺服器,再由伺服端傳回參數資料的網頁:

傳送參數-POST

送出資料至網頁伺服器的另一個方法是 POST,利用 URLConnection 類別的方法 getOutputStream 可取得輸出資料流,再以 OutputStream 的 write 方法寫出資料至伺服器端。因為連線預設是不輸出資料的,所以在輸出資料之前,必需先以方法 setDoOutput(true) 允許連線時送出資料,如下方程式碼第 4 行:

第2-3行,建立連線。

第4行,設定連線為允許輸出。

第5-6行,由conn連線物件取得輸出資料流,再轉為Writer類別。

第7行,先送出參數資料。

第12-16行,依序讀取傳入的HTML檔案內容。

觀察執行結果中,以 POST 方法送出的參數 id 與 func 皆被伺服器程式成功接收:

HttpURLConnection類別

URLConnection 類別可用於 http、ftp、file 等 URL 可以表示的網路資源,但特別針對 HTTP 協定設計的連線類別則是 HttpURLConnection 類別,它是URLConnection 的子類別,除了具有父類別的屬性與方法外,它還提供了專門適用 HTTP 協定的方法,如設定資料傳遞要使用 get 或 post、是否連線時使用代理器(Proxy)等。

  • 物件的產生

HttpURLConnection 的物件產生方式不是由建構子來達成,而是將它的父類別物件轉型,而得到物件。也就是說,如果一個網址物件(URL)其內容是http 協定,由 URL 物件的 openConnection() 方法取得的 URLConnection 物件就可以再轉型成 HttpURLConnection 物件,如下程式片段:

此程式片段可以簡化為:

若是網址內不是 http 協定而仍進行轉型為 HttpURLConnection 時,將在執行時拋出轉型失敗的例外,例如下方程式片段中使用的是 ftp 協定:

執行時出現例外:

  • 常用方法

  1. String getRequestMethod():取得本連線的要求(request)方式,大都與參數的傳遞有關,回傳字串格式。
  2. void setRequestMethod(String method):設定本連線的要求方式,傳入字串進行設定,通常都在初始階段使用。
  3. int getResponseCode():得到回應狀態碼,整數型態。
  4. String getResponseMessage():得到回應狀態訊息,字串型態。
  5. void setFollowRedirects(boolean set):設定是否隨HTTP規範的重導狀態碼3xx,進行頁面重導,以true或false設定。
  6. boolean getFollowRedirects():得到本連線是否啟用重導功能,回傳值為boolean值。

 

相關文章:

Category: Android Java 標籤:, , , , , ,

About Hank Tom

專長為程式語言、雲端服務開發,Linux系統管理, 任職:利拓科技 技術長,海林行動科技 技術總監 輔仁大學 兼任助理教授 ,為 Android高效入門>深度學習、CentOS 7建置、管理與伺服器架設實戰、Java網路程式設計、雲端網頁程式設計-Google App Engine應用實作 等書作者

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *