[J2ME] 無線網絡開發
無線網絡開發MIDP提供了一組通用的網絡開發接口,用來針對不同的無線網絡應用可以采取不同的開發接口。基于CLDC的網絡支持是由統一網絡連接框架(Generic Connection Frameword, 簡稱GCF)定義的。其相關類、接口以及異常都放置在javax.microedtion.io包中。
在CLDC之中定義了七個接口,它們分別是:
1. Connection
2. StreamConnectionNotifier
3. InputConnection
4. OutputConnection
5. DatagramConnection
6. StreamConnection
7. ContentConnection
直接繼承自Connection的有四個類:StreamConnectionNotifier、DatagramConnection、InputConnection和OutputStream。其中,StreamConnectionNotifier只要提供Socket開發接口。DatagramConnection提供UDP開發接口。由于需要對網絡傳輸的數據輸入和輸出進行控制,因此提供了InputConnection和OutputConnection開發接口。
通用的網絡開發接口都是繼承自Connection接口。
GCF繼承體系
MIDP2.0中,新添加了3個網絡開發接口:
?? javax.microedition.io.SocketConnection 負責TCP/IP方面的網絡開發
?? javax.microedition.io.ServerSocketConnection 負責TCP/IP方面的網絡開發
?? javax.microedition.io.UDPDatagramConnection 負責UDP方面的開發
網絡開發中,最重要的一個連接類是HttpConnection接口, 其繼承自ContentConnection。HttpConnection中定義了大量的基本聯網和獲取數據的操作。Http聯網功能是MIDP規范中要求廠商必須支持的連接方式,而其它方式,則取決于廠商與網絡服務商的設備支持情況。
也就是說,只有HTTP傳輸協議才是能夠在各家平臺上使用的對外溝通的方式。
GCF規范提出不管使用何種網絡或者本地文件的連接方式,所有的連接都使用Connector的open(URL)方法創建一個新的網絡連接(Connection.open(url)方法將返回一個Connection對象):
URL的格式如下;
<協議>://<用戶名>:<密碼>@<資源所在主機>:<端口號>/<資源路徑>;<參數>
協議如: http、https、socket方式等。資源所在主機代表資源所在位置的主機名稱或者IP地址。資源路徑的格式和使用的協議有關,有些協議會有額外的參數需要設定。
1) 創建Http連接
Connector.open(“http://www.sun.com.cn”);
2) 創建Socket連接
Connector.open(“socket://127.0.0.1:8080”);
3) 創建Datagram連接
Connector.open(“datagram://www.sun.com.cn:9000”);
4) 創建本地文件連接
Connector.open(“file:/input.txt”);
Connector.open()方法有3個重載的方法:
public static Connection open(String name)
public static Connection open(String name, int mode)
public static Connection open(String name, int mode, Boolean timeout)
參數:
name指定網絡地址
mode指定網絡連接的方式,分為讀、寫和讀寫三種方式
timeout指定了網絡連接超時的時間。如果網絡連接超時,則拋出異常
一、 MIDP開發HTTP程序
使用Connector.open()方法獲得一個網絡連接以后,就可以調用相應的Connection類的方法獲得需要的信息。
利用Connection的openInputStream()方法可以獲得網絡傳輸過來的流數據。
(一) 使用StreamConnection接口
StreamConnection接口繼承了InputConnection和OutputConnection接口,因此創建一個StreamConnection對象可以返回一個獲得網絡傳輸數據的流對象,可以使用返回的流對象對數據進行整理,再選取需要的數據顯示在屏幕上。
流程如下:
1. 創建一個StreamConnection類型的網絡連接
StreamConnection sc = null;
sc = (StreamConnection)Connector.open(url);
2. 創建了StreamConnection類型的網絡連接以后,可以調用openInputStream()方法返回一個InputStream對象,通過該對象獲得流數據。也可以使用openDataInputStream()方法返回一個DataInputStream對象。
InputStream is = null;
is = sc.openInputStream();
3. 通過數據對象操作獲得的數據:
StringBuffer sb = new StringBuffer();
int ch;
while ((ch = is.read()) != -1)
{
sb.append((char)ch);
}
System.out.println(sb.toString());
4. 操作數據完成,關閉連接對象。
is.close();
練習:用StreamConnection實現讀取網頁內容。
(二) 使用HttpConnection接口
使用HttpConnection接口的開發流程與使用StreamConnection接口基本一樣。由于HttpConnection接口繼承了StreamConnection接口,不但擁有StreamConnection接口的功能,還擴展了許多功能。
例:
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import java.io.*;
/**
*
* @author Allan
* @version
*/
public class HttpConnectionMidlet extends MIDlet
{
private Display display;
public HttpConnectionMidlet()
{
display = Display.getDisplay(this);
}
public void startApp()
{
TextBox tb = new TextBox("Browser", "", 10000, TextField.ANY);
String url = " HttpConnection hc = null;
try
{
hc = (HttpConnection)Connector.open(url);
InputStream is = hc.openInputStream();
StringBuffer sb = new StringBuffer();
int ch;
while ((ch = is.read()) != -1)
{
sb.append((char)ch);
}
//System.out.println(sb.toString());
tb.setString(sb.toString());
display.setCurrent(tb);
}
catch (Exception e)
{
}
finally
{
if (hc != null)
{
try
{
hc.close();
}
catch (Exception e)
{
}
}
}
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}
(三) 顯示服務器信息
HttpConnection接口提供了多個獲得服務器信息的方法。
獲得了一個HttpConnection的連接對象以后,不需要使用openInputStream()方法就可以直接返回服務器信息(訪問資源的信息)。
例:
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import java.io.*;
/**
*
* @author Administrator
* @version
*/
public class ServerInfoMidlet extends MIDlet
{
private Display display;
public ServerInfoMidlet()
{
display = Display.getDisplay(this);
}
public void startApp()
{
Form f = new Form("");
String url = " HttpConnection hc = null;
StringBuffer sb = new StringBuffer();
try
{
hc = (HttpConnection)Connector.open(url);
//要顯示的服務器的信息
sb.append("Host: " + hc.getHost());
sb.append("\nPost: " + hc.getPort());
sb.append("\nDate: " + hc.getDate());
sb.append("\nProtocol: " + hc.getProtocol());
sb.append("\nFile: " + hc.getFile());
sb.append("\nType: " + hc.getType());
//在屏幕上顯示信息
f.append(sb.toString());
display.setCurrent(f);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if (hc != null)
{
try
{
hc.close();
}
catch (Exception e)
{
}
}
}
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}
(四) HTTP連接方式的簡單應用
1. 下載觀看圖片
從網絡上下載圖片是通過二進制數據傳輸的,因此可以使用Image的重載方法:
Image createImage(byte[] imageData, int imageOffset, int imageLength)
Image createImage(InputStream stream) MIDP2.0提供
將圖片的數據存儲到字節數組imageData或者輸入流對象stream中,就可以使用createImage()方法將它們轉換為Image對象,以便在屏幕上顯示出來。
使用方法1:
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import java.io.*;
/**
*
* @author Allan
*/
public class MyForm extends Form implements CommandListener, Runnable
{
private TextField tfUrl;
//下載的圖像
private ImageItem iiImage;
public MyForm()
{
super("");
tfUrl = new TextField("圖片地址", "", 100, TextField.URL);
iiImage = new ImageItem("Image", null, Item.LAYOUT_CENTER, "圖片位置");
iiImage.setPreferredSize(getWidth(), 100);
append(tfUrl);
append(iiImage);
addCommand(new Command("下載", Command.OK, 1));
setCommandListener(this);
}
public Image getImage(String url)
{
HttpConnection hc = null;
//供返回的image對象
Image image = null;
try
{
hc = (HttpConnection)Connector.open(url);
DataInputStream dis = hc.openDataInputStream();
byte[] imageData;
int len = (int)hc.getLength();
if (len != -1)
{
imageData = new byte[len];
dis.readFully(imageData);
}
else
{
//未知長度,則按照字節的方式讀取數據
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int ch;
while ((ch = dis.read()) != -1)
{
baos.write(ch);
}
imageData = baos.toByteArray();
baos.close();
}
if (dis != null)
{
dis.close();
}
//根據二進制數據創建一個Image
return Image.createImage(imageData, 0, imageData.length);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
finally
{
if (hc != null)
{
try
{
hc.close();
}
catch (Exception e)
{
}
}
}
}
public void commandAction(Command c, Displayable s)
{
String cmd = c.getLabel();
if (cmd.equals("下載"))
{
new Thread(this).start();
}
}
public void run()
{
Image tmp = this.getImage(tfUrl.getString().trim());
iiImage.setImage(tmp);
}
}
2. 方法二:
public Image getImage2(String url)
{
HttpConnection hc = null;
//供返回的image對象
Image image = null;
try
{
hc = (HttpConnection)Connector.open(url);
//判斷連接是否成功
if (hc.getResponseCode() != HttpConnection.HTTP_OK)
{
return null;
}
//取得圖像二進制數據
DataInputStream dis = hc.openDataInputStream();
image = Image.createImage(dis);
dis.close();
//根據二進制數據創建一個Image
return image;
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
finally
{
if (hc != null)
{
try
{
hc.close();
}
catch (Exception e)
{
}
}
}
}
(五) 練習:文本文件查看器,選擇保存將其保存到Restor Store中,可供以后觀看。
(六) 用HTTP方式與服務器交互信息
使用Get方式與服務器交互信息,則具體的開發流程:
1. 設置Get訪問方式的Http地址:
String url = “http://127.0.0.1:8080/MyTest/hello?username=abc&password=456”;
2. 創建HttpConnection類型的網絡連接。
HttpConnection hc = (HttpConnection)Connector.open(url);
3. 調用HttpConnection的setRequestMethod()方法設置與服務器交互信息的類型為GET類型:
hc.setRequestMetho(HttpConnection.GET);
4. 服務器獲得請求后,會調用doGet()方法,但MIDlet并不知道服務器是否正確獲得請求,可以使用下面語句判斷:
if (hc.getResponseCode() == HttpConnection.HTTP_OK)
{
//讀取服務器信息
… …
}
5. 讀取信息后,顯示結果。
(七) 使用Socket網絡開發接口
MIDP提供了對開發Socket程序的支持,但是由于Socket程序并不是MIDP2.0中要求移動設備廠商必須支持的協議,所以有可能實際應用并不能使用,所以需要根據當地的設備情況進行選擇性的開發。
1. 什么是Socket? 客戶機/服務器?
可以把Socket看成是兩個手機程序進行通訊連接中的一個端點,一個程序將一段信息寫入Socket,該Socket將這段信息發送給另一個Socket中,使這段信息能傳送到另一個手機程序中。
手機的Socket傳輸方式可以理解為手機的客戶機/手機的服務器的交流模式。
客戶機/服務器在分布處理過程中,使用基于連接的網絡通信模式。該通信模式首先在客戶機和服務器之間定義一套通信協議,并創建一Socket類,利用這個類建立一條可靠的鏈接;然后,客戶機/服務器再在這條鏈接上可靠地傳輸數據。
客戶機發出請求,服務器監聽來自客戶機的請求,并為客戶機提供響應服務。這就是典型的“請求——應答”模式。
手機客戶機/服務器典型運作過程:
1) 手機服務器監聽響應端口的輸入;
2) 手機客戶機發出一個請求;
3) 手機服務器接收到此請求,處理請求,并把請求結果返回給手機客戶機;
4) 重復上述過程,直至完成一次信息交互過程。
利用以上過程,可以使用MIDP編寫作為服務器和客戶機的手機應用程序。一個作為服務器端的手機應用程序在負責監聽另外一個作為客戶機的手機程序的請求,為每個
作為客戶機請求的手機程序建立Socket連接,從而為作為客戶機的手機程序提供服務。
2. 開發Socket點到點程序
點到點程序使用的是服務器、客戶機的“請求——應答”模式,所以開發Socket程序可以分為開發服務器端和客戶端程序兩種
開發手機服務器端程序步驟如下:
步驟一:建立服務器端的Socket監聽端口,監聽所有客戶機的連接請求。創建一個具體的連接是使用ServerSocketConnection類來實現的。
監聽端口的字符串格式:
socket://:port
由于服務器從本地端口監聽客戶端連接,所以不需要指定本機器的IP地址,只需要指定監聽的端口port就可以了。
創建一個監聽端口為8859的ServerSocketConnection的代碼如下:
String url = “socket://:8859”;
ServerSocketConnection ssc;
ssc = (ServerSocketConnection)Connector.open(url);
步驟二:建立收發客戶機信息的SocketConnection。ServerSocket僅僅是創建監聽端口,具體的信息交流需要通過SocketConnection來實現。可以調用ServerSocketConnection的acceptAndOpen()方法返回一個SocketConnection類的對象。然后利用這個返回的對象操作客戶端傳來的信息:
SocketConnection sc;
sc = (SocketConnection)scc.acceptAndOpen();
acceptAndOpen()一直監聽客戶端是否請求連接服務器,如果沒有發現任何請求,則會一直監聽,直到有連接請求才會返回一個SocketConnection對象。
步驟三:可以通過返回的SocketConnection對象設置服務器的監聽屬性:
sc.setSocketOption(DELAY, 0); //設置延遲
sc.setSocketOption(LINGER, 0); //設置生存時間
sc.setSocketOption(KEEPALIVE, 0); //持續狀態
sc.setSocketOption(RCVBUF, 128); //獲得字節數
sc.setSocketOption(SNDBUF, 128); //發送字節數
步驟四:通過服務器端監聽客戶機連接成功后的SocketConnection對象與客戶機進行數據的接收和發送。
DataInputStream dis = sc.openDataInputStream();
DataOutputStream dos = sc.openDataOutputStream();
String result = dis.readUTF();
dos.writeUTF(result);
步驟五:關閉/釋放資源
dis.close();
dos.close();
sc.close();
ssc.close();
開發手機客戶端程序步驟如下:
步驟一:建立手機客戶端的Socket連接端口,必須指定連接地址,其格式如下:
socket://主機地址:端口號
例:
String url = “socket://127.0.0.1:8859”;
SocketConnection sc;
sc = (SocketConnection)Connector.open(url);
步驟二:利用SocketConnection的read()方法和write()方法接收和發送數據。
步驟三:釋放各種資源。
(八) 開發Datagram程序
MIDP提供了對Datagram程序的支持,但是由于Datagram程序并不是MIDP2.0中要求移動廠商必須支持的協議,所以有可能實際應用并不能使用,因此像Socket程序的開發一樣,需要根據當地的設備情況進行選擇性的開發。
1、 Datagram是什么?
Socket是針對TCP/IP協議開發的,是一種面向連接的保證可靠傳輸的協議。發送方與接收方在成對的兩個Socket之間建立連接,以便在TCP基礎上進行通信。
Datagram(數據報)則是針對UDP協議進行開發的,是非面向連接的,不能保證數據可靠到達。
每個數據報都是一個獨立的信息包,包括完整的源地址或目的地址,它在網絡上以任何可能的路徑傳往目的地,因此能否到達目的地,到達目的地的時間以及內容的正確性都是不能被保證的。
使用UDP時,每個數據報中都給出了完整的地址信息,因此無需建立發送方和接收方的連接。
使用UDP傳輸數據時是有大小限制的,每個被傳輸的數據報必須限定在64KB之內。而TCP則沒有這方面的限制。
協議
面向連接
可靠傳輸
數據大小限制
TCP
是
是
無
UDP
否
否
64KB
UDP協議不可或缺的原因:
?? 可靠傳輸需要付出代價,對數據內容的正確性檢驗占用計算機處理時間和網絡的帶寬,TCP的傳輸效率不如UDP。
?? 許多應用程序并不需要嚴格的傳輸可靠性,比如視頻會議系統,并不要求音頻/視頻數據的絕對正確,只要保證連貫就可以了,這種情況使用UDP更合理。
2、 開發Datagram程序流程
MIDP的Datagram點到點程序和開發Socket點到點的程序流程相似,都是C/S的交流方式。
Datagram程序繼承于DataInput和DataOutput類,Sun公司開發這類的目的是為了提供一個簡單的途徑讀取和發送二進制數據,替代使用getData()和sendData()方法。
使用Datagram的read/write方法的時候,讀取數據的指針會自動增加。與Socket程序的讀取數據不同的是Datagram程序每次寫入一個數據報之前,都要用reset()方法復位,例如下面的代碼寫入了數據包信息:
DatagramConnection connection;
datagram = connection.new Datagram(max);
//重設并準備重新獲得數據
datagram.reset();
//writeUTF會自動增加數據報的長度
datagram.writeUTF(“hello the world!”);
connection.send(datagram);
下面的代碼讀取一個單個的數據報信息:
datagram = connection.new Datagram(max);
connection.receive(datagram);
message = datagram.readUTF();
服務器端監聽的地址編寫格式是”datagram://:8859”。