[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 = "http://openzone.ipchina.org/index.jsp";
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 = "http://www.google.com.cn/index.html";
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”。