一、“NetConnection”簡介:
轉述Matrix上zhengyun_ustc所述:“你的HttpConnection是否封裝的足夠健壯呢?遇到各種情況,你是否有信心應對呢?譬如說,你要請求的Response包實在太大,以至于運營商給你掐了告訴你說超時;譬如說你是不是總要自己寫一個線程來專門作http連接?譬如說有一些移動運營商設置了caching proxy servers,妨礙了你的測試。”
為了解決這個問題,一位日本程序員“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定義
posted on 2007-03-17 18:00
英明 閱讀(1246)
評論(0) 編輯 收藏 所屬分類:
J2ME