封裝MIDP 1.0 HttpConnection用于商業(yè)應(yīng)用[javaME]
Posted on 2005-12-01 18:19 讓變化成為計(jì)劃的一部分 閱讀(834) 評(píng)論(0) 編輯 收藏 所屬分類: J2ME一、“NetConnection”簡(jiǎn)介:
轉(zhuǎn)述Matrix上zhengyun_ustc所述:“你的HttpConnection是否封裝的足夠健壯呢?遇到各種情況,你是否有信心應(yīng)對(duì)呢?譬如說,你要請(qǐng)求的Response包實(shí)在太大,以至于運(yùn)營(yíng)商給你掐了告訴你說超時(shí);譬如說你是不是總要自己寫一個(gè)線程來專門作http連接?譬如說有一些移動(dòng)運(yùn)營(yíng)商設(shè)置了caching proxy servers,妨礙了你的測(cè)試。”
為了解決這個(gè)問題,一位日本程序員“JAY-F”針對(duì)MIDP1.0提供了一種robust的“NetConnection”封裝。這個(gè)HttpConnnection類負(fù)責(zé)管理連接并易于使用。
二、“NetConnection”特性:
1. 跨過Proxy-server阻礙:
一些移動(dòng)網(wǎng)絡(luò)放置了代理服務(wù)器用來提高訪問速度,但是它的cache也成為了開發(fā)人員測(cè)試/調(diào)試程序的一大障礙。“NetConnection”類使用一個(gè)簡(jiǎn)單的http request屬性將server上的代理功能關(guān)閉掉。
2. 使用線程分離的連接模式:
本類可以使用單線程、多線程兩種模式運(yùn)行,只要設(shè)置一個(gè)簡(jiǎn)單的標(biāo)志即可。
3. 支持Http request range:
由于服務(wù)商在其網(wǎng)絡(luò)上可能存在一些針對(duì)回應(yīng)數(shù)據(jù)最大長(zhǎng)度的限制,所以“NetConnection”類提供了構(gòu)造request URL的功能使回應(yīng)數(shù)據(jù)分為多個(gè)數(shù)據(jù)包。從而去除了前面的限制。
三、netConnection是如何實(shí)現(xiàn)的?
1。netConnection類結(jié)構(gòu)分析:
此類實(shí)現(xiàn)了Runnable接口,其運(yùn)行模式支持多線程模式:當(dāng)前只能由一個(gè)線程使用資源,其它線程wait。
此類使用了一些靜態(tài)成員變量:
//當(dāng)前只能由一個(gè)線程使用singleton。
private static NetConnection singleton = new NetConnection();
private static HttpConnection httpConn;
private static String url;
private static String method;
private static byte[] data;
private static String contentType;
private static long lowRange;
private static long highRange;
private static boolean disableProxy;
private static boolean detached;
private static byte[] response;
類方法:
//線程run方法
public void run()
//當(dāng)前運(yùn)行的線程執(zhí)行完畢后,通報(bào)給其它的由于等待資源而wait狀態(tài)的線程
private synchronized void forceNotify()
//當(dāng)資源正在被其它線程使用時(shí),當(dāng)前線程進(jìn)入wait狀態(tài)
private synchronized void forceWait()
//關(guān)閉http連接
private static void severConnection()
由于使用了這些static成員變量,所以一些操作方法需要同步(synchronized)。
2。netConnection核心代碼解析:
netConnection類的實(shí)現(xiàn)思想很簡(jiǎn)單,就是設(shè)置一些request屬性和對(duì)于GET方法構(gòu)造一個(gè)特殊的URL。更重要的是其作者對(duì)http協(xié)議的深入理解、嚴(yán)謹(jǐn)?shù)拇a風(fēng)格值得吾輩學(xué)習(xí)、研究。這也是本人分析其核心代碼的一大原因。
/**
* 實(shí)現(xiàn)了連接邏輯。
* 調(diào)用者可以在分離的線程中使用netConnection類的靜態(tài)連接。
* @throws IllegalStateException 如果此方法直接其它類調(diào)用則拋出該異常
*/
public void run() {
if (url == null) {
throw new IllegalStateException("Cannot invoke this method!");
}
DataOutputStream dos = null;
DataInputStream dis = null;
StringBuffer buffer = null;
try {
int permissions = 0;
//根據(jù)method值,設(shè)置Connector的權(quán)限(READ/READ_WRITE)
if (HttpConnection.GET.equals(method)) {
permissions = Connector.READ;
} else if (HttpConnection.POST.equals(method)) {
permissions = Connector.READ_WRITE;
}
//如果關(guān)閉server代理功能,則構(gòu)造noProxyUrl。
//原理:使用timestamp作為該URL中no-proxy參數(shù)值,
// 致使server視其為client發(fā)來的新請(qǐng)求。
if (disableProxy) {
boolean hasQueryParams = false;
char[] ca = url.toCharArray();
//判斷原URL中是否含有參數(shù)
for (int loop = 0; loop < url.length(); loop++) {
if (ca[loop] == '?') {
hasQueryParams = true;
break;
}
}
//由于需要多次字符串拼接,所以使用可提供效率的StringBuffer類
StringBuffer noProxyUrl = new StringBuffer();
//將原URL內(nèi)容復(fù)制到noProxyUrl
noProxyUrl.append(url);
//如果原URL中含有參數(shù),
// 則需要在noProxyUrl中增加"&",
// 否則直接在noProxyUrl中增加"?",
// 這樣做為了后面增加no-proxy參數(shù)做準(zhǔn)備。
if (hasQueryParams) {
noProxyUrl.append("&");
} else {
noProxyUrl.append("?");
}
//增加no-proxy參數(shù)
noProxyUrl.append("no-proxy=");
noProxyUrl.append(System.currentTimeMillis()); // timestamp
//將構(gòu)造好的noProxyUrl復(fù)制到原URL
url = noProxyUrl.toString();
}
// 打開Http 連接
httpConn = (HttpConnection) Connector.open(url, permissions, true);
//設(shè)置request方法
httpConn.setRequestMethod(method);
//如果request權(quán)限為READ(即request方法為GET),
//則需要設(shè)置http request屬性的Range。
//原理:設(shè)置http request屬性的Range后的,
// server接收到該request后將把response數(shù)據(jù)分成小部分發(fā)回。
// 從而避免了部分運(yùn)營(yíng)商對(duì)http response size的限制。
if (permissions == Connector.READ) {
if (lowRange > -1 && lowRange < highRange) {
StringBuffer range = new StringBuffer();
range.append("bytes=");
range.append(lowRange);
range.append("-");
range.append(highRange);
httpConn.setRequestProperty("Range", range.toString());
}
//否則,request權(quán)限為READ_WRITE(即request方法為POST),
//那么設(shè)置request的Content-Type屬性。
} else if (permissions == Connector.READ_WRITE) {
// POST request
httpConn.setRequestProperty("Content-Type", contentType);
dos = httpConn.openDataOutputStream();
dos.write(data);
}
} catch (Exception e) {
exceptionPipe = e;
//如果程序運(yùn)行在多線程模式,則在異常發(fā)生后需要喚醒其它睡眠的線程繼續(xù)run
if (detached) {
forceNotify();
}
return;
} finally {
try {
try {
if (dos != null) {
// 關(guān)閉dos
dos.close();
}
} catch (Exception e) {
// 如果程序運(yùn)行在多線程模式,則在異常發(fā)生后需要喚醒其它睡眠的線程繼續(xù)run
if (exceptionPipe == null) {
exceptionPipe = e;
if (detached) {
forceNotify();
}
return;
}
} finally {
dos = null;
}
// 讀取http連接的回應(yīng)代碼
int responseCode = httpConn.getResponseCode();
//當(dāng)request方法為GET,并設(shè)置了request range時(shí),接收到的回應(yīng)代碼為HTTP_PARTIAL
//當(dāng)request方法為POST,接收到的回應(yīng)代碼為HTTP_OK
//如果上述兩種回應(yīng)代碼均沒有收到,則表明連接失敗或者出問題
if (responseCode != HttpConnection.HTTP_OK
&& responseCode != HttpConnection.HTTP_PARTIAL) {
if (exceptionPipe == null) {
StringBuffer errorCode = new StringBuffer();
errorCode.append("Response code from server: ");
errorCode.append(responseCode);
errorCode.append("\nMessage: [");
errorCode.append(httpConn.getResponseMessage());
errorCode.append("]");
exceptionPipe = new IOException(errorCode.toString());
if (detached) {
forceNotify();
}
return;
}
}
//如果收到了上述的兩種回應(yīng)代碼之一,則可以繼續(xù)讀取server的response數(shù)據(jù)
dis = httpConn.openDataInputStream();
//循環(huán)讀取repsonse數(shù)據(jù)
int ch;
buffer = new StringBuffer();
while ((ch = dis.read()) != -1) {
buffer.append((char) ch);
}
//將response數(shù)據(jù)進(jìn)行必要的編碼轉(zhuǎn)換
response = buffer.toString().getBytes("ISO8859_1");
//接收到回應(yīng)后,表明整個(gè)http會(huì)話過程結(jié)束,線程將結(jié)束。
//如果程序運(yùn)行在多線程模式,則此時(shí)需要喚醒其它睡眠的線程繼續(xù)run
if (detached) {
forceNotify();
}
return;
} catch (Exception e) {
if (exceptionPipe == null) {
exceptionPipe = e;
if (detached) {
forceNotify();
}
return;
}
} finally {
try {
if (dis != null) {
// 關(guān)閉dis
dis.close();
}
} catch (Exception e) {
// 若關(guān)閉dis時(shí)發(fā)生異常,則進(jìn)行異常處理
if (exceptionPipe == null) {
exceptionPipe = e;
if (detached) {
forceNotify();
}
return;
}
} finally {
dis = null;
}
try {
if (httpConn != null) {
//關(guān)閉http連接
httpConn.close();
httpConn = null;
}
} catch (Exception e) {
if (exceptionPipe == null) {
exceptionPipe = e;
if (detached) {
forceNotify();
}
return;
}
}
}
}
}
五、參考資料:
聯(lián)系netConnection作者:JAY-F
源代碼下載
HTTP/1.1定義
不知道大家會(huì)更喜歡哪一種封裝呢?
是HttpConnectionWrapper嗎?
他的調(diào)用方法:
Wait w = (Wait) display.getCurrent();
HttpConnection conn = null;
InputStream in = null;
try {
conn = new HttpConnectionWrapper( (HttpConnection) Connector.open( url ) );
conn.setRequestProperty( "User-Agent", "Profile/MIDP-1.0 Configuration/CLDC-1.0" );
conn.setRequestProperty( "Connection", "close" );
conn.setRequestProperty( "Send-HTTP-Log-To", "ericgiguere@ericgiguere.com" );
int rc = conn.getResponseCode();
w.update( "Response code " + rc );
in = conn.openInputStream();
for( int i = 0; i < 100; ++i ){
if( in.read() == -1 ) break;
}
封裝類示意:
/**
* A wrapper for the HttpConnection interface that logs method
* calls and state transitions. Wrap a connection immediately
* after obtaining it from Connector.open. Information is logged
* according to the log level set the "httpwrapper" logger.
*/
public class HttpConnectionWrapper implements HttpConnection {
還是NetConnection?
他的調(diào)用方法:
NetConnection.connect("http://www.molon.com.cn/",data,HttpConnection.GET,...);
封裝類示意:
/**
* This class provides robust network connectivity and exception handling
* over an unreliable MIDP HttpConnection.
*
* In addition the caller may perform a combination of any of the below
* functions.
*
* - Proxy-server thwarting
* - HTTP range requests
* - Thread-separated connections
*
* @author Jason Fuerstenberg (http://www.jay-f.jp)
* @version 2004/03/15
*/
public final class NetConnection implements Runnable {