BlackBerry网络连接编程


BlackBerry 网络连接编程 作者: 王恒进 P a g e | 2 目录 1. BlackBerry 上连接 HTTP 或 Socket 的五种方式 ................................................................................. 3 1.1. BlackBerry Enterprise Server(BES)方式 .................................................................................... 3 1.2. BlackBerry Internet Servie(BIS)方式 ................................................................................... 3 1.3. Direct TCP 方式 ......................................................................................................................... 3 1.4. Wi-Fi .......................................................................................................................................... 4 1.5. WAP ........................................................................................................................................... 4 2. BlackBerry 上中国移动的 WAP 网关连接技巧................................................................................... 7 3. BlackBerry 上中国电信的 WAP 网关连接分析................................................................................. 10 4. BlackBerry 5.0 新提供的 Network API:自动选择网关的终极解决方案 ....................................... 11 5. 网络设置解决方案举例 ..................................................................................................................... 14 6. HTTP 连接代码实例 ........................................................................................................................... 16 P a g e | 3 1. BlackBerry 上连接 HTTP 或 Socket 的五种方式 BlackBerry JDE 的 API 提供了五种方式来进行 HTTP 或者 socket 连接,包括: 1.1. BlackBerry Enterprise Server(BES)方式 这种方式通过使用 BES 的 BlackBerry MDS Services 来进行网络连接,BlackBerry MDS Services 负责处理所有的浏览器请求或者连接请求、并负责数据加密,这是黑莓 手机的默认连接方式,如下: 例 1: (HttpConnection) Connector.open("http://www.testserver.com"); 以上代码会自动将 BlackBerry MDS Services 作为它的默认连接路径。实际开发中, 如果要确保应用程序使用 uses BlackBerry MDS Services 作为它的连接路径,需要在 URL 最后加上参数“deviceside=false”,这也是我们推荐的方式,如下: 例 2: (HttpConnection)Connector.open(“http://www.testserver.com;deviceside=false”); 1.2. BlackBerry Internet Servie(BIS)方式 这种方式是为第三方提供的连接接口,它对数据不进行加密,用户可以通过使用 HTTPS 和 SSL 来进行安全的连接。注:目前仅对加入了 BlackBerry Alliance Program 的第三方开放合作伙伴开放,详情参考: http://na.blackberry.com/eng/partners/alliance.jsp 1.3. Direct TCP 方式 这种方式允许在没有使用 BlackBerry MDS 的黑莓手机上直接 TCP 连接。为了能启用 direct TCP 方式,用户需要在手机的“选项-高级选项-TCP/IP”中设置 APN,以及相 应的用户名和密码。 注 1:运行在 iDEN 网络上的黑莓手机(包括 6510、7510、7520 和 7100i),如果不指 定 deviceside 参数(如例 1),默认的连接是 direct TCP;非运行在 iDEN 网络上的其 他黑莓手机,如果不指定 deviceside 参数,默认的连接是 BlackBerry MDS。 注 2:如果连接时 BlackBerry MDS 不存在,黑莓手机也会自动采用 direct TCP 方 式。 因此,如果要将 direct TCP 作为黑莓手机的默认连接方式,我们建议在 URL 中加入 “deviceside=true”参数,如下: 例 3-1: (SocketConnection)Connector.open("socket://testserver:600;deviceside=true"); P a g e | 4 此外,如果应用程序不希使用用户自己设置的 APN,也可以在 URL 中指定自己 APN,以下是指定通过中国移动 CMNET 进行直接 TCP 连接的例子: 例 3-2: (SocketConnection)Connector.open("socket://testserver:600;deviceside=true;APN=cmnet"); 1.4. Wi-Fi 如果需要在 Wi-Fi 上创建网络连接,不需要在应用程序中考虑特别的底层逻辑,可以在 URL 中加入参数“interface=wifi”就能实现 Wi-Fi 连接,如下: 例 4:(StreamConnection)Connector.open(“socket:// testserver:600;interface=wifi”); 1.5. WAP 1.5.1. WAP 1.x 并不是所有的移动运营商都支持通过 WAP 网关进行连接的,所以如果要创建 WAP 连 接,开发者需要和移动运营商联系,获取是否提供这种支持、并且获取他们的 WAP 网 管参数。 以下是一个基于中国移动 CMWAP 的 WAP 网关进行 HTTP 连接的例子: 例 5: (HttpConnection)Connector.open("http://wap.google.com;WAPGatewayIP=10.0.0.172;WAPGatewayAPN=cm wap"); 注:其中 WAPGatewayIP 和 WAPGatewayAPN 这两项参数必须指定,参数之间用 “;”隔开,以下是所有的 WAP 参数列表,实际开发时请根据运营商提供的信息决定 哪些参数需要设置。 Parameter Description WapGatewayIP IP address of the gateway. WapGatewayAPN APN for General Packet Radio Service (GPRS) networks only. For testing purposes, you can use rim.net.gprs WapGatewayPort Gateway port value. If port 9203 is specified, Wireless Transport Layer Security (WTLS) is used unless WapEnableWTLS=false is specified. WapSourceIP IP address of the source. P a g e | 5 1.5.2. WAP 2.0 如果要连接 WAP2.0 的网关,需要在连接时指定 service record、以及 UID。以下代码说明 了 WAP2.0 的连接时如何工作的: 例 5: ServiceBook sb = ServiceBook.getSB(); ServiceRecord[] records = sb.findRecordsByCid("WPTCP"); String uid = null; for(int i=0; i < records.length; i++) { //Search through all service records to find the //valid non-Wi-Fi and non-MMS //WAP 2.0 Gateway Service Record. if (records[i].isValid() && !records[i].isDisabled()) { if (records[i].getUid() != null && records[i].getUid().length() != 0) { if ((records[i].getUid().toLowerCase().indexOf("wifi") == -1) && (records[i].getUid().toLowerCase().indexOf("mms") == -1)) { uid = records[i].getUid(); break; } } } } if (uid != null) { //open a WAP 2 connection Connector.open(_url + ";ConnectionUID=" + uid); } WapSourcePort Source port value. TunnelAuthUsername User name for APN session, when Password Authentication Protocol (PAP) or Challenge Handshake Application Protocol (CHAP) authentication is used. TunnelAuthPassword Password for APN session, when PAP or CHAP authentication is used. WapEnableWTLS Explicitly turns on or turns off WTLS. If this parameter is not specified, WTLS is used by default for connections to port 9203. P a g e | 6 else { //Consider another transport or alternative action. } 在这里,基本流程是先从 Service Book 中获得 Service Record,然后再从中获得 UID, 后文会对此进行更具体的分析。 P a g e | 7 2. BlackBerry 上中国移动的 WAP 网关连接技巧 首先解释一下,中国移动提供 CMNET 和 CMWAP 两个 APN,本质上,这两个 APN 其实本应没 有区别,但是运营商从商业角度出发,从以下两方面来定位这二者: 1,采用不同的计费方式,不包月的情况下,CMNET 有可能收费更高 2,网络接入的授权不同,CMNET 能获得完全网络访问,就如 PC 直接上网一样,而 CMWAP 只能限制在 WAP(基本上可以等同于 HTTP,也就是只能访问 web service) 那么接下来,看黑莓上这两个 APN 有什么区别呢,显然,连 CMNET 能干更多的事,比如: 1, 长连接 2, 对端口的 socket 连接 而 CMWAP 由于计费便宜,因此用户可能的话,往往倾向于用这个 APN。不少应用为了达到 更好的体验,也将自己的应用首选或者甚至绑定 CMWAP 方式。 最后,来罗列一下中国移动的 WAP 情况下,有哪些连接方式。 WAP 1.x(国内的开发者,特别是从 J2ME 转过来的开发者,往往倾向于此) 1, 长连接或者 socket 连接 建议用: (SocketConnection)Connector.open("socket://testserver:600;deviceside=true;APN= cmnet"); 如果在选项-高级-TCP/IP 中填上了 CMNET,用下面这种方式也能成功 (SocketConnection)Connector.open("socket://testserver:600;deviceside=true"); 2, 用 CMWAP 访问 WAP 网站 (HttpConnection)Connector.open(http://wap.google.com;WAPGatewayIP=10.0.0.172;W APGatewayAPN=cmwap); P a g e | 8 这里,注意不要写“WapGatewayPort=80”(似乎很多人都容易犯这个错误),原因是系 统会根据关键字 http 自动识别应该用哪个网关端口,实际上在黑莓上这个值也不是 80, 而是 9201 3, 用 CMNET 访问 WAP 网站 (HttpConnection)Connector.open(http://wap.google.com;WAPGatewayIP=10.0.0.172;W APGatewayAPN=cmnet ); 或用 CMNET 访问任意网站 (HttpConnection)Connector.open( http://www.google.com;deviceside=true;APN=cmne t ) WAP 2.0 方式(这是我强烈推荐的) 可能大家也发现了,用 WAP1.x 非常之不灵活,代码也往往会变得很复杂很乱。 而其实黑莓上有更好更简洁的办法,就是通过 WAP 2.0 来访问,在上一节里面写了一个例 子,来说明如何通过 WAP2.0 接入网络。在这里解释一下: 打开选项-高级选项-服务预订中,能看到一项叫做 WAP2 Transport[WPTCP],点进去可以 看到以下信息 名称: WAP2 Transport UID: WAP2 trans CID: WPTCP 这些信息从哪里来的呢,这是黑莓上面的一个概念叫做 service book,每一项 service book 其实就是一套对手机服务进行描述的配置文件,在这里的项目 RIM 和中国移动合作的 时候准备好的配置项,由黑莓手机上市前预加载、或者手机启动时由运营商将配置发送到 手机上。 就这一项而言,UID 是描述服务的关键词,CID 是描述服务走的是什么网络通道。UID 为 WAP2 trans 就表示这是 WAP2.0 的 service book,而且基本上这个关键词是不会变的。这 样就好理解了上一节中对 WAP2.0 那一段的代码了。 P a g e | 9 其实有更简单的办法,用这个 URL 就直接达到效果了: http://www.google.com;DeviceSide=true;ConnectionUID=WAP2 trans P a g e | 10 3. BlackBerry 上中国电信的 WAP 网关连接分析 中国电信已经发布了 BlackBerry 9530,不同的是,这一款是烧号的,因此可以想象开发的时候还 是会遇到一些不同的。 1, C 网和 G 网的差别。中国电信的 CDMA,在 WAP 连接时没有 GPRS 上 APN 的概念。可是可能 有人要说,那么电信的“CTNET”和“CTWAP”是怎么一回事呢。其实这涉及到 CDMA 的底层, CDMA 是建立在 PPP 之上的,也就是手机拨号上网,那么可能经历过 modem 拨号年代的童鞋可能 还记得拨号的时候需要一个号码,还需要用户名和密码。那好,这里的 ctnet 和 ctwap 其实就是 这里的用户名。中国电信的服务端(一个叫做 PDSN 的东东)会对用户不同的用户名做配置, ctwap 被授予的权限显然被限制在 WAP,而 CTNET 则能获得完全的 TCP/IP 的权限。通过这一手 段,达到了中国移动 cmnet 和 cmwap 类似的效果。为什么这么做呢,我觉得应该是商业模式吧, 让移动转来的用户能更快适应。在国外,通常没有这么复杂。 2,那么 9530 上如何上网呢。如果你打开选项-高级-TCP/IP,那么你会发现 APN 是不可改的,也 就是说你没法配置 CTNET 或者 CTWAP 之类的东东。那么可以猜想,RIM 应该和中国电信有一种 协议,9530 会自动的通过某种帐号上网。通过调试,我发现肯定不会是 CTNET,很可能是类似 CTWAP 的帐号。 最后,好了,现在电信上支持的网络连接方式总结如下: 1,如果你希望通过 WAP 上网,采用 WAP2.0 的方式 2,如果你希望能访问 TCP,只能采用 BES 的方式(虽然国内黑莓用户大多都是纯水货,没有开通 BES,但是在9530上没有这个问题,买到烧号的9530的,应该都通过中国电信开通了企业 服务) P a g e | 11 4. BlackBerry 5.0 新提供的 Network API:自动选择网关的终 极解决方案 从前问来看,要想在 BlackBerry 上实现自动选择 WAP 来连接网络,似乎很复杂。但其实在 BlackBerry 5.0 上,这一切都已成为往事。随着 BlackBerry 5.0 的发布,其中包含了不少非常有用的 API,其中 Network API 就是用来提高网络编程效率的。Network API 包括以下类和接口: 包名:net.rim.device.api.io.transport 接口: ConnectionAttemptListener This interface prescribes methods for a class that listens for connection attempts performed by a ConnectionFactory object. 类: ConnectionDescriptor This class stores information about a Connection opened by using a ConnectionFactory. ConnectionFactory This class enables you to create HTTP, HTTPS, socket, TLS, and SSL connections over supported transports. TransportDescriptor This class encapsulates information related to a specific transport instance. TransportInfo This class provides methods that provide information about the transport types available on a BlackBerry device. 其中的 ConnectionFactory 可以帮开发人员自动选择多种连接方式,也可以设置重试次数,自动支 持 HTTPS 和 SSL/TLS。以下是一个例子: 根据自动搜索,第一条发现的通道(由系统来决定)来建立 HTTP 连接: // Create ConnectionFactory ConnectionFactory factory = new ConnectionFactory(); // use the factory to get a connection ConnectionDescriptor conDescriptor = factory.getConnection("http://www.blackberry.com");//不设任何参数 P a g e | 12 if ( conDescriptor != null ) { // connection succeeded int transportUsed = conDescriptor.getTransportDescriptor().getTransportType(); // using the connection HttpConnection httpCon = (HttpConnection) conDescriptor.getConnection(); ... } 设定一个连接方式列表,按照预设定的优先顺序来建立 HTTP 连接: // make a list of transport types ordered according to preference (they will be tried in succession) int[] preferredTransportTypes = {TransportInfo.TRANSPORT_MDS, TransportInfo.TRANSPORT_WAP2};//连接方式列表,第一优先:BES 方式,第二优先:WAP2 方式 // Create ConnectionFactory ConnectionFactory factory = new ConnectionFactory(); // Configure the factory factory.setPreferredTransportTypes( preferredTransportTypes ); // use the factory to get a connection ConnectionDescriptor conDescriptor = factory.getConnection("http://www.blackberry.com"); if ( conDescriptor != null ) { P a g e | 13 // connection suceeded int transportUsed = conDescriptor.getTransportDescriptor().getTransportType(); // using the connection HttpConnection httpCon = (HttpConnection) conDescriptor.getConnection(); ... } 设定一项特定的通道类型来建立 HTTP 连接: // Create ConnectionFactory ConnectionFactory factory = new ConnectionFactory(); // use the factory to get a connection ConnectionDescriptor conDescriptor = factory.getConnection("http://www.blackberry.com", TransportInfo.TRANSPORT_WAP2, null); if ( conDescriptor != null ) { // connection over WAP2 succeeded // using the connection HttpConnection httpCon = (HttpConnection) conDescriptor.getConnection(); ... } 设定一条具体的通道来建立 HTTP 连接: // Create ConnectionFactory ConnectionFactory factory = new ConnectionFactory(); P a g e | 14 // use the factory to get a connection ConnectionDescriptor conDescriptor = factory.getConnection("http://www.blackberry.com", TransportInfo.TRANSPORT_MDS, "S109234"); if ( conDescriptor != null ) { // connection to specific BES succeeded // using the connection HttpConnection httpCon = (HttpConnection) conDescriptor.getConnection(); ... } 5. 网络设置解决方案举例 以上介绍了各种网络连接的代码实现,但是国内黑莓开发的初学者在设计的时候,最头疼的一个 问题就是网络设置。 BlackBerry 与其他手机不同的是,多了好几种联网方式,如何把这些整合在你的设置里面,让 用户自己来选择希望的联网通道。国外很多成熟的应用这方面早就已经很完美的解决了。 这里拿国外某股票软件做例子,网络连接上这个应用是基于 Socket 的,而非 HTTP。 我们首先来看它的设置首页,提供四种方式:BIS,BES,TCP,WiFi 补充介绍一下,这个软件底层是基于 TCP/IP 的,否则的话,如果是基于 WAP 的软件,通常应该 再加上 WAP 的 APN 的设置。 P a g e | 15 另外,根据实际情况,有的软件可能需要加上“自动选择网络”,并将其设为默认选项。 P a g e | 16 6. HTTP 连接代码实例 以下这个例子不很完善,因为没有包括 WAP 和 WAP2 连接,但在前文已经做了很充分的 讨论,不过这段代码还是满足绝大部分情况下的需求,而且同时支持 GET 和 POST。 public static final int CONNECTION_DEFAULT = 0; public static final int CONNECTION_BIS = 1; public static final int CONNECTION_BES = 2; public static final int CONNECTION_TCPIP = 3; public static final int CONNECTION_WIFI = 4; public boolean get(String url) { service(url, null); } public boolean post(String url, URLEncodedPostData postdata) { service(url, postdata); } private boolean service(String url, URLEncodedPostData postdata) { HttpConnection con = null; InputStream in = null; try { if (CoverageInfo.isOutOfCoverage()) return false; P a g e | 17 byte[] data = null; if (postdata != null) { data = postdata.getBytes(); } // If WLAN is active, try wlan if ((RadioInfo.getActiveWAFs() & RadioInfo.WAF_WLAN) != 0) { try { if (CoverageInfo.isCoverageSufficient( CoverageInfo.COVERAGE_CARRIER, RadioInfo.WAF_WLAN, false)) { con = HttpUtils.makeHttpConnection( url, new HttpHeaders(), data, CONNECTION_WIFI); con.getResponseCode(); } } catch (Exception ex) { Log.error("UP.R.WF: " + ex); con = null; } } // Otherwise try the preferred connection type. if (con == null) { P a g e | 18 try { con = HttpUtils.makeHttpConnection( url, new HttpHeaders(), data, IOPreferences.getPreferredConnectionType()); con.getResponseCode(); } catch (Exception ex) { Log.error("UP.R1: " + ex); con = null; } } if (con == null) { // If preferred type doesnot work, find something else. int ctype = getCoverageBasedConnectionType(); if (ctype >= 0) { try { con = HttpUtils.makeHttpConnection(url, new HttpHeaders(), data, ctype); con.getResponseCode(); } catch (Exception ex) { Log.error("UP.R2: " + ex); con = null; } } } P a g e | 19 int respcode = con.getResponseCode(); // You can handle HTTP_MOVED_* here if you want to // We don't because we connect to our own servers if (respcode == HttpConnection.HTTP_OK) { in = con.openInputStream(); // Handle incoming data handleData(con, in); return true; } } catch (Exception e) { Log.error("UP.R() : " + e); } finally { close(con, in); } return false; } protected abstract void handleData(HttpConnection con, InputStream in); private static void close(HttpConnection con, InputStream in) { P a g e | 20 if (in != null) { try { in.close(); } catch (IOException e2) { } } if (con != null) { try { con.close(); } catch (IOException e) { } } } public static int getCoverageBasedConnectionType() { if (CoverageInfo.isCoverageSufficient(CoverageInfoProxy.COVERAGE_CARRIER, RadioInfo.WAF_WLAN)) { return IOConstants.CONNECTION_WIFI; } if (CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_BIS_B)) { return IOConstants.CONNECTION_BIS; } P a g e | 21 if (CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_MDS)) { return IOConstants.CONNECTION_BES; } return IOConstants.CONNECTION_DEFAULT; } Finally the method to make the http connection class HttpUtils { public static final int CONNECTION_DEFAULT = 0; public static final int CONNECTION_BIS = 1; public static final int CONNECTION_BES = 2; public static final int CONNECTION_TCPIP = 3; public static final int CONNECTION_WIFI = 4; /** * This method opens a HTTP connection to the given url. The method used is * GET or POST depending on whether postData is null or not. Only the * provided connType is used. For example, if the connType is * CONNECTION_BES, the connection is tried using the BES only. * The only time provided connection type is not used is when the URL * contains ";deviceside=". * P a g e | 22 * @param url * The url to connect to. * @param requestHeaders * The headers in the request. May be null or empty. * @param postData * Data to be posted to the server. If null, the GET method used * for the http connection. * @param connType * The type of transport (BES / BIS / WIFI / Default) to be used * for opening connection. * @return Opened HttpConnection object or null if some error occurs. */ public static HttpConnection makeHttpConnection(String url, HttpHeaders requestHeaders, byte[] postData, int connType) { HttpConnection conn = null; OutputStream out = null; if (StringUtilities.startsWithIgnoreCase(url, "www.")) { url = "http://" + url; } try { if (url.indexOf(";deviceside=") == -1) { switch (connType) { case CONNECTION_BES: P a g e | 23 url = url + ";deviceside=false"; break; case CONNECTION_BIS: url = url + ";XXXXXXXXXXXXXXXX"; break; case CONNECTION_TCPIP: url = url + ";deviceside=true"; break; case CONNECTION_WIFI: url = url + ";interface=wifi"; } } conn = (HttpConnection) Connector.open(url); if (requestHeaders != null) { String referer = requestHeaders.getPropertyValue("referer"); boolean sendReferrer = true; if (referer != null && StringUtilities.startsWithIgnoreCase(referer, "https:") && !StringUtilities.startsWithIgnoreCase(url, "https:")) { sendReferrer = false; } P a g e | 24 int size = requestHeaders.size(); for (int i = 0; i < size;) { String header = requestHeaders.getPropertyKey(i); // remove header if needed if (!sendReferrer && header.equals("referer")) { requestHeaders.removeProperty(i); --size; continue; } String value = requestHeaders.getPropertyValue(i++); if (value != null) { conn.setRequestProperty(header, value); } } } if (postData == null) { conn.setRequestMethod(HttpConnection.GET); conn.setRequestProperty("User-Agent", "Profile/MIDP-2.0 Configuration/CLDC-1.0"); } else { conn.setRequestMethod(HttpConnection.POST); conn.setRequestProperty( P a g e | 25 HttpProtocolConstants.HEADER_CONTENT_LENGTH, String.valueOf(postData.length)); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setRequestProperty("User-Agent", "Profile/MIDP-2.0 Configuration/CLDC-1.0"); out = conn.openOutputStream(); out.write(postData); out.flush(); } } catch (IOException e1) { Log.error("UTIL.HTC " + e1); close(conn, null); // Close the connection conn = null; } finally { close(null, out); // Close the output, but keep connection open } return conn; } private static void close(HttpConnection con, OutputStream out) { if (out != null) { try { P a g e | 26 out.close(); } catch (IOException e2) { } } if (con != null) { try { con.close(); } catch (IOException e) { } } } }
还剩25页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享pdf获得金币 ] 1 人已下载

下载pdf

pdf贡献者

BlackBerry

贡献于2010-09-16

下载需要 20 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf