另外,還有一篇必讀的文章《Debugging MIDP HTTP Requests,http://developers.sun.com/techtopics/mobility/midp/articles/httpdebug/》,也給出了一份源代碼,http://developers.sun.com/techtopics/mobility/midp/articles/httpdebug/src/httpwrapper.zip,算是另外一種封裝實現了。
一個來自日本的MIDP 1.0 HttpConnection類的robust封裝
作者:zhengyun_ustc、cleverpig一、“NetConnection”簡介:轉述Matrix上zhengyun_ustc所述:“你的HttpConnection是否封裝的足夠健壯呢?遇到各種情況,你是否有信心應對呢?譬如說,你要請求的Response包實在太大,以至于運營商給你掐了告訴你說超時;譬如說你是不是總要自己寫一個線程來專門作http連接?譬如說有一些移動運營商設置了caching proxy servers,妨礙了你的測試?!?BR>
為了解決這個問題,一位日本程序員“JAY-F”針對MIDP1.0提供了一種robust的“NetConnection”封裝。這個HttpConnnection類負責管理連接并易于使用。
二、“NetConnection”特性:1. 跨過Proxy-server阻礙:
一些移動網絡放置了代理服務器用來提高訪問速度,但是它的cache也成為了開發人員測試/調試程序的一大障礙。“NetConnection”類使用一個簡單的http request屬性將server上的代理功能關閉掉。
2. 使用線程分離的連接模式:
本類可以使用單線程、多線程兩種模式運行,只要設置一個簡單的標志即可。
3. 支持Http request range:
由于服務商在其網絡上可能存在一些針對回應數據最大長度的限制,所以“NetConnection”類提供了構造request URL的功能使回應數據分為多個數據包。從而去除了前面的限制。
三、netConnection是如何實現的?1。netConnection類結構分析:
此類實現了Runnable接口,其運行模式支持多線程模式:當前只能由一個線程使用資源,其它線程wait。
此類使用了一些靜態成員變量:
//當前只能由一個線程使用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()
//當前運行的線程執行完畢后,通報給其它的由于等待資源而wait狀態的線程
private synchronized void forceNotify()
//當資源正在被其它線程使用時,當前線程進入wait狀態
private synchronized void forceWait()
//關閉http連接
private static void severConnection()
由于使用了這些static成員變量,所以一些操作方法需要同步(synchronized)。
2。netConnection核心代碼解析:
netConnection類的實現思想很簡單,就是設置一些request屬性和對于GET方法構造一個特殊的URL。更重要的是其作者對http協議的深入理解、嚴謹的代碼風格值得吾輩學習、研究。這也是本人分析其核心代碼的一大原因。
/**
* 實現了連接邏輯。
* 調用者可以在分離的線程中使用netConnection類的靜態連接。
* @throws IllegalStateException 如果此方法直接其它類調用則拋出該異常
*/
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;
//根據method值,設置Connector的權限(READ/READ_WRITE)
if (HttpConnection.GET.equals(method)) {
permissions = Connector.READ;
} else if (HttpConnection.POST.equals(method)) {
permissions = Connector.READ_WRITE;
}
//如果關閉server代理功能,則構造noProxyUrl。
//原理:使用timestamp作為該URL中no-proxy參數值,
// 致使server視其為client發來的新請求。
if (disableProxy) {
boolean hasQueryParams = false;
char[] ca = url.toCharArray();
//判斷原URL中是否含有參數
for (int loop = 0; loop < url.length(); loop++) {
if (ca[loop] == '?') {
hasQueryParams = true;
break;
}
}
//由于需要多次字符串拼接,所以使用可提供效率的StringBuffer類
StringBuffer noProxyUrl = new StringBuffer();
//將原URL內容復制到noProxyUrl
noProxyUrl.append(url);
//如果原URL中含有參數,
// 則需要在noProxyUrl中增加"&",
// 否則直接在noProxyUrl中增加"?",
// 這樣做為了后面增加no-proxy參數做準備。
if (hasQueryParams) {
noProxyUrl.append("&");
} else {
noProxyUrl.append("?");
}
//增加no-proxy參數
noProxyUrl.append("no-proxy=");
noProxyUrl.append(System.currentTimeMillis()); // timestamp
//將構造好的noProxyUrl復制到原URL
url = noProxyUrl.toString();
}
// 打開Http 連接
httpConn = (HttpConnection) Connector.open(url, permissions, true);
//設置request方法
httpConn.setRequestMethod(method);
//如果request權限為READ(即request方法為GET),
//則需要設置http request屬性的Range。
//原理:設置http request屬性的Range后的,
// server接收到該request后將把response數據分成小部分發回。
// 從而避免了部分運營商對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權限為READ_WRITE(即request方法為POST),
//那么設置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;
//如果程序運行在多線程模式,則在異常發生后需要喚醒其它睡眠的線程繼續run
if (detached) {
forceNotify();
}
return;
} finally {
try {
try {
if (dos != null) {
// 關閉dos
dos.close();
}
} catch (Exception e) {
// 如果程序運行在多線程模式,則在異常發生后需要喚醒其它睡眠的線程繼續run
if (exceptionPipe == null) {
exceptionPipe = e;
if (detached) {
forceNotify();
}
return;
}
} finally {
dos = null;
}
// 讀取http連接的回應代碼
int responseCode = httpConn.getResponseCode();
//當request方法為GET,并設置了request range時,接收到的回應代碼為HTTP_PARTIAL
//當request方法為POST,接收到的回應代碼為HTTP_OK
//如果上述兩種回應代碼均沒有收到,則表明連接失敗或者出問題
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;
}
}
//如果收到了上述的兩種回應代碼之一,則可以繼續讀取server的response數據
dis = httpConn.openDataInputStream();
//循環讀取repsonse數據
int ch;
buffer = new StringBuffer();
while ((ch = dis.read()) != -1) {
buffer.append((char) ch);
}
//將response數據進行必要的編碼轉換
response = buffer.toString().getBytes("ISO8859_1");
//接收到回應后,表明整個http會話過程結束,線程將結束。
//如果程序運行在多線程模式,則此時需要喚醒其它睡眠的線程繼續run
if (detached) {
forceNotify();
}
return;
} catch (Exception e) {
if (exceptionPipe == null) {
exceptionPipe = e;
if (detached) {
forceNotify();
}
return;
}
} finally {
try {
if (dis != null) {
// 關閉dis
dis.close();
}
} catch (Exception e) {
// 若關閉dis時發生異常,則進行異常處理
if (exceptionPipe == null) {
exceptionPipe = e;
if (detached) {
forceNotify();
}
return;
}
} finally {
dis = null;
}
try {
if (httpConn != null) {
//關閉http連接
httpConn.close();
httpConn = null;
}
} catch (Exception e) {
if (exceptionPipe == null) {
exceptionPipe = e;
if (detached) {
forceNotify();
}
return;
}
}
}
}
}
五、參考資料:聯系netConnection作者:JAY-F源代碼下載HTTP/1.1定義
不知道大家會更喜歡哪一種封裝呢?
是HttpConnectionWrapper嗎?
他的調用方法:
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?
他的調用方法:
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 {