(源自:http://www.j2medev.com/Article/Class1/Class14/200804/5259.html)
J2me提供了HttpConnection和SocketConnection。前者从API级提供了对http协议的支持,而后者只是单纯的一个socket流API。
实际上对于使用Http协议的上层应用来说,网络层使用Http或者Socke应该是透明的。作为框架,应该为用户提供方便的选择方式。
J2me中对HTTP 1.2中定义的KEEPALIVE支持得并不是很好,并不能获得一个http的长连接。对于JSE中http connection的实现还没验证。
使用Socket的一个好处是可以避免为每个网络请求建立单独的Connection。并且因为socket的inputStream, outputStream是双工的方式工作,所以可以连续的发送网络请求而不必等待网络回应。
如果让socket的inputStream/outputStream以双工的方式工作(即:通过outputStream发送之后不等待inputStream的response),那么需要应用层的协议进行支持。比如,需要对发出的网络请求进行编号,这样从inputstream中获得response的时候可以把这个回应匹配到正确的请求上。实现双工,很直接的想法是inputStream、outputStream分别由单独的一个线程处理。
这个工具包应该能够做到支持:
- Http GET/POST;
- File download;
- 可以选择使用socket或是Http;
- 支持同步/异步(阻塞/非阻塞)发送请求,提供timeout机制;
- 清晰的异常定义,方便上层进行错误处理。
- 对于socket是否可用连接池?[对于j2me程序似乎没有必要]
Connection中的receive, send都是阻塞的。如果要实现异步收发,需要在一个单独的线程中处理请求。
以下是通过把阻塞的API放在一个线程中运行而获得的异步效果。理想状态下,在到达了Timeout事件后,应该把这个线程关闭。但是因为这个线程中存在阻塞方法,所以不能及时地获得停止通知(很难终止这个线程)。这个网络请求很可能会在你设置的timeout时间后获得网络response。为了处理timeout之后仍旧获得网络数据的情况,可以先把异步请求的数据对象放到一个队列中,如果网络回应在timeout之前返回,则从这个队列中查找该对象并移走这个对象; 当timeout之后,也应该把这个对象移走(可考虑放到另一个队列进行其它的特殊处理),这样当延迟的网络回应到来后,从队列中找不到对象,就不做处理。
sendRequestSynch是同步的API。 sendRequest()会调用同步方法,因此放在一个单独的线程中运行。匿名线程t启动之后,通过request.wait()方法,使调用sendRequestSynch()的方法被阻塞。从request.wait()方法恢复之后,判断是否timeout发生。
public void sendRequestSynch(final NetworkRequest request, long timeout) throws Exception {
synchronized (request) {
Thread t = new Thread() {
public void run() {
try {
log.debug("send request in another thread.");
sendRequest(request);
}catch (Exception e) {
log.warn(e.getMessage());
}
log.debug("Exit the thread.");
}
};
t.start();
log.debug("Try to wait for " + timeout);
request.wait(timeout);
if (request.isDelayed()) {
queue.removeElement(request);
timeoutQueue.addElement(request);
log.debug("Request TIMEOUT !!!!!!!!!!!!!!!!");
log.debug("Stop the network request thread for the timeout.");
}else {
log.debug("Be Notified and wakeup.");
}
}
}
sendRequestAsynch是异步的API。这个是写作过程中的一个版本。在review之后发现其中存在同步问题。 synchronized (request) 不是在启动孙子线程tt之后执行。问题在于如果sendRequest(request)在执行到最后是发送request.notifyAll()时,可能还没有执行 synchronized (request) 。这样就存在request.wait(timeout)不会被唤醒而超时的问题。
public void sendRequestAsynch(final NetworkRequest request, final long timeout) throws Exception {
Thread t = new Thread() {
public void run() {
try {
log.debug("send request in another thread.");
Thread tt = new Thread() {
public void run() {
try {
log.debug("send request in grandson thread. " + request.getSequenceId());
sendRequest(request);
}catch (Exception e) {
log.warn(e.getMessage());
}
}
};
tt.start();
synchronized (request) {
log.debug("Try to wait for " + timeout + " " + request.getSequenceId());
request.wait(timeout);
if (request.isDelayed()) {
queue.removeElement(request);
timeoutQueue.addElement(request);
log.debug("Request TIMEOUT !!!!!!!!!!!!!!!!");
log.debug("Stop the network request thread for the timeout.");
}else {
log.debug("Be Notified and wakeup.");
}
}
}catch (Exception e) {
log.warn(e.getMessage());
}
log.debug("Exit the thread.");
}
};
t.start();
}
应当把sendRequest(request) 放到同步块中。改正后的代码:
public void sendRequestAsynch(final NetworkRequest request, final long timeout) throws Exception {
Thread t = new Thread() {
public void run() {
try {
log.debug("send request in another thread.");
synchronized (request) {
Thread tt = new Thread() {
public void run() {
try {
log.debug("send request in grandson thread. " + request.getSequenceId());
sendRequest(request);
}catch (Exception e) {
log.warn(e.getMessage());
}
}
};
tt.start();
log.debug("Try to wait for " + timeout + " " + request.getSequenceId());
request.wait(timeout);
if (request.isDelayed()) {
queue.removeElement(request);
timeoutQueue.addElement(request);
log.debug("Request TIMEOUT !!!!!!!!!!!!!!!!");
log.debug("Stop the network request thread for the timeout.");
}else {
log.debug("Be Notified and wakeup.");
}
}
}catch (Exception e) {
log.warn(e.getMessage());
}
log.debug("Exit the thread.");
}
};
t.start();
}
[原文发布在: http://docs.google.com/Doc?id=dd6zq46t_419gwrb84hk 代码下载: http://j2mekit.googlecode.com/svn/trunk/ j2mekit-read-only ]