CMNET和CMWAP网络连接

J2me上的GPRS网络接入点有两个,一个就是CMNET,一个就是CMWAP。一般移动梦网,百宝箱就是挂在CMWAP上的。

CMNET

CMNET就是一般的互联网的网络接入点。我们一般在SUN那里看到的J2ME实例代码都是基于CMNET接入点的网络下载。一般来说,CMNET可以连接所有的网络站点。下面就是我在开发仙剑的CMNET的下载资源包的代码片断。

/***************************************************************************

* 下载网络文件

* @param url String 要下载的文件的地址URL

* @return byte[] 如果下载成功,返回文件的字节缓冲;

* 如果下载失败,返回null

*/

public byte[] download_CMNET(String url) {

ContentConnection c;

InputStream is = null;

byte[] data = null;

try {

c= (ContentConnection)Connector.open("http://"+ServerName+"/"+url,Connector.READ,true);

is = c.openInputStream();

int dataLength = (int) c.getLength();

if (dataLength == -1) {

ByteArrayOutputStream bStrm = new ByteArrayOutputStream();

int ch;

while ( (ch = is.read()) != -1)

bStrm.write(ch);

data = bStrm.toByteArray();

bStrm.close();

}

else {

data = new byte[dataLength];

Data_Read_Buf(is,data,0,dataLength);

}

is.close();

c.close();

}catch (Exception e) {

data = null;

}

c= null;

return data;

}

代码很普通,不过需要注意的有以下两点。

1. getLength()并不是一定都有效,比如我发现在Nokia 40的手机上这个函数有效,但是在Nokia 60的手机上,这个函数通常都无效。所以我们通常都必须要有另外一个下载方式,既是从while循环不断从输入流中一个一个读byte,如果读出为-1,则表示输入流数据读完。

2. Connector.open最后一个参数是表示是否允许timeout,通常我们都得设置true,因为GPRS网路经常中断,那么必须有个timeout来退出连接。依照我的经验,一般如果能够执行完c.openInputStream()后,就表明连接上了,只要连接上后,数据的下载是比较快的(也就是说网络连接是最慢的)。

CMWAP

MOTO的手机通常都是默认接入点就是CMWAP。在手机上的“网页“->”网页设定”中可以设置默认的接入点。一般中国大陆的出产的MOTO手机都是设置的”移动梦网”的网络接入点,起始就是CMWAP接入点。而MOTO最可恶的一点就是一旦默认接入点是CMWAP,那么所有的J2ME应用程序都无法访问CMNET。而Nokia 40虽然默认的接入点是CMWAP,但是运行中可以自动检查CMNET来访问CMNET。

解决MOTO的网络连接有两个办法:

1. 在”网页“->”网页设定”中新建一个网页,然后不需要填写任何参数,设置成默认后,它就可以让手机上J2ME程序通过CMNET访问网络了。

2. 通过移动的代理来实现通过CMWAP访问互联网。下面是仙剑中使用CMWAP下载资源包的代码片断:

/***************************************************************************

* 下载网络文件

* @param url String 要下载的文件的地址URL

* @return byte[] 如果下载成功,返回文件的字节缓冲;

* 如果下载失败,返回null

*/

public byte[] download_CMWAP(String url) {

HttpConnection c;

InputStream is = null;



byte[] data = null;

try {

c= (HttpConnection)Connector.open("http://10.0.0.172:80/"+url,Connector.READ,true);

c.setRequestProperty("X-Online-Host",ServerName);

c.setRequestProperty("Accept", "*/*");

is = c.openInputStream();

int dataLength = (int) c.getLength();

if (dataLength == -1) {

ByteArrayOutputStream bStrm = new ByteArrayOutputStream();

int ch;

while ( (ch = is.read()) != -1)

bStrm.write(ch);

data = bStrm.toByteArray();

bStrm.close();

}

else {

data = new byte[dataLength];

Data_Read_Buf(is,data,0,dataLength);

}

is.close();

c.close();

}catch (Exception e) {

data = null;

}

c= null;

return data;

}

比如我们要下载

http://xxx.xxx.xxx.xxx/BB/AA.dat,那么上面的ServerName= ”xxx.xxx.xxx.xxx”, 而url = ”BB/AA.dat”
///


要清楚的知道所谓的cmwap和cmnet只是中国移动(CMCC)为了它的计费方便,给用户提供的两个手机上网接入点。cmwap是按包月计费的,前提是该用户已经加入某一个包月服务(废话>>*&^%^%)。cmnet是按照流量来计算的,也有谣传说cmnet中国移动无法计费的,不管怎么说,这种接入方式,玩家的荷包要大出血的,我的好多机友都有惨痛经历,在此深表同情。
现在清楚了cmwap和cmnet的区别之后,我给大家再说说对于开发人员来说,具体实现的做法。
一般我们现在程序开发的时候有3种方式:
①HTTP协议,直接联网。
如果url是“www.mySpace.com/test”,那么程序如下:
httpConnection = (HttpConnection)Connector.open(“http://www.mySpace.com/test”);
②HTTP协议,需要添加主机头域"X-Online-Host"和分隔符头域“Accept”。
如果url是“www.mySpace.com/test”,那么程序如下:
httpConnection = (HttpConnection)Connector.open(“http://10.0.0.172:80/test”);
httpConnection.setRequestProperty("X-Online-Host", “www.mySpace.com”);
httpConnection.setRequestProperty("Accept", "*/*");
③SOCKET协议。
如果url是“www.mySpace.com/test”,那么程序如下:
socketConnection = (SocketConnection)Connector.open(“socket://www.mySpace.com/test”);
OK,这就是我们最普遍的程序联网的方式。那么和cmwap与cmnet有什么区别呢?现在我就说明。
cmwap只可以走HTTP协议,也就是第三种方式不可行。cmnet既可以走HTTP协议,又可以走SOCKET协议,只是无论如何都需要直接连接url,即第二种不可行。于是现在问题出来了,按照我们程序员的认识open(url)就足以,为什么会闹出来一个添加头域信息的呢?而且同属于在cmwap下面的联网方式的?这里我是这么理解的,部分手机终端厂商为了迎合中国移动的要求,部分手机里面内置了一个程序,一旦检测到接入点是cmwap的,它就会把当前请求的url分解,怎么分解的呢?就是第二种方式的分解,把主机和主机后面的url分别写入不同的位置了,在头域里面标记了主机的内容和一个分隔符的标志。说到这里,部分读者就会明白了,如果说部分手机有这么一套机制可以分解url,那么那些没有这个机制的,就只有程序员自己实现了,于是出现了第二种方式。是的,至少我想是这个原因。
所以,这就解释了两个问题:
1、为什么有些手机,比如NOKIA7210,用第二种方式不能够联网?因为手机自己会解析请求的url,如果按照第二种方式写的话,那么url又被解析一次,就成了
httpConnection = (HttpConnection)Connector.open(“10.0.0.172:80/test”);
httpConnection.setRequestProperty("X-Online-Host", “10.0.0.172:80”);
httpConnection.setRequestProperty("Accept", "*/*");
成了请求移动网关了,它怎么会有你的test呢?不可能成功的,除非是赶巧了。那就要恭喜你中奖了,可以给移动来点XXXX的了。
2、选择cmwap联网的手机实际上最终请求的是“10.0.0.172:80”这个url,即移动的网关。这就知道了移动如何计费的了。走它的网关,它想怎么计费还不行啊。
现在需要重点说明的是,HTTP协议的直接请求url的方式,不是cmwap专有的,上面说了cmnet也可以。
那么怎么区分是cmwap还是cmnet呢?
我的观点是:程序员不可能实现的。因为这个是手机自己的特性。它要么是在出厂的时候就被限制死了,要么就是像部分智能手机那样可以让用户自己选择。所以,程序员无法控制这些。除非你事先知道了这个手机的这个特性,否则你要是硬来的话,就像7210上面采用第二种方式联网,那么你就别想连接成功了。
现在说说GPRS,这个大家已经很熟悉的词汇。在此还是要感谢一下阿里山和xinian_yang,因为是阿兄给我讲明白的这些,而xinian_yang兄则是这个话题的发起者,嘿嘿,我是组织者。要说有啥好讲的,GPRS不就是上网吗?原来网不是这么好上的,GPRS是分级别的!我们都知道手机有自己的语音通道,那么上网走的是手机的什么通道?通过阿兄的讲解,我知道了,GPRS的10级才可以上网和语音双通道同时开通,比这个级别低的,都只有一个通道单独存在,即要么上网要么通话,当低级别的GPRS在上网的时候,如果有电话打入的话,你会听到的是占线的提示音,叮咚!这下子大家知道,为商在上网的时候会接不到电话或者短消息了吧?哈哈。
似乎没有什么要说的,有!就是关于数据传输方面的。
网上的,或者说sun提供的,大部分是这样的:
connection = (XXXXXXXX)Connector.open(“10.0.0.172:80/test”);
os = connection.openOutputStream();
is = connection.openInputStream();
os.writeByte(...);
in ch = 0;
while(ch != 1){
ch = is.read();
}
好的,就是这样,当然,里面的部分实例会依据不同的协议有所区别。那么,我的问题是,谁会用这个糟糕的东西去实现自己的应用的???当然,这个只是例子,目的是告诉大家该怎么用这些API,呵呵,所以,大家千万别以为sun出的就一定是真理!不是的!
通过我的研究,我发现在数据传输方面有六种方式(没有算上sun提供的那种方式):
①HTTP的协议,参数写在头域中。
②HTTP的GET方式。
③HTTP的POST方式,但是在BODY中自己实现了名值对的组合,即param1=XX&param2=XX的参数传输形式,采用的API是DataOutputStream的writeUTF()。
④HTTP的POST方式,但是在BODY中是按照和服务器端事先约定好的数据传输格式传输的,采用的API是DataOutputStream的write()、writeByte()、writeUTF()、writeInt()、writeShort()等等方法。
⑤SOCKET的方式,这个时候就需要为网络部分建立两个线程,一个用来发送数据,采用名值对,我这里不多说了,另一个是接收数据,用的是DataInputStream的read()、readByte()、readUTF()、readInt()、readShort()等等方法。
⑥SOCKET的方式,这个时候仍然需要为网络部分建立两个线程,一个用来发送数据,用的是第三种方式提到的那些API,另一个是接收数据,用的是DataInputStream的read()、readByte()、readUTF()、readInt()、readShort()等等方法。
HTTP的协议的接受方式有两种:
①读取服务器反馈信息的头域,用的是HttpConnection的getHeaderField()方法。
②读取服务器反馈信息的BODY,用的是DataInputStream的read()、readByte()、readUTF()、readInt()、readShort()等等方法。
我个人主张的在程序里面实现第四种和第六种方式接收数据采用读取BODY的内容。因为这两个在结构上是相似的,容易架构在一起,而且解析的过程也比较简单,不用找分隔符,而且可以传输大容量的数据。只是,它也有缺点,就是采用这种方式的话,需要服务器端的开发人员和客户端的开发人员,事先定义好他们之间的数据传输的格式。
以登陆为例,就是要定义是先传登陆名还是先传密码,登陆名是什么数据类型,是byte还是int还是String的,密码是什么类型,是byte还是int还是String的。
就是这堆东西。由于不同的应用这些东西可能不一样,所以需要为每一个都量身定做。似乎这样很麻烦的。呵呵,其实不是的,至少我认为不是,因为我们可以把那些通用的都整理出来,这样也就便于维护了,比如登陆、注册、更新数据等等,这些几乎每一个应用都有,也几乎每一个都是一个样子的,那么在第一次定义好之后,以后就不用再定义了,那不是省事了,哈哈。
这里忘记了一个最重要的东西,那就是,一定要告诉服务器你打算做什么!就是你要登陆还是注册,这个一般都是用数字表示的。按照我的想法,也应该是写在BODY里面的,我一般都是使用int类型的,表示事件的ID。
最后,再说说网络框架的搭建。我的建议是为网络开两个线程(这样加上应用主线程,总共就是三个线程,别害怕啊,呵呵)。一个是发送数据的线程,一个是接收数据的线程。大家一看,诶?和上面的SOCKET的方式一样。呵呵,是的,这就是为了把HTTP协议的和SOCKET协议的整合在一起做出的妥协。具体的原因,我就不多说了,大家亲自做的时候,可以体会到的。我要说的就是一个实现的大体思路。其实很简单的,就是在线程中灵活使用wait()和notify()这两个方法。就这么简单,呵呵,别的不多说了,打得手累!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值