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();
}
exception的處理還沒有規范起來。許多細節還待完善(post還沒有被支持),...