一、“NetConnection”簡(jiǎn)介:
轉(zhuǎn)述Matrix上zhengyun_ustc所述:“你的HttpConnection是否封裝的足夠健壯呢?遇到各種情況,你是否有信心應(yīng)對(duì)呢?譬如說(shuō),你要請(qǐng)求的Response包實(shí)在太大,以至于運(yùn)營(yíng)商給你掐了告訴你說(shuō)超時(shí);譬如說(shuō)你是不是總要自己寫一個(gè)線程來(lái)專門作http連接?譬如說(shuō)有一些移動(dòng)運(yùn)營(yíng)商設(shè)置了caching proxy servers,妨礙了你的測(cè)試。”
為了解決這個(gè)問(wèn)題,一位日本程序員“JAY-F”針對(duì)MIDP1.0提供了一種robust的“NetConnection”封裝。這個(gè)HttpConnnection類負(fù)責(zé)管理連接并易于使用。
二、“NetConnection”特性:
1. 跨過(guò)Proxy-server阻礙:
一些移動(dòng)網(wǎng)絡(luò)放置了代理服務(wù)器用來(lái)提高訪問(wèn)速度,但是它的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ā)來(lái)的新請(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)代碼均沒(méi)有收到,則表明連接失敗或者出問(wèn)題
????????????????????????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ì)話過(guò)程結(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定義