通过利用UDP(User Datagram Protocal,用户数据报协议),BlackBerry设备支持数据报连接。应用程序使用UDP和标准的网络服务通信。
数据报是应用程序发送到网络的独立数据包。对于Datagram的负载字节数组来说,一个Datagram对象是一个包装器。为获得这个字节数组的一个引用,调用getData()方法。
和HTTP连接不一样,数据报连接不稳定:数据包以任意的顺序到达,并且传输得不到保证。应用程序的责任是确保请求数据报的数据负载根据网络服务的标准来格式化,这个标准是在数据报传播上的。应用程序也必须能解析从服务器返回发送的数据报。
使用数据报连接来发送短消息。为获得更多信息,参看121页的“发送和接受SMS”。
为使用UDP连接,你必须有一个你自己的基础设施来连接无线网络,包括一个GPRS(General Packet Radio Service,通用分组无线业务)网络的APN(Access Point Name)。
注:数据报连接没有使用BlackBerry的基础设施,因此连接没有加密。模拟器的APN是net.rim.gprs.
javax.microedition.io.DatagramConnection接口,扩展了Connection类,它定义了发送和接受数据报的连接。Datagram接口定义了在数据报连接上发送和接受的数据包。
注:使用UDP连接需要你和服务商紧密联系。联系你的服务商确认UDP连接是否支持。
如果你的服务商不支持多个PDP上下文,那么你可能没有建立一个UDP连接。一个PDP上下文为发送消息的BlackBerry.net.APN保留。尽管如此,你可以为UDP使用blackberry.net 当作APN。联系你的服务商以获得更多信息。
使用下面的格式调用Connector.open().指定udp为你的协议。将返回的对象转化为一个DatagramConnection。
(DatagramConnection)Connector.
open("udp://host:dest_port[;src_port]/apn");
参数 |
描述 |
host |
指定点阵ASCII十进制格式的主机地址 |
dest_port |
指定了主机地址的目标端口(接受信息时是可选的)。 |
src_port |
指定本地源端口(可选)。 |
apn |
指定字符串形式的网络APN。 |
注:可以在相同的端口上收发UDP数据报。
为了在UDP连接上发送数据,在连接字符串里指定目标端口。为了在UDP连接上接收数据,在连接字符串里指定源端口。为了接收指定主机的所有端口上的数据报,省略目标端口。
注:为了在一个非GPRS的网络里打开一个连接,不要指定APN。在源端口后包含斜线”/”.例如CDMA网络连接的地址应该是udp://121.0.0.0:6343/.
创建一个数据报
调用DatagramConnection.newDatagram().
Datagram outDatagram = conn.newDatagram(buf, buf.length); |
将数据加入到数据报中
调用Datagram.setData().数据的格式由接收它的服务决定。
byte[] buf = new byte[256]; outDatagram.setData(buf, buf.length); |
在UDP连接上发送数据
在数据报连接山调用send()。
conn.send(outDatagram); |
注:如果应用程序试图在一个UDP连接上发送一个数据报,并且接收者没有监听指定的源端口,一个IOException会抛出.
接收UDP连接上的数据
调用数据报连接上的receive().
byte[] buf = new byte[256]; Datagram inDatagram = conn.newDatagram(buf, buf.length); conn.receive(inDatagram); |
注:receive()方法会阻塞其他操作,直至它接收完一个数据包.如果你知道正在发送的数据格式,转化他们为合适的格式.
从数据报提取数据
在数据报连接上调用getData()方法。如果你知道正在接收的数据类型,将数据转化为合适的格式。
String received = new String(inDatagram.getData()); |
关闭UDP连接
和MIDP框架中所有连接一样,调用输入和输出流以及数据报上的close()方法,
DatagramConnectionBase类提供了方法来处理Mobitex网络上的BlackBerry数据报连接以及传输操作。
调用Connector.open(),然后将返回的对象转化为一个DatagramConnectionBase。DatagramConnectionBase类实现了DatagramConnection,并且提供了额外的方法,对注册一个数据报状态监听者来说,这些方法是必要的。
为提供一个参数给Connector.open(),连接字符串使用下面的格式:
mobitex:<type>:<MAN>
参数 |
描述 |
<type> |
接受下列值:“TEXT”,”DATA”,”STATUS”,或”HPDATAHPID(在这些值中,JPID的格式是ASCII十进制) |
<MAN> |
Mobitex访问号码(Mobitex Access Number),接受ASCII十进制格式。 |
注:如果你打开一个服务器连接(一个监听者),MAN留为空白。
// The datagram connection <type> is DATA and the MAN is left blank for an incoming // connection. DatagramConnection dc = (DatagramConnection)Connector.open("mobitex:DATA:"); DatagramConnectionBase dcb = (DatagramConnectionBase)dc; |
如果你想注册一个数据报状态事件监听者,使用一个DatagramBase,而不是Datagram来抓住(hold)数据。DatagramBase实现了Datagram接口,并且提供额外的方法,这些方法对注册数据报状态事件监听者是有用的。
// dc is a DatagramConnection. Datagram d = dc.newDatagram(dc.getMaximumLength()); d.setAddress(address); d.setData(raw, 0, raw.length); DatagramBase db = (DatagramBase)d; // An error if this fails. |
注册一个数据报状态监听者
你的DatagramStatusListener接口的实现监听事件,例如一个数据报的接收。参看API参考的DatagramStatusListener以获得完整的数据报状态事件列表。
为了分配一个数据报ID,并将之显式指派给DatagramStatusListener,调用DatagramConnectionBase.allocateDatagramId().
int id = dcb.allocateDatagramId(d); |
以此种方式预先分配数据报ID,可以确保你的监听者代码知道与此ID相关联的数据报。
MobitexAddress类封装了Mobitex地址信息,例如MAN,消息的类型以及消息的状态。
MobitexPacketHeader类提供对无线头字段(radio header field)的底层访问。为了对所有地址操作使用MobitexPacketHeader并忽略其他的MobitexAddress字段,调用MobitexAddress.setPacketHeader().
MobitexInfo类提供对象存储普通的无线(radio)状态信息。Mobitex.MobitexCellInfo类提供对象存储Mobitex蜂窝信息。
MobitexDemo.java代码实例描述了Mobitex无线称API的使用。
例:MobitexDemo.java
/*
* MobitexDemo.java
*
* Copyright (C) 2003-2005 Research In Motion Limited
*/
package com.rim.docs.samples.mobitexdemo;
import javax.microedition.io.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.i18n.*;
import java.util.*;
import net.rim.device.api.io.*;
import net.rim.device.api.system.*;
import java.io.*;
import com.rim.docs.samples.baseapp.*;
/**
* A simple mobitex layer sample.
*/
public final class MobitexDemo extends BaseApp
implements MobitexDemoResource {
private MainScreen _mainScreen;
private EditField _pin;
private EditField _data;
private RichTextField _messages;
private SenderThread _sendThread;
private ReceiverThread _receiverThread;
// statics ------------------------------------------------------------------
private static ResourceBundle _resources =
ResourceBundle.getBundle(MobitexDemoResource.BUNDLE_ID,
MobitexDemoResource.BUNDLE_NAME);
static public void main(String[] args)
{
new MobitexDemo().enterEventDispatcher();
}
// menu items ----------------------------------------------------------------
// Cache the send menu item for reuse.
private MenuItem _sendMenuItem = new MenuItem(_resources, MOBITEXDEMO_MENUITEM_SEND,
100, 10) {
public void run()
{
// Don’t execute on a blank address.
String pin = _pin.getText();
String data = _data.getText();
if ( pin.length() > 0 )
{
send(pin, data);
}
}
};
// Cache the clear messages menu item for reuse.
private MenuItem _clearMessages = new MenuItem(_resources,
MOBITEXDEMO_MENUITEM_CLEARMESSAGES, 105, 10) {
public void run() {
_messages.setText("");
}
};
public MobitexDemo()
{
_mainScreen = new MainScreen();
_mainScreen.setTitle( new LabelField(_resources.getString(MOBITEXDEMO_TITLE),
LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH));
_pin = new EditField(_resources.getString(MOBITEXDEMO_LABEL_PIN), null,
Integer.MAX_VALUE, EditField.FILTER_PIN_ADDRESS);
_mainScreen.add(_pin);
_data = new EditField(_resources.getString(MOBITEXDEMO_LABEL_DATA), null);
_mainScreen.add(_data);
_mainScreen.add(new SeparatorField());
_messages = new RichTextField(_resources.getString(MOBITEXDEMO_CONTENT_DEFAULT));
_mainScreen.add(_messages);
_mainScreen.addKeyListener(this); //implemented by the super
_mainScreen.addTrackwheelListener(this); //implemented by the super
//start the helper threads
_sendThread = new SenderThread();
_sendThread.start();
_receiverThread = new ReceiverThread();
_receiverThread.start();
//push the main screen - a method from the UiApplication class
pushScreen(_mainScreen);
}
// methods ------------------------------------------------------------------
/*public boolean keyChar(char key, int status, int time)
{
if ( UiApplication.getUiApplication().getActiveScreen().getLeafFieldWithFocus() ==
pin && key == Characters.ENTER )
{
_sendMenuItem.run();
return true; // I’ve absorbed this event, so return true.
}
else
{
return super.keyChar(key, status, time);
}
}*/
protected void makeMenu(Menu menu, int instance)
{
menu.add(_sendMenuItem);
menu.add(_clearMessages);
menu.addSeparator();
super.makeMenu(menu, instance);
}
private void send(String pin, String data)
{
_sendThread.send(pin, data);
}
private void message(String msg)
{
System.out.println(msg);
_messages.setText(_messages.getText() + msg + "/n");
}
// inner classes -------------------------------------------------------------
private class ReceiverThread extends Thread
{
private DatagramConnection _dc;
// Shut down the thread.
public void stop()
{
try {
_dc.close();
}
catch(IOException e) {
MobitexDemo.this.message(e.toString());
}
}
public void run()
{
try {
// Incoming data connection - leave the MAN blank.
_dc = (DatagramConnection)Connector.open("mobitex:DATA:");
for(;;)
{
Datagram d = _dc.newDatagram(_dc.getMaximumLength());
_dc.receive(d);
DatagramBase db = (DatagramBase)d;
MobitexAddress ma = (MobitexAddress)db.getAddressBase();
MobitexPacketHeader mph = ma.getPacketHeader();
StringBuffer sb = new StringBuffer();
sb.append("Recieved packet");
sb.append("/nFROM:");
sb.append(mph.getSourceAddress());
sb.append("/nTO:");
sb.append(mph.getDestinationAddress());
sb.append("/nFLAGS:");
sb.append(Integer.toHexString(mph.getPacketFlags()));
sb.append("/nDATA:");
sb.append(new String(db.getData()));
MobitexDemo.this.message(sb.toString());
}
}
catch (IOException e) {
MobitexDemo.this.message(e.toString());
}
}
}
/**
* The ConnectionThread class manages the datagram connection
*/
private class SenderThread extends Thread implements DatagramStatusListener {
private static final int TIMEOUT = 500; //ms
private Vector _sendQueue = new Vector(5);
private volatile boolean _start = false;
private volatile boolean _stop = false;
// Queue something for sending
public void send(String pin, String data)
{
synchronized(_sendQueue) {
_sendQueue.addElement(new String[] { pin, data });
_start = true;
}
}
// Shut down the thread.
public void stop() {
_stop = true;
}
public void run()
{
for(;;) {
String pin = null;
String data = null;
// Thread control.
synchronized(_sendQueue) {
while( !_start && !_stop)
{
// Sleep for a bit so we don’t spin.
try {
_sendQueue.wait(TIMEOUT);
}
catch (InterruptedException e) {
System.err.println(e.toString());
}
}
if (_start) {
String[] a = (String[])_sendQueue.firstElement();
if ( a != null ) {
pin = a[0];
data = a[1];
}
_start = false;
}
else if ( _stop ) { // Exit condition
return;
}
}
// Open the connection and extract the data.
DatagramConnection dc = null;
try {
String address = "DATA:" + pin;
dc = (DatagramConnection)Connector.open("mobitex:" + address);
//an error if this fails
DatagramConnectionBase dcb = (DatagramConnectionBase)dc;
Datagram d = dc.newDatagram(dc.getMaximumLength());
byte[] raw = data.getBytes();
d.setAddress(address);
d.setData(raw, 0, raw.length);
// An error if this fails.
DatagramBase db = (DatagramBase)d;
// Allocate a datagram ID - if you want to know about status.
// For this particular datagram, then we can allocate the id
// here and log it for later follow up
dcb.allocateDatagramId(d);
// Set up a status listener.
db.setDatagramStatusListener(this);
dcb.send(d);
dc.close();
}
catch (IOException e) {
MobitexDemo.this.message(e.toString());
}
// We’re done one connection so reset the start state.
_start = false;
}
}
public void updateDatagramStatus(int dgId, int code, Object context) {
String msg = "Datagram: " + Integer.toHexString(dgId) +
"/nStatus: " + DatagramStatusListenerUtil.getStatusMessage(code);
MobitexDemo.this.message(msg);
}
}
protected void onExit() {
_sendThread.stop();
_receiverThread.stop();
}
}
使用UDP在数据报包上发送和接收短消息(SMS)。包含了BlackBerry消息头信息的短消息数据报包,其大小是固定的160个字节。
注:短消息不是所有的网络都支持。和你的服务商确认是否相关的网络全部和部分支持SMS。大多数情况下,GPRS和支持CDMA的BlackBerry设备支持SMS。如果服务商提供SMS服务,系统管理员也可以使用IT策略来控制公司用户对SMS的使用。管理员可以将ENABLE_SMS项设置为TRUE或FALSE。缺省值是TRUE(SMS可用)。
打开一个数据报连接来发送SMS
调用Connector.open().使用下面格式提供一个连接字符串:
DatagramConnection _dc = Connector.open("sms://<peer_address>");
在这里,<peer_address>是接收者的电话号码-MSISDN(MobileStation ISDN Number)。
你也可以忽略peer_address,代替它的是调用Datagram.setAddress()来设置消息的目的地地址。
创建一个SMS
调用DatagramConnection.newDatagram().
Datagram smsMessage = conn.newDatagram(buf, buf.length); |
设置SMS内容
调用setData()。
private String _msg = "This is a test message"; byte[] data = _msg.getBytes(); smsMessage.setData(data, 0, data.length); |
发送一个SMS消息
注:在一个与主应用程序线程独立的线程上打开网络连接,这样UI就不会停止。
如果你没有在连接字符串中指定peer_address,那么调用Datagram.setAddress()设置SMS地址。为了发送SMS,调用DatagramConnection.send()。
smsMessage.setAddress("sms://+15555551234"); _dc.send(datagram); |
代码实例
SendSms.java代码实例描述了如何在独立的线程上发送一条SMS消息。
在实例的工作空间中,SendSms.java实例需要一个服务器端的应用程序来与BlackBerry设备模拟器交互,来模拟发送和接收SMS消息。你不能从BlackBerry设备模拟器发送一个实际的SMS。
样例(SMSServer.java)的服务器端程序包含在BlackBerry JDE下。为了运行服务器端程序,运行run.bat,它在你的BlackBerry JDE实例的子目录下。例如:
/samples/com/rim/samples/server/smsdemo/.
例:SendSms.java
/**
* SendSms.java
* Copyright (C) 2002-2005 Research In Motion Limited. All rights reserved.
*/
package com.rim.samples.docs.smsdemo;
import net.rim.device.api.io.*;
import net.rim.device.api.system.*;
import javax.microedition.io.*;
import java.util.*;
import java.io.*;
public class SendSms extends Application {
private static final int MAX_PHONE_NUMBER_LENGTH = 32;
// Members.
private String addr = "15195551234";
private String msg = "This is a test message.";
private DatagramConnection _dc = null;
private static String _openString = "sms://";
public static void main(String[] args) {
new SendSms().enterEventDispatcher();
}
public SendSms() {
try {
_dc = (DatagramConnection)Connector.open(_openString);
byte[] data = msg.getBytes();
Datagram d = _dc.newDatagram(_dc.getMaximumLength());
d.setAddress("//" + addr);
_dc.send(d);
}
catch ( IOException e) {
}
System.exit(0);
}
}
创建一个独立的监听线程
在一个与主应用程序线程独立的线程上监听消息,以致UI不会停止。
打开数据报连接
调用Connector.open().使用下面的格式提供一个字符串连接:
_dc = (DatagramConnection)Connector.
open("sms://<peer_address><port>");
在这里:
获取数据报
创建一个Datagram对象存储数据报。为获取SMS消息数据报,调用数据报连接上的receive()。这个操作会阻塞直至数据接收完毕。
Datagram d = _dc.newDatagram(160); // SMS messages have a fixed size of 160 bytes _dc.receive(d); |
从数据报提取数据
为了从SMS消息提取地址,调用Datagram.getAddress().为了从SMS消息中提取数据,调用Datagram.getData().
String address = d.getAddress(); String msg = new String(d.getData()); |
代码实例
ReceiveSms.java描述了如何在一个独立的线程上接收一个SMS消息。
例:ReceiveSms.java
/**
* ReceiveSms.java
* Copyright (C) 2002-2005 Research In Motion Limited. All rights reserved.
*/
package com.rim.samples.docs.smsdemo;
import net.rim.device.api.io.*;
import net.rim.device.api.system.*;
import javax.microedition.io.*;
import java.util.*;
import java.io.*;
public class ReceiveSms extends Application {
private ListeningThread _listener;
// Additional code required for complete sample.
public static void main(String[] args) {
new ReceiveSms().enterEventDispatcher();
}
ReceiveSms() {
_listener = new ListeningThread();
_listener.start();
}
private static class ListeningThread extends Thread {
private boolean _stop = false;
private DatagramConnection _dc;
public synchronized void stop() {
_stop = true;
try {_
dc.close(); // Close the connection so the thread returns.
}
catch (IOException e) {
System.err.println(e.toString());
}
}
public void run() {
try {
_dc = (DatagramConnection)Connector.open("sms://");
for(;;) {
if ( _stop ) {
return;
}
Datagram d = _dc.newDatagram(_dc.getMaximumLength());
_dc.receive(d);
String address = new String(d.getAddress());
String msg = new String(d.getData());
System.out.println("Message received:" + msg);
System.out.println("From:" + address);
System.exit(0);
}
}
catch (IOException e) {
System.err.println(e.toString());
}
}
}
}