无线消息传递的目标是扩展 J2ME 应用程序的联网和 I/O 能力,以便能使用 GSM 网络上的消息传递服务(如短信服务(Short Message Service,SMS)和小区广播服务(Cell Broadcast Service,CBS))来发送和接收消息。
GSM 短信服务(SMS)
SMS 是 GSM 网络中移动电话、传真机和/或 ip 地址之间简短文本消息的传递。消息不得超过 160 个字母数字字符且不包含图像或图形。这一服务的主要特性是迅速、价廉并能保证消息会到达目标用户(哪怕他在信号覆盖范围之外或已关掉电话)。消息一经发送,就会由短信服务中心(SMSC)接收,该中心必须随即将消息发送到适当的移动设备。为了做到这一点,SMSC 会向归属位置寄存器(home location register,HLR)发送 SMS 请求以找到漫游用户。一旦 HLR 接收到该请求,就会以用户的状态(如 1. 不活动或活动,和 2. 漫游位置)来响应 SMSC。假如响应是不活动,则 SMSC 将保留消息一段时间。当用户使用其设备时,HLR 会向 SMSC 发送一个 SMS 通知,而 SMSC 则尝试进行发送。SMSC 以短信发送点对点(Short Message Delivery Point-to-Point)格式将消息发送到 GSM 消息发送系统。该系统会寻呼设备,若设备作出响应,则发送消息。SMSC 会收到验证,即消息已经由最终用户接收,然后将该消息归类为已发送,并且将不再尝试发送它。图 1 说明了 SMS 发送机制。
图 1. SMS 发送系统
GSM 小区广播服务
GSM 小区广播服务答应将消息发送到当前位于某个特定小区的每个移动台(Mobile Station,MS),如移动电话、传真机和/或 IP 地址。在一段时间内会不时重复小区广播消息,从而使在第一次发送之后才进入小区的 MS 也能接收到消息。可以用二进制数据或 ASCII 文本的形式最多发送 15 页的数据,每页最多有 93 个字符;测试装置仅提供对 ASCII 消息的支持。小区广播消息按主题分类,给每条消息都分配了通道号、消息代码、更新号和语言。
1.通道号是标识消息主题的头部号(如‘气象报告’或‘交通信息’)。
2.消息代码标识特定的消息,这样当 MS 收到的消息的消息代码与以前收到的相同时,会意识到这是一条重复消息,可能就不会显示给用户了。
3.更新号用来标识消息的特定版本。这对于报告动态情况比较有用,在这样的情况下,消息可能正在报告某个事件(如前方道路施工),但事件的详情经常改变(例如,塞车的长度)。在某个小区中停留一段时间的 MS 将接收带相同消息代码的消息,但会接收作为同一消息更新版本的更新号;然而,进入该小区的 MS 将仅接收最新版本的消息(以及此后的任何后继版本)。
4.语言指明消息所用的语言。更改此参数不会使消息的原文得到翻译。
SMS 是一对一和一对几的消息传递系统,而 CBS 则可以在某一个地区提供一对多消息传递。
无线消息传递系统
可以将这一系统看作一个三层体系结构,由接口层(Interface Layer)、实现层(Implementation Layer)和传输层(Transport Layer)组成。
接口层构成了一组通用的消息传递接口,它们独立于所有消息传递协议。这些接口提供消息的基本定义,定义发送和接收消息的基本功能,以及提供向 MIDlet 应用程序通知进入消息的机制。
实现层包含这样的类:它们实现每个接口层以访问无线消息传递,如 GSM 移动设备上的 SMS 或 CBS 功能。例如,就 SMS 而言,这一层提供了用于 SMS 消息的消息连接的实现,以及具有文本或二进制属性的 SMS 消息的实现。实现层还执行用于底层协议的消息分段和并置。然后,MIDlet 可以在 MessageConnection 中指定应该将某条消息拆分成几段。
传输层包含这样的类:它们实际实现了将消息传送到移动设备的协议。这一三层机制如图 2 所示。
图 2. 无线消息传递系统体系结构
通用消息传递 API — javax.wireless.messaging
此 API 由 javax.wireless.messaging 包定义,该包定义了所有用于发送和接收无线消息的接口。以下是一个接口列表:
一、Message:它提供消息的基本定义,它充当一个容器来容纳消息的地址、有效负载及关于发送和阻塞的标志。它是 TextMessage 和 BinaryMessage 的超接口(superinterface),后两个分别是带文本有效负载属性和带二进制有效负载属性的消息对象。Message 的结构如图 3 所示。
图 3. 消息的结构
二、MessageConnection:它提供接收和发送消息的基本功能。它包含一个发送和接收消息的方法、一个创建新 Message 对象的工厂(factory)方法和一个计算发送某指定 Message 对象所需底层协议段数量的方法。通过调用 Connector.open() 可将这个类实例化。 在客户机方式连接中,只能发送消息。通过将标识着目的地址的字符串传递到 Connector.open() 方法来创建客户机方式连接。该方法返回 MessageConnection 对象。
clientConn = (MessageConnection)Connector.open("sms:// +18643630999:5000");
在服务器方式连接中,可以发送或接收消息。通过将标识本地主机上端点(取决于协议的标识符,例如,端口号)的字符串传递给 Connector.open() 方法来创建服务器方式连接。
serverConn = (MessageConnection)Connector.open("sms://:5000");
三、MessageListener:它提供了向 MIDlet 应用程序通知有进入消息的基本机制。当可以读取新到消息时,它答应 MIDlet 接收一个回调。
短信服务 API
com.sun.midp.io.j2me.sms 包提供用于短信服务消息传递系统的 API,并答应 MIDlet 访问 GSM 移动设备上的 SMS 功能。该包的主要组件 — MessageObject 和 PRotocol — 支持 SMS 消息的发送和接收。
一、MessageObject:MessageObject 是 SMS 消息的实现。在实现层,javax.wireless.messaging.Message 接口是作为缓冲区实现的。MessageObject 处理消息缓冲区的创建和缓冲区之外的输入/输出操作。此外,它有两个子类 — TextObject 和 BinaryObject.这些类实现具有文本或二进制有效负载的 SMS 消息。
二、Protocol.它实现发送 SMS 消息所需的与低层传输(Transport)机制的消息连接。在这一过程中,它检查所有的运行时配置参数,并处理与无效 URL 语法、安全性违规、I/O 违规和无效参数有关的异常。Protocol 还处理使用数据报或串行端口连接的消息发送与接收。
小区广播消息传递 API
com.sun.midp.io.j2me.cbs 包提供用于小区广播消息传递系统的 API,并答应 MIDLlet 访问 GSM 移动设备上的 CBS 功能。该包的主要组件 com.sun.midp.io.j2me.cbs.Protocol 支持 CBS 消息的接收。CBS 与 SMS 的不同之处在于:URL 连接字符串不支持指定的主机,而且它仅用于入站协议。有 CBS 能力的 MIDlet 可接收消息,但不能发送它们。
J2ME 消息传递应用程序
在这里,我们演示一个 WMAServer 示例,它等候进入的 SMS 消息,然后将它们显示在电话屏幕上。javax.microedition.lcdui 包提供了一组功能,用于实现应用程序的用户界面。WMAServer MIDlet 通过将标识本地主机上端点(取决于协议的标识符,例如,端口号)的字符串传递给 Connector.open() 方法来创建服务器方式连接。 为了能收到进入消息的通知,MIDlet 在 MessageConnection 实例 serverConn 处注册一个 MessageListener 对象。
serverConn.setMessageListener(MessageListener ml);
它还在 MessageListener 接口中实现 notifyIncomingMessage()。当进入消息到达 MessageConnection 时,就调用 notifyIncomingMessage() 方法。应用程序必须使用 MessageConnection 的 receive() 方法来检索该消息。WMAServer 应用程序从进入消息读取文本或二进制的有效负载数据,然后将其存储在字符串对象中供以后显示。
public void notifyIncomingMessage(MessageConnection conn) {
Message msg = null;
//Try reading (maybe block for) a message
try {
msg = conn.receive();
}
catch (Exception e) {
// Handle reading errors
System.out.println("processMessage.receive " + e);
}
// Process the received message
if (msg instanceof TextMessage) {
TextMessage tmsg = (TextMessage)msg;
msgReceived = tmsg.getPayloadText();
}
else
{
// process received message
if (msg instanceof BinaryMessage) {
BinaryMessage bmsg = (BinaryMessage)msg;
byte[] data = bmsg.getPayloadData();
//Handle the binary message...
msgReceived = data.toString();
}
}
当必须释放连接资源和相关联的侦听器对象时,应用程序提供 destroyApp() 方法。
public void destroyApp(boolean unconditional) {
try {
if (serverConn != null) {
serverConn.setMessageListener(null);
serverConn.close();
}
}
catch (IOException e) {
//Handle the exception...
e.printStacktrace();
}
以下是完整的应用程序代码: