[eelaix原作文章,转贴请注明出处]
作者:caigao@hotmail.com
2004-7
我在应用开发过程中,常常用到J2ME的网络应用,但限于J2ME无线设备的能力,我们不得不在可用性和性能之间作出选择,因此,我做了以下的试验,并由此总结出一点结论,以方便后来者不必再走弯路。
我的应用主要功能是测试J2ME设备的网络连接性能,因此,其他方面的测试概不涉及。
我们知道,J2ME的通用连接框架为我们提供了http/socket/数据报/本地文件/线外等连接方式,但由于具体设备实现不同而导致程序开发方面的不便。举例来说,J2ME 的 MIDP 1.0版本中没有规定socket是必须的,但到了MIDP 2.0 socket才成为必须实现的协议。因此,现阶段大多数流行的设备都没有socket连接,这给我们编程带来了不利因素。在我们的测试程序中,SOCKET测试采用了StreamConnection类来建立连接,而走的是socket协议。分析一下程序即可知。
本文涉及了多种协议情况下的数据上传与下载,中文上传下载处理及网络传输计时、数据加密解密等方面的内容。为了做到通用,我在该程序的实现中没有用到特定设备的类。
本文给出的测试数据中第一个是建立连接所使用的时间,其余9项是连接建立后的数据传送时间。最后一项是前面9项数据的平均值。
测试结果数据比较:
机型,所用时间(秒),连接次数
- SUN WTK 模拟器
HTTP 1.391 0.188 0.625 0.141 0.141 0.391 0.313 0.204 0.375 0.484 0.318
SOCK 0.500 0.531 0.157 0.531 0.360 0.172 0.313 0.641 0.437 0.266 0.378 - Nokia6610
HTTP 4.258 1.682 1.612 1.373 2.681 1.820 1.264 1.512 1.919 1.808 1.741
SOCK 未通过 - Nokia 7650
HTTP 7.812 2.790 2.641 2.500 2.630 3.922 2.438 2.672 2.219 2.594 2.712
SOCK 1.719 1.312 1.609 3.359 3.360 1.375 1.781 3.313 1.250 1.329 2.076 - Nokia 6600
HTTP 3.937 1.984 2.172 1.641 1.500 1.531 2.125 1.906 1.843 2.156 1.873
SOCK 3.188 1.281 1.406 1.266 1.833 1.688 1.719 1.688 1.625 1.703 1.579 - 索爱P802
HTTP 7.484 1.484 1.500 1.859 1.485 1.500 1.938 1.781 1.938 1.701 1.687
SOCK 1.360 1.328 1.281 1.625 1.406 1.592 1.140 1.281 1.312 1.297 1.362
从以上数据分析,我认为:
1、 每一款机器每一种连接方式在同一时刻的网络连接速度波动都较大,即每一次连接都可能有导致网络连接滞后的不确定因素;
2、 无论哪一种连接方式,无线设备的网络连接速度比PC连接要慢;
3、 当前网络连接的时间超过了游戏者可接受的延迟,不适合做实时或即时性操作;
4、 随着中国移动EDGE的上线,或者联通公司的CDMA1X普及,实时性手机应用将会得到发展,但仍需要进一步测试;
5、 在无线应用网络连接术语中有呼叫建立延迟(Call Setup latency)和通话信号传输延迟(Intra-call latency)两种,从以上数据可以看出,第一种延迟要比后一种延迟要明显一些,即第一次连接时用户需要等待更多的时间;
以下是本文的代码片断:
public class Game extends MIDlet implements CommandListener {
private Display display;
private Form fmHTTP;
private Form fmHTTP2;
private Form fmStream;
private Form fmSocket;
private Command cmdTest = new Command("测试", Command.BACK, 0);
private Command cmdStream = new Command("STREAM", Command.ITEM, 1);
private Command cmdSocket = new Command("SOCKET", Command.ITEM, 2);
private Command cmdHTTP = new Command("HTTP", Command.ITEM, 3);
private TestThread testThread;
public Game() {
display = Display.getDisplay(this);
fmHTTP = new Form("1.HTTP测试");
fmHTTP.addCommand(cmdStream);
fmHTTP.addCommand(cmdSocket);
fmHTTP.addCommand(cmdHTTP2);
fmHTTP.addCommand(cmdExit);
fmHTTP.setCommandListener(this);
fmHTTP.append("HTTP协议网络测试工具用于检测手机是否具备HTTP网络连接能力。第一行数据是由服务器产生的,经过UTF编码和DES加密传输到手机上的。/n");
fmStream = new Form("3.STREAM测试");
fmStream.addCommand(cmdHTTP);
fmStream.addCommand(cmdHTTP2);
fmStream.addCommand(cmdSocket);
fmStream.setCommandListener(this);
fmStream.append("STREAM网络连接是走的socket端口,对应的服务器端口是10000。/n");
testThread = new TestThread(getAppProperty("Server").toLowerCase().trim());
}
class TestThread implements Runnable {
private String server;
private String message;
private Form curForm;
private int responseCode;
private InputStream is2;
private HttpConnection conHttp;
private String keyOptimize;
private HttpConnection conOptimize;
public TestThread(String server) {
this.server = server;
}
public void start(Form curForm) {
this.curForm = curForm;
Thread t = new Thread(this);
t.start();
}
public void run() {
message = "";
long start,timeused;
int formid = Integer.parseInt(curForm.getTitle().substring(0,1));
start = System.currentTimeMillis(); //网络用时测试起点
if (formid==1) { //1.HTTP测试
try {
String key = genPassword(); //产生8位的随机字符串密码,用于返回给服务器进行DES加密
conHttp = (HttpConnection) Connector.open("http://" + server + ":8080/ns/hellodes?r=" + key);
responseCode = conHttp.getResponseCode();
if (responseCode==HttpConnection.HTTP_OK){
InputStream is = conHttp.openInputStream();
int len = (int) conHttp.getLength();
byte[] bytestr = new byte[len];
is.read(bytestr); //直接从输入流中读取字节,
cipher = new DesCipher(key.getBytes());
for (int i = 0; i < bytestr.length / 8; i++) {
cipher.decrypt(bytestr, i * 8, bytestr, i * 8); //解密;
}
message = gbDecode(new String(bytestr)); //解码
is.close();
is = null;
}else{
message = "服务器无法连接!" + responseCode;
}
} catch (Exception e) {
message = e.getMessage();
}
} else if (formid==3) { //3.STREAM测试
try {
conStream = (StreamConnection) Connector.open("socket://" + server + ":10000", Connector.READ_WRITE);
InputStream is = conStream.openInputStream();
byte[] outs = new byte[20];
int len = 0, ch;
while ((ch = is.read()) != -1) {
outs[len] = (byte) ch;
len++;
}
byte[] bytestr = new byte[len];
System.arraycopy(outs, 0, bytestr, 0, len);
message = gbDecode(new String(bytestr));
is.close();
is = null;
} catch (Exception e) {
message = e.getMessage();
}
} else if (formid==4) { //SOCKET测试
/*
try {
sck = (SocketConnection)Connector.open("socket://"+server+":10009",Connector.READ_WRITE);
sck.setSocketOption(SocketConnection.LINGER,5);
dis = sck.openDataInputStream();
message = dis.readUTF();
dis.close();
sck.close();
dis = null;
sck = null;
} catch (Exception e) {
message = e.getMessage();
}
*/
message = "SOCKET连接只能在MIDP2.0中使用";
}
timeused = System.currentTimeMillis() - start;
curForm.append(message+"/n");
message = "网络用时 " + timeused / 1000 + "." + timeused % 1000 + "/n";
curForm.append(new StringItem(null,message)); //HTTP连接结果为1.6-1.9秒之间
}
private String alpha =
"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private String genPassword() {
StringBuffer tmpstr = new StringBuffer(8);
int iRandNum;
Random rnd = new Random();
for (int i = 0; i < 8; i++) {
iRandNum = Math.abs(rnd.nextInt()) % 62;
tmpstr.append(alpha.substring(iRandNum, iRandNum + 1));
}
return tmpstr.toString();
}
private String gbDecode(final String dataStr) {
int start = 0;
int end = 0;
final StringBuffer buffer = new StringBuffer();
while (start > -1) {
end = dataStr.indexOf("//u", start + 2);
String charStr = "";
if (end == -1) {
charStr = dataStr.substring(start + 2, dataStr.length());
} else {
charStr = dataStr.substring(start + 2, end);
}
char letter = (char) Integer.parseInt(charStr, 16); // 16进制parse整形字符串。
buffer.append(new Character(letter).toString());
start = end;
}
return buffer.toString();
}
}