在無線J2ME裝置上實現超文字傳輸協議

javaprogramers發表於2005-03-26

      隨著越來越多手提電話和個人數字助理開始融入到資訊高速公路之上,從移動裝置上訪問Web站點變得越來

越重要。Java開創了消費裝置中小型的儲存容量的先河,它是用於開發手機、傳呼機及其他微型裝置應用程式

的理想語言。

  在本文中,我們將學習如何從一個J2ME客戶機上向伺服器傳送一條HTTP GET請求和一條HTTP POST

求。雖然這只是一篇探討性質的文章,但是我還是假定讀者已經熟悉JavaJ2ME,以及Java MidletsMIDP

用程式)的運作機制。我們將使用J2MEMIDP簡表,並利用SUNJ2ME的無線應用程式開發工具包編譯、配

置和測試我們的應用程式。對於HTTP伺服器,任何WWW地址都可以被訪問,但是預設時我們將使用一個簡單

Java Servlet來返回我們的HTTP請求的細節。

  如何使用J2ME客戶機向Web伺服器和類似的支援HTTP的伺服器傳送HTTP請求呢?答案就是使用可在

javax.microedition.io程式包中可找到的J2ME的網路類。本文就想具體闡述這個問題。

  本文概述

  使用J2ME設計無線網路應用程式

  .傳送一條超文字GET請求

  .傳送一條超文字POST請求

  .使用J2ME進行無線網路程式設計

  Java的網路程式設計能力是相當健壯的。Java 2標準版( J2SE)java.iojava.net程式包中定義了100多個介面

程式,類和異常。通過這些庫實現的功能是很強大的,但是這隻適用於傳統的計算機系統,這些計算機系統有

強大的CPU處理能力,快速的記憶體和持久的資料儲存,但是這些在大多數的無線裝置上是不現實的。因此,

J2ME定義了這些函式的子集,並提供了一套用於網路和檔案訪問的固定的程式包--- javax.microedition.io程式

包。由於可移動裝置種類繁多,這個程式包僅僅定義了一套介面,而為每個可移動裝置供應廠商留下了實際的

應用程式介面實現。這就在可移植性和裝置特定特徵的應用中找到了一個最佳的平衡點。

  定義在javax.microedition.io類中的抽象網路和檔案輸入輸出框架稱為通用連線框架(Generic Connection

Framework,簡稱GCF)。GCF定義了一套有關抽象化的內容來描述不同的通訊方法。最高階的抽象被稱作連

接(Connection),還宣告瞭六個介面(四個是直接的,兩個是間接的)。這七個介面就構成了J2MECLDC

的一部分,CLDC是大多數的能使用Java的無線裝置使用的配置。設計這個配置的目的就是為所有的CLDC裝置

(手提電話,雙向傳呼機,低檔的PDA等等)提供公用的網路和檔案輸入輸出能力。雖然GCF的目的是公用網路

和檔案輸入輸出框架,但是生產商並不要求實現GCF中宣告的所有的介面。有的廠家可以決定只支援socket

接,而其它的廠家可以選擇只支援基於資料包的通訊。為了促進跨越類似裝置的可移植性,MIDP規範要求所有

MIDP裝置實現HttpConnection介面。HttpConnection不是GCF的一部分,但是它是從GCF的一個介面

ContentConnection衍生出來的。我們將使用HttpConnection介面構造我們樣本應用程式。

  傳送一個HTTP GET請求

  這一節將重點解釋程式程式碼,在下一節中我們將只講述被用來傳送HTTP請求並檢索由伺服器返回的響應通

用連線框架介面和HttpConnection介面。建立MIDP使用者介面的程式程式碼見附錄。

  我們先要定義一個方法來放用於傳送HTTP GET請求的程式碼。因為這個方法中的有些操作有潛在的丟擲

IOException的可能,所以我們將把這樣的意外(exception)拋給呼叫方法。

 

 

 

public String sendHttpGet( String url ) throws IOException {
HttpConnection hcon = null;
DataInputStream dis = null;
StringBuffer message = "";
try {

 

 

 

 


  第一步是使用Connector類開啟一個到伺服器的連線,這是GCF的關鍵。我們將把這個連線強制轉換為需

  第一步是使用Connector類開啟一個到伺服器的連線,這是GCF的關鍵。我們將把這個連線強制轉換為需

要的型別,在本例中為HttpConnection型別。

 

 

 

hcon = ( HttpConnection ) Connector.open( url );

 

 

 

 


  接下來,我們得到HttpConnection上的一個DataInputStream,允許我們一個字元一個字元的讀取伺服器

  接下來,我們得到HttpConnection上的一個DataInputStream,允許我們一個字元一個字元的讀取伺服器

的響應資料。

 

 

 

dis = new DataInputStream( hcon.openInputStream() );

 

 

 

 


  使用DataInputStreamread ()方法,伺服器響應的每個字元都被集中起來放入StringBuffer物件。

 

 

 

int ch;
while ( ( ch = dis.read() ) != -1 ) {
message = message.append( ( char ) ch );
}

 

 

 

 



  最後,連線物件被淨空以儲存資源,而資訊從這個方法中返回。

 

 

 

} finally {
if ( hcon != null ) hcon.close();
if ( dis != null ) dis.close();
}//
結束try/finally程式碼段
return message.toString();
}//
結束 sendGetRequest( String )

 

 

 

 

如何傳送一個HTTP POST請求

  你可以想象,傳送一個HTTP POST請求的處理過程其實與傳送一個GET請求非常地類似。我們將修改一個

現有命令,新增少量的新的命令,並新增一個來自通用連線框架的附加的物件和一個附加的StringBuffer物件把

POST請求體重的內容傳送到伺服器中。剩下的命令將保持不變。

  複製我們剛才建立的sendHttpGet()方法,把它貼上進同一個類檔案,改名為sendHttpPost() 現在,我

們將修改這個新方法來傳送一個HTTP POST請求到伺服器。 在方法的頂部新增兩個新的變數說明。 宣告一個

型別為DataOutputStream的變數和另一個String型別的變數。 我們將使用DataOutputStream物件把存在於字

符串變數中的POST請求體傳送到伺服器中。

 

 

 

DataOutputStream dos = null;
String requestBody = null;

 

 

 

 


  修改connector.open()命令包含另一個引數,指出連線將允許客戶端可以通過連線在伺服器上讀和寫。

 

 

 

hcon = ( HttpConnection ) Connector.open( url, Connector.READ_WRITE );

 

 

 

 


  設定HttpConnection物件使用的請求方法為POST(預設的方法是GET)。

 

 

 

hcon.setRequestMethod( HttpConnection.POST );

 

 

 

 


  得到一個用於現有的HTTP連線的DataOutputStream物件。

 

 

 

dos = hc.openDataOutputStream();

 

 

 

 


  宣告一個位元組陣列並通過檢索一個來自requestBody字串的位元組陣列初始化。 然後把DataOutputStream

  宣告一個位元組陣列並通過檢索一個來自requestBody字串的位元組陣列初始化。 然後把DataOutputStream

的緩衝寫入位元組陣列內。

 

 

 

byte[] byteRequest = requestBody.getBytes();
for( int i = 0; i < byteRequest.length; i++ ) {
dos.writeByte(byteRequest[i]);
}//
結束for( int i = 0; i < byteRequest.length; i++ )

dos.flush(); //
包含本句,在某些設被上將可能會產生不可預期的結果

 

 

 

 


  呼叫flush ()方法的意圖是傳送已經寫入的資料到DataOutputStream的伺服器的緩衝區中。 在某些電話

  呼叫flush ()方法的意圖是傳送已經寫入的資料到DataOutputStream的伺服器的緩衝區中。 在某些電話

上,這個操作工作正常,在其他的電話上,它導致HTTP請求的Transfer - Encoding被設定為" chunked ",有一

些隨機字元被放到請求本身的前面和後面。那又怎樣處理這個問題呢?這個方法呼叫實際上是根本不需要的。

在接下來的一行中,伺服器連線開啟(通過openInputStream ()),將自動輸入緩衝區。因此,你最好不要調

用緩衝區的flush()方法。這個方法其餘的部分保持不變,除了DataOutputStream物件必須在finally{}語句塊中關

閉。

 

 

 

} finally {
if ( hc != null ) hc.close();

if ( dis != null ) dis.close();

if ( dos != null ) dis.close();
}//
結束 try/finally

 

 

 

 


  這就是所有的程式程式碼!並請參見本文後附帶的程式程式碼。

  隨著可以使用國際網際網路絡和支援網路的無線裝置日益的增多普及,JavaJ2ME的重要性也在不斷的變

  這就是所有的程式程式碼!並請參見本文後附帶的程式程式碼。

  隨著可以使用國際網際網路絡和支援網路的無線裝置日益的增多普及,JavaJ2ME的重要性也在不斷的變

大。因為HTTP協議是當前僅有的,被所有的遵從MIDP規範的裝置支援的網路協議,它也是用於開發無線網路

應用程式的最好的候選者。

  在本文中,我們探究了無線網路程式設計的基本結構和幾個核心問題,我們看了如何呼叫兩個最常用的HTTP

求方法:GETPOSTJ2ME仍然在它的發展初期,並且無線裝置也即將得到大面積的普及。所以,所有有志

投身於無線網路程式設計中的開發者們將得到大展拳腳的好機會。

  附錄:

 

 

 

/*
* HttpMidlet.java
*/
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import java.io.*;


public class HttpMidlet extends MIDlet implements CommandListener {
//
使用預設的URL。使用者可以從圖形使用者介面改變這個值
private static String defaultURL = "http://localhost:8080/test/servlet/EchoServlet";

//
MIDP 顯示
private Display myDisplay = null;

//
輸入URL的圖形使用者介面元件
private Form requestScreen;
private TextField requestField;

//
用於提交請求的圖形使用者介面元件
private List list;
private String[] menuItems;

//
用於顯示伺服器響應的圖形使用者介面元件
private Form resultScreen;
private StringItem resultField;

//
用於requestScreen"send"按鈕
Command sendCommand;
//
用於requestScreen"exit"按鈕
Command exitCommand;
//
用於requestScreen"back"按鈕
Command backCommand;

public HttpMidlet(){
//
初始化圖形使用者介面元件
myDisplay = Display.getDisplay( this );
sendCommand = new Command( "SEND", Command.OK, 1 );
exitCommand = new Command( "EXIT", Command.OK, 1 );
backCommand = new Command( "BACK", Command.OK, 1 );

//
顯示請求的URL
requestScreen = new Form( "Type in a URL:" );
requestField = new TextField( null, defaultURL, 100, TextField.URL );
requestScreen.append( requestField );
requestScreen.addCommand( sendCommand );
requestScreen.addCommand( exitCommand );
requestScreen.setCommandListener( this );

//
選擇想要的HTTP請求方法
menuItems = new String[] {"GET Request", "POST Request"};
list = new List( "Select an HTTP method:", List.IMPLICIT, menuItems, null );
list.setCommandListener( this );

//
先是從伺服器上收到的資訊
resultScreen = new Form( "Server Response:" );
resultScreen.addCommand( backCommand );
resultScreen.setCommandListener( this );

}//
結束HttpMidlet()

public void startApp() {
myDisplay.setCurrent( requestScreen );
}//
結束 startApp()

public void commandAction( Command com, Displayable disp ) {
//
當使用者點選"send"按鈕
if ( com == sendCommand ) {
myDisplay.setCurrent( list );
} else if ( com == backCommand ) {
requestField.setString( defaultURL );
myDisplay.setCurrent( requestScreen );
} else if ( com == exitCommand ) {
destroyApp( true );
notifyDestroyed();
}//
結束 if ( com == sendCommand )

if ( disp == list && com == List.SELECT_COMMAND ) {

String result;

if ( list.getSelectedIndex() == 0 ) //
傳送一個 GET 請求到伺服器
result = sendHttpGet( requestField.getString() );
else //
傳送一個 POST 請求到伺服器
result = sendHttpPost( requestField.getString() );

resultField = new StringItem( null, result );
resultScreen.append( resultField );
myDisplay.setCurrent( resultScreen );
}//
結束if ( dis == list && com == List.SELECT_COMMAND )
}//
結束 commandAction( Command, Displayable )

private String sendHttpGet( String url )
{
HttpConnection hcon = null;
DataInputStream dis = null;
StringBuffer responseMessage = new StringBuffer();

try {
//
使用READ許可權的標準的 HttpConnection
hcon = ( HttpConnection )Connector.open( url );

//
HttpConnection取得一個 DataInputStream
dis = new DataInputStream( hcon.openInputStream() );

//
從伺服器上取回響應
int ch;
while ( ( ch = dis.read() ) != -1 ) {
responseMessage.append( (char) ch );
}//
結束while ( ( ch = dis.read() ) != -1 )
}
catch( Exception e )
{
e.printStackTrace();
responseMessage.append( "ERROR" );
} finally {
try {
if ( hcon != null ) hcon.close();
if ( dis != null ) dis.close();
} catch ( IOException ioe ) {
ioe.printStackTrace();
}//
結束try/catch
}//
結束try/catch/finally
return responseMessage.toString();
}//
結束sendHttpGet( String )

private String sendHttpPost( String url )
{
HttpConnection hcon = null;
DataInputStream dis = null;
DataOutputStream dos = null;
StringBuffer responseMessage = new StringBuffer();
//
請求體
String requeststring = "This is a POST.";

try {
//
使用讀寫許可權的 HttpConnection
hcon = ( HttpConnection )Connector.open( url, Connector.READ_WRITE );

//
設定請求方法為POST
hcon.setRequestMethod( HttpConnection.POST );

//
取得傳送請求字串的DataOutputStream
dos = hcon.openDataOutputStream();
byte[] request_body = requeststring.getBytes();

//
傳送請求字串到伺服器
for( int i = 0; i < request_body.length; i++ ) {
dos.writeByte( request_body[i] );
}//
結束 for( int i = 0; i < request_body.length; i++ )

//
取得做為接收伺服器響應的DataInputStream
dis = new DataInputStream( hcon.openInputStream() );

//
從伺服器上取回響應
int ch;
while( ( ch = dis.read() ) != -1 ) {
responseMessage.append( (char)ch );
}//
結束while( ( ch = dis.read() ) != -1 ) {
}
catch( Exception e )
{
e.printStackTrace();
responseMessage.append( "ERROR" );
}
finally {
//
釋放輸入輸出流和HTTP連線
try {
if( hcon != null ) hcon.close();
if( dis != null ) dis.close();
if( dos != null ) dos.close();
} catch ( IOException ioe ) {
ioe.printStackTrace();
}//
結束try/catch
}//
結束try/catch/finally
return responseMessage.toString();
}//
結束sendHttpPost( String )

public void pauseApp() {
}//
結束pauseApp()

public void destroyApp( boolean unconditional ) {
myDisplay = null;
requestScreen = null;
requestField = null;
resultScreen = null;
resultField = null;
}//
結束 destroyApp( boolean )
}//
結束HttpMidlet

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相關文章