[J2ME] 無(wú)線網(wǎng)絡(luò)開(kāi)發(fā)
無(wú)線網(wǎng)絡(luò)開(kāi)發(fā)MIDP提供了一組通用的網(wǎng)絡(luò)開(kāi)發(fā)接口,用來(lái)針對(duì)不同的無(wú)線網(wǎng)絡(luò)應(yīng)用可以采取不同的開(kāi)發(fā)接口。基于CLDC的網(wǎng)絡(luò)支持是由統(tǒng)一網(wǎng)絡(luò)連接框架(Generic Connection Frameword, 簡(jiǎn)稱GCF)定義的。其相關(guān)類、接口以及異常都放置在javax.microedtion.io包中。
在CLDC之中定義了七個(gè)接口,它們分別是:
1. Connection
2. StreamConnectionNotifier
3. InputConnection
4. OutputConnection
5. DatagramConnection
6. StreamConnection
7. ContentConnection
直接繼承自Connection的有四個(gè)類:StreamConnectionNotifier、DatagramConnection、InputConnection和OutputStream。其中,StreamConnectionNotifier只要提供Socket開(kāi)發(fā)接口。DatagramConnection提供UDP開(kāi)發(fā)接口。由于需要對(duì)網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)輸入和輸出進(jìn)行控制,因此提供了InputConnection和OutputConnection開(kāi)發(fā)接口。
通用的網(wǎng)絡(luò)開(kāi)發(fā)接口都是繼承自Connection接口。
GCF繼承體系
MIDP2.0中,新添加了3個(gè)網(wǎng)絡(luò)開(kāi)發(fā)接口:
?? javax.microedition.io.SocketConnection 負(fù)責(zé)TCP/IP方面的網(wǎng)絡(luò)開(kāi)發(fā)
?? javax.microedition.io.ServerSocketConnection 負(fù)責(zé)TCP/IP方面的網(wǎng)絡(luò)開(kāi)發(fā)
?? javax.microedition.io.UDPDatagramConnection 負(fù)責(zé)UDP方面的開(kāi)發(fā)
網(wǎng)絡(luò)開(kāi)發(fā)中,最重要的一個(gè)連接類是HttpConnection接口, 其繼承自ContentConnection。HttpConnection中定義了大量的基本聯(lián)網(wǎng)和獲取數(shù)據(jù)的操作。Http聯(lián)網(wǎng)功能是MIDP規(guī)范中要求廠商必須支持的連接方式,而其它方式,則取決于廠商與網(wǎng)絡(luò)服務(wù)商的設(shè)備支持情況。
也就是說(shuō),只有HTTP傳輸協(xié)議才是能夠在各家平臺(tái)上使用的對(duì)外溝通的方式。
GCF規(guī)范提出不管使用何種網(wǎng)絡(luò)或者本地文件的連接方式,所有的連接都使用Connector的open(URL)方法創(chuàng)建一個(gè)新的網(wǎng)絡(luò)連接(Connection.open(url)方法將返回一個(gè)Connection對(duì)象):
URL的格式如下;
<協(xié)議>://<用戶名>:<密碼>@<資源所在主機(jī)>:<端口號(hào)>/<資源路徑>;<參數(shù)>
協(xié)議如: http、https、socket方式等。資源所在主機(jī)代表資源所在位置的主機(jī)名稱或者IP地址。資源路徑的格式和使用的協(xié)議有關(guān),有些協(xié)議會(huì)有額外的參數(shù)需要設(shè)定。
1) 創(chuàng)建Http連接
Connector.open(“http://www.sun.com.cn”);
2) 創(chuàng)建Socket連接
Connector.open(“socket://127.0.0.1:8080”);
3) 創(chuàng)建Datagram連接
Connector.open(“datagram://www.sun.com.cn:9000”);
4) 創(chuàng)建本地文件連接
Connector.open(“file:/input.txt”);
Connector.open()方法有3個(gè)重載的方法:
public static Connection open(String name)
public static Connection open(String name, int mode)
public static Connection open(String name, int mode, Boolean timeout)
參數(shù):
name指定網(wǎng)絡(luò)地址
mode指定網(wǎng)絡(luò)連接的方式,分為讀、寫(xiě)和讀寫(xiě)三種方式
timeout指定了網(wǎng)絡(luò)連接超時(shí)的時(shí)間。如果網(wǎng)絡(luò)連接超時(shí),則拋出異常
一、 MIDP開(kāi)發(fā)HTTP程序
使用Connector.open()方法獲得一個(gè)網(wǎng)絡(luò)連接以后,就可以調(diào)用相應(yīng)的Connection類的方法獲得需要的信息。
利用Connection的openInputStream()方法可以獲得網(wǎng)絡(luò)傳輸過(guò)來(lái)的流數(shù)據(jù)。
(一) 使用StreamConnection接口
StreamConnection接口繼承了InputConnection和OutputConnection接口,因此創(chuàng)建一個(gè)StreamConnection對(duì)象可以返回一個(gè)獲得網(wǎng)絡(luò)傳輸數(shù)據(jù)的流對(duì)象,可以使用返回的流對(duì)象對(duì)數(shù)據(jù)進(jìn)行整理,再選取需要的數(shù)據(jù)顯示在屏幕上。
流程如下:
1. 創(chuàng)建一個(gè)StreamConnection類型的網(wǎng)絡(luò)連接
StreamConnection sc = null;
sc = (StreamConnection)Connector.open(url);
2. 創(chuàng)建了StreamConnection類型的網(wǎng)絡(luò)連接以后,可以調(diào)用openInputStream()方法返回一個(gè)InputStream對(duì)象,通過(guò)該對(duì)象獲得流數(shù)據(jù)。也可以使用openDataInputStream()方法返回一個(gè)DataInputStream對(duì)象。
InputStream is = null;
is = sc.openInputStream();
3. 通過(guò)數(shù)據(jù)對(duì)象操作獲得的數(shù)據(jù):
StringBuffer sb = new StringBuffer();
int ch;
while ((ch = is.read()) != -1)
{
sb.append((char)ch);
}
System.out.println(sb.toString());
4. 操作數(shù)據(jù)完成,關(guān)閉連接對(duì)象。
is.close();
練習(xí):用StreamConnection實(shí)現(xiàn)讀取網(wǎng)頁(yè)內(nèi)容。
(二) 使用HttpConnection接口
使用HttpConnection接口的開(kāi)發(fā)流程與使用StreamConnection接口基本一樣。由于HttpConnection接口繼承了StreamConnection接口,不但擁有StreamConnection接口的功能,還擴(kuò)展了許多功能。
例:
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)
{
}
}
(三) 顯示服務(wù)器信息
HttpConnection接口提供了多個(gè)獲得服務(wù)器信息的方法。
獲得了一個(gè)HttpConnection的連接對(duì)象以后,不需要使用openInputStream()方法就可以直接返回服務(wù)器信息(訪問(wèn)資源的信息)。
例:
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);
//要顯示的服務(wù)器的信息
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連接方式的簡(jiǎn)單應(yīng)用
1. 下載觀看圖片
從網(wǎng)絡(luò)上下載圖片是通過(guò)二進(jìn)制數(shù)據(jù)傳輸?shù)模虼丝梢允褂肐mage的重載方法:
Image createImage(byte[] imageData, int imageOffset, int imageLength)
Image createImage(InputStream stream) MIDP2.0提供
將圖片的數(shù)據(jù)存儲(chǔ)到字節(jié)數(shù)組imageData或者輸入流對(duì)象stream中,就可以使用createImage()方法將它們轉(zhuǎn)換為Image對(duì)象,以便在屏幕上顯示出來(lái)。
使用方法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對(duì)象
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
{
//未知長(zhǎng)度,則按照字節(jié)的方式讀取數(shù)據(jù)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int ch;
while ((ch = dis.read()) != -1)
{
baos.write(ch);
}
imageData = baos.toByteArray();
baos.close();
}
if (dis != null)
{
dis.close();
}
//根據(jù)二進(jìn)制數(shù)據(jù)創(chuàng)建一個(gè)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對(duì)象
Image image = null;
try
{
hc = (HttpConnection)Connector.open(url);
//判斷連接是否成功
if (hc.getResponseCode() != HttpConnection.HTTP_OK)
{
return null;
}
//取得圖像二進(jìn)制數(shù)據(jù)
DataInputStream dis = hc.openDataInputStream();
image = Image.createImage(dis);
dis.close();
//根據(jù)二進(jìn)制數(shù)據(jù)創(chuàng)建一個(gè)Image
return image;
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
finally
{
if (hc != null)
{
try
{
hc.close();
}
catch (Exception e)
{
}
}
}
}
(五) 練習(xí):文本文件查看器,選擇保存將其保存到Restor Store中,可供以后觀看。
(六) 用HTTP方式與服務(wù)器交互信息
使用Get方式與服務(wù)器交互信息,則具體的開(kāi)發(fā)流程:
1. 設(shè)置Get訪問(wèn)方式的Http地址:
String url = “http://127.0.0.1:8080/MyTest/hello?username=abc&password=456”;
2. 創(chuàng)建HttpConnection類型的網(wǎng)絡(luò)連接。
HttpConnection hc = (HttpConnection)Connector.open(url);
3. 調(diào)用HttpConnection的setRequestMethod()方法設(shè)置與服務(wù)器交互信息的類型為GET類型:
hc.setRequestMetho(HttpConnection.GET);
4. 服務(wù)器獲得請(qǐng)求后,會(huì)調(diào)用doGet()方法,但MIDlet并不知道服務(wù)器是否正確獲得請(qǐng)求,可以使用下面語(yǔ)句判斷:
if (hc.getResponseCode() == HttpConnection.HTTP_OK)
{
//讀取服務(wù)器信息
… …
}
5. 讀取信息后,顯示結(jié)果。
(七) 使用Socket網(wǎng)絡(luò)開(kāi)發(fā)接口
MIDP提供了對(duì)開(kāi)發(fā)Socket程序的支持,但是由于Socket程序并不是MIDP2.0中要求移動(dòng)設(shè)備廠商必須支持的協(xié)議,所以有可能實(shí)際應(yīng)用并不能使用,所以需要根據(jù)當(dāng)?shù)氐脑O(shè)備情況進(jìn)行選擇性的開(kāi)發(fā)。
1. 什么是Socket? 客戶機(jī)/服務(wù)器?
可以把Socket看成是兩個(gè)手機(jī)程序進(jìn)行通訊連接中的一個(gè)端點(diǎn),一個(gè)程序?qū)⒁欢涡畔?xiě)入Socket,該Socket將這段信息發(fā)送給另一個(gè)Socket中,使這段信息能傳送到另一個(gè)手機(jī)程序中。
手機(jī)的Socket傳輸方式可以理解為手機(jī)的客戶機(jī)/手機(jī)的服務(wù)器的交流模式。
客戶機(jī)/服務(wù)器在分布處理過(guò)程中,使用基于連接的網(wǎng)絡(luò)通信模式。該通信模式首先在客戶機(jī)和服務(wù)器之間定義一套通信協(xié)議,并創(chuàng)建一Socket類,利用這個(gè)類建立一條可靠的鏈接;然后,客戶機(jī)/服務(wù)器再在這條鏈接上可靠地傳輸數(shù)據(jù)。
客戶機(jī)發(fā)出請(qǐng)求,服務(wù)器監(jiān)聽(tīng)來(lái)自客戶機(jī)的請(qǐng)求,并為客戶機(jī)提供響應(yīng)服務(wù)。這就是典型的“請(qǐng)求——應(yīng)答”模式。
手機(jī)客戶機(jī)/服務(wù)器典型運(yùn)作過(guò)程:
1) 手機(jī)服務(wù)器監(jiān)聽(tīng)響應(yīng)端口的輸入;
2) 手機(jī)客戶機(jī)發(fā)出一個(gè)請(qǐng)求;
3) 手機(jī)服務(wù)器接收到此請(qǐng)求,處理請(qǐng)求,并把請(qǐng)求結(jié)果返回給手機(jī)客戶機(jī);
4) 重復(fù)上述過(guò)程,直至完成一次信息交互過(guò)程。
利用以上過(guò)程,可以使用MIDP編寫(xiě)作為服務(wù)器和客戶機(jī)的手機(jī)應(yīng)用程序。一個(gè)作為服務(wù)器端的手機(jī)應(yīng)用程序在負(fù)責(zé)監(jiān)聽(tīng)另外一個(gè)作為客戶機(jī)的手機(jī)程序的請(qǐng)求,為每個(gè)
作為客戶機(jī)請(qǐng)求的手機(jī)程序建立Socket連接,從而為作為客戶機(jī)的手機(jī)程序提供服務(wù)。
2. 開(kāi)發(fā)Socket點(diǎn)到點(diǎn)程序
點(diǎn)到點(diǎn)程序使用的是服務(wù)器、客戶機(jī)的“請(qǐng)求——應(yīng)答”模式,所以開(kāi)發(fā)Socket程序可以分為開(kāi)發(fā)服務(wù)器端和客戶端程序兩種
開(kāi)發(fā)手機(jī)服務(wù)器端程序步驟如下:
步驟一:建立服務(wù)器端的Socket監(jiān)聽(tīng)端口,監(jiān)聽(tīng)所有客戶機(jī)的連接請(qǐng)求。創(chuàng)建一個(gè)具體的連接是使用ServerSocketConnection類來(lái)實(shí)現(xiàn)的。
監(jiān)聽(tīng)端口的字符串格式:
socket://:port
由于服務(wù)器從本地端口監(jiān)聽(tīng)客戶端連接,所以不需要指定本機(jī)器的IP地址,只需要指定監(jiān)聽(tīng)的端口port就可以了。
創(chuàng)建一個(gè)監(jiān)聽(tīng)端口為8859的ServerSocketConnection的代碼如下:
String url = “socket://:8859”;
ServerSocketConnection ssc;
ssc = (ServerSocketConnection)Connector.open(url);
步驟二:建立收發(fā)客戶機(jī)信息的SocketConnection。ServerSocket僅僅是創(chuàng)建監(jiān)聽(tīng)端口,具體的信息交流需要通過(guò)SocketConnection來(lái)實(shí)現(xiàn)。可以調(diào)用ServerSocketConnection的acceptAndOpen()方法返回一個(gè)SocketConnection類的對(duì)象。然后利用這個(gè)返回的對(duì)象操作客戶端傳來(lái)的信息:
SocketConnection sc;
sc = (SocketConnection)scc.acceptAndOpen();
acceptAndOpen()一直監(jiān)聽(tīng)客戶端是否請(qǐng)求連接服務(wù)器,如果沒(méi)有發(fā)現(xiàn)任何請(qǐng)求,則會(huì)一直監(jiān)聽(tīng),直到有連接請(qǐng)求才會(huì)返回一個(gè)SocketConnection對(duì)象。
步驟三:可以通過(guò)返回的SocketConnection對(duì)象設(shè)置服務(wù)器的監(jiān)聽(tīng)屬性:
sc.setSocketOption(DELAY, 0); //設(shè)置延遲
sc.setSocketOption(LINGER, 0); //設(shè)置生存時(shí)間
sc.setSocketOption(KEEPALIVE, 0); //持續(xù)狀態(tài)
sc.setSocketOption(RCVBUF, 128); //獲得字節(jié)數(shù)
sc.setSocketOption(SNDBUF, 128); //發(fā)送字節(jié)數(shù)
步驟四:通過(guò)服務(wù)器端監(jiān)聽(tīng)客戶機(jī)連接成功后的SocketConnection對(duì)象與客戶機(jī)進(jìn)行數(shù)據(jù)的接收和發(fā)送。
DataInputStream dis = sc.openDataInputStream();
DataOutputStream dos = sc.openDataOutputStream();
String result = dis.readUTF();
dos.writeUTF(result);
步驟五:關(guān)閉/釋放資源
dis.close();
dos.close();
sc.close();
ssc.close();
開(kāi)發(fā)手機(jī)客戶端程序步驟如下:
步驟一:建立手機(jī)客戶端的Socket連接端口,必須指定連接地址,其格式如下:
socket://主機(jī)地址:端口號(hào)
例:
String url = “socket://127.0.0.1:8859”;
SocketConnection sc;
sc = (SocketConnection)Connector.open(url);
步驟二:利用SocketConnection的read()方法和write()方法接收和發(fā)送數(shù)據(jù)。
步驟三:釋放各種資源。
(八) 開(kāi)發(fā)Datagram程序
MIDP提供了對(duì)Datagram程序的支持,但是由于Datagram程序并不是MIDP2.0中要求移動(dòng)廠商必須支持的協(xié)議,所以有可能實(shí)際應(yīng)用并不能使用,因此像Socket程序的開(kāi)發(fā)一樣,需要根據(jù)當(dāng)?shù)氐脑O(shè)備情況進(jìn)行選擇性的開(kāi)發(fā)。
1、 Datagram是什么?
Socket是針對(duì)TCP/IP協(xié)議開(kāi)發(fā)的,是一種面向連接的保證可靠傳輸?shù)膮f(xié)議。發(fā)送方與接收方在成對(duì)的兩個(gè)Socket之間建立連接,以便在TCP基礎(chǔ)上進(jìn)行通信。
Datagram(數(shù)據(jù)報(bào))則是針對(duì)UDP協(xié)議進(jìn)行開(kāi)發(fā)的,是非面向連接的,不能保證數(shù)據(jù)可靠到達(dá)。
每個(gè)數(shù)據(jù)報(bào)都是一個(gè)獨(dú)立的信息包,包括完整的源地址或目的地址,它在網(wǎng)絡(luò)上以任何可能的路徑傳往目的地,因此能否到達(dá)目的地,到達(dá)目的地的時(shí)間以及內(nèi)容的正確性都是不能被保證的。
使用UDP時(shí),每個(gè)數(shù)據(jù)報(bào)中都給出了完整的地址信息,因此無(wú)需建立發(fā)送方和接收方的連接。
使用UDP傳輸數(shù)據(jù)時(shí)是有大小限制的,每個(gè)被傳輸?shù)臄?shù)據(jù)報(bào)必須限定在64KB之內(nèi)。而TCP則沒(méi)有這方面的限制。
協(xié)議
面向連接
可靠傳輸
數(shù)據(jù)大小限制
TCP
是
是
無(wú)
UDP
否
否
64KB
UDP協(xié)議不可或缺的原因:
?? 可靠傳輸需要付出代價(jià),對(duì)數(shù)據(jù)內(nèi)容的正確性檢驗(yàn)占用計(jì)算機(jī)處理時(shí)間和網(wǎng)絡(luò)的帶寬,TCP的傳輸效率不如UDP。
?? 許多應(yīng)用程序并不需要嚴(yán)格的傳輸可靠性,比如視頻會(huì)議系統(tǒng),并不要求音頻/視頻數(shù)據(jù)的絕對(duì)正確,只要保證連貫就可以了,這種情況使用UDP更合理。
2、 開(kāi)發(fā)Datagram程序流程
MIDP的Datagram點(diǎn)到點(diǎn)程序和開(kāi)發(fā)Socket點(diǎn)到點(diǎn)的程序流程相似,都是C/S的交流方式。
Datagram程序繼承于DataInput和DataOutput類,Sun公司開(kāi)發(fā)這類的目的是為了提供一個(gè)簡(jiǎn)單的途徑讀取和發(fā)送二進(jìn)制數(shù)據(jù),替代使用getData()和sendData()方法。
使用Datagram的read/write方法的時(shí)候,讀取數(shù)據(jù)的指針會(huì)自動(dòng)增加。與Socket程序的讀取數(shù)據(jù)不同的是Datagram程序每次寫(xiě)入一個(gè)數(shù)據(jù)報(bào)之前,都要用reset()方法復(fù)位,例如下面的代碼寫(xiě)入了數(shù)據(jù)包信息:
DatagramConnection connection;
datagram = connection.new Datagram(max);
//重設(shè)并準(zhǔn)備重新獲得數(shù)據(jù)
datagram.reset();
//writeUTF會(huì)自動(dòng)增加數(shù)據(jù)報(bào)的長(zhǎng)度
datagram.writeUTF(“hello the world!”);
connection.send(datagram);
下面的代碼讀取一個(gè)單個(gè)的數(shù)據(jù)報(bào)信息:
datagram = connection.new Datagram(max);
connection.receive(datagram);
message = datagram.readUTF();
服務(wù)器端監(jiān)聽(tīng)的地址編寫(xiě)格式是”datagram://:8859”。
posted on 2008-09-01 19:51 騎豬闖天下 閱讀(922) 評(píng)論(0) 編輯 收藏