国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
J2ME編程最佳實(shí)踐之聯(lián)網(wǎng)開發(fā)[組圖]

  由于無線設(shè)備所能支持的網(wǎng)絡(luò)協(xié)議非常有限,僅限于HTTP,Socket,UDP等幾種協(xié)議,不同的廠家可能還支持其他網(wǎng)絡(luò)協(xié)議,但是,MIDP 1.0規(guī)范規(guī)定,HTTP協(xié)議是必須實(shí)現(xiàn)的協(xié)議,而其他協(xié)議的實(shí)現(xiàn)都是可選的。

因此,為了能在不同類型的手機(jī)上移植,我們盡量采用HTTP作為網(wǎng)絡(luò)連接的首選協(xié)議,這樣還能重用服務(wù)器端的代碼。但是,由于HTTP是一個基于文本的效率較低的協(xié)議,因此,必須仔細(xì)考慮手機(jī)和服務(wù)器端的通信內(nèi)容,盡可能地提高效率。

 

  對于MIDP應(yīng)用程序,應(yīng)當(dāng)盡量做到:

  1.發(fā)送請求時,附加一個User-Agent頭,傳入MIDP和自身版本號,以便服務(wù)器能識別此請求來自MIDP應(yīng)用程序,并且根據(jù)版本號發(fā)送相應(yīng)的相應(yīng)。

  2.連接服務(wù)器時,顯示一個下載進(jìn)度條使用戶能看到下載進(jìn)度,并能隨時中斷連接。

  3.由于無線網(wǎng)絡(luò)連接速度還很慢,因此有必要將某些數(shù)據(jù)緩存起來,可以存儲在內(nèi)存中,也可以放到RMS中。

  對于服務(wù)器端而言,其輸出響應(yīng)應(yīng)當(dāng)盡量做到:

  1. 明確設(shè)置Content-Length字段,以便MIDP應(yīng)用程序能讀取HTTP頭并判斷自身是否有能力處理此長度的數(shù)據(jù),如果不能,可以直接關(guān)閉連接而不必繼續(xù)讀取HTTP正文。

  2. 服務(wù)器不應(yīng)當(dāng)發(fā)送HTML內(nèi)容,因?yàn)镸IDP應(yīng)用程序很難解析HTML,XML雖然能夠解析,但是耗費(fèi)CPU和內(nèi)存資源,因此,應(yīng)當(dāng)發(fā)送緊湊的二進(jìn)制內(nèi)容,用DataOutputStream直接寫入并設(shè)置Content-Type為application/octet-stream。

  3. 盡量不要重定向URL,這樣會導(dǎo)致MIDP應(yīng)用程序再次連接服務(wù)器,增加了用戶的等待時間和網(wǎng)絡(luò)流量。

  4. 如果發(fā)生異常,例如請求的資源未找到,或者身份驗(yàn)證失敗,通常,服務(wù)器會向?yàn)g覽器發(fā)送一個顯示出錯的頁面,可能還包括一個用戶登錄的Form,但是,向MIDP發(fā)送錯誤頁面毫無意義,應(yīng)當(dāng)直接發(fā)送一個404或401錯誤,這樣MIDP應(yīng)用程序就可以直接讀取HTTP頭的響應(yīng)碼獲取錯誤信息而不必繼續(xù)讀取相應(yīng)內(nèi)容。

  5. 由于服務(wù)器的計(jì)算能力遠(yuǎn)遠(yuǎn)超過手機(jī)客戶端,因此,針對不同客戶端版本發(fā)送不同響應(yīng)的任務(wù)應(yīng)該在服務(wù)器端完成。例如,根據(jù)客戶端傳送的User-Agent頭確定客戶端版本。這樣,低版本的客戶端不必升級也能繼續(xù)使用。

  MIDP的聯(lián)網(wǎng)框架定義了多種協(xié)議的網(wǎng)絡(luò)連接,但是每個廠商都必須實(shí)現(xiàn)HTTP連接,在MIDP 2.0中還增加了必須實(shí)現(xiàn)的HTTPS連接。因此,要保證MIDP應(yīng)用程序能在不同廠商的手機(jī)平臺上移植,最好只使用HTTP連接。雖然HTTP是一個基于文本的效率較低的協(xié)議,但是由于使用特別廣泛,大多數(shù)服務(wù)器應(yīng)用的前端都是基于HTTP的Web頁面,因此能最大限度地復(fù)用服務(wù)器端的代碼。只要控制好緩存,仍然有不錯的速度。

  SUN的MIDP庫提供了Javax.microediton.io包,能非常容易地實(shí)現(xiàn)HTTP連接。但是要注意,由于網(wǎng)絡(luò)有很大的延時,必須把聯(lián)網(wǎng)操作放入一個單獨(dú)的線程中,以避免主線程阻塞導(dǎo)致用戶界面停止響應(yīng)。事實(shí)上,MIDP運(yùn)行環(huán)境根本就不允許在主線程中操作網(wǎng)絡(luò)連接。因此,我們必須實(shí)現(xiàn)一個靈活的HTTP聯(lián)網(wǎng)模塊,能讓用戶非常直觀地看到當(dāng)前上傳和下載的進(jìn)度,并且能夠隨時取消連接?! ∫粋€完整的HTTP連接為:用戶通過某個命令發(fā)起連接請求,然后系統(tǒng)給出一個等待屏幕提示正在連接,當(dāng)連接正常結(jié)束后,前進(jìn)到下一個屏幕并處理下載的數(shù)據(jù)。如果連接過程出現(xiàn)異常,將給用戶提示并返回到前一個屏幕。用戶在等待過程中能夠隨時取消并返回前一個屏幕。

 

  我們設(shè)計(jì)一個HttpThread線程類負(fù)責(zé)在后臺連接服務(wù)器,HttpListener接口實(shí)現(xiàn)Observer(觀察者)模式,以便HttpThread能提示觀察者下載開始、下載結(jié)束、更新進(jìn)度條等。HttpListener接口如下:

public interface HttpListener {
    void onSetSize(int size);
    void onFinish(byte[] data, int size);
    void onProgress(int percent);
    void onError(int code, String message);
}

  實(shí)現(xiàn)HttpListener接口的是繼承自Form的一個HttpWaitUI屏幕,它顯示一個進(jìn)度條和一些提示信息,并允許用戶隨時中斷連接:

public class HttpWaitUI extends Form implements CommandListener, HttpListener {
    private Gauge gauge;
    private Command cancel;
    private HttpThread downloader;
    private Displayable displayable;
    public HttpWaitUI(String url, Displayable displayable) {
        super("Connecting");
        this.gauge = new Gauge("Progress", false, 100, 0);
        this.cancel = new Command("Cancel", Command.CANCEL, 0);
        append(gauge);
        addCommand(cancel);
        setCommandListener(this);
        downloader = new HttpThread(url, this);
        downloader.start();
    }
    public void commandAction(Command c, Displayable d) {
        if(c==cancel) {
            downloader.cancel();
            ControllerMIDlet.goBack();
        }
    }
    public void onFinish(byte[] buffer, int size) { … }
    public void onError(int code, String message) { … }
    public void onProgress(int percent) { … }
    public void onSetSize(int size) { … }
}  HttpThread是負(fù)責(zé)處理Http連接的線程類,它接受一個URL和HttpListener:

 

class HttpThread extends Thread {
    private static final int MAX_LENGTH = 20 * 1024; // 20K
    private boolean cancel = false;
    private String url;
    private byte[] buffer = null;
    private HttpListener listener;
    public HttpThread(String url, HttpListener listener) {
        this.url = url;
        this.listener = listener;
    }
    public void cancel() { cancel = true; }
}

  使用GET獲取內(nèi)容

  我們先討論最簡單的GET請求。GET請求只需向服務(wù)器發(fā)送一個URL,然后取得服務(wù)器響應(yīng)即可。在HttpThread的run()方法中實(shí)現(xiàn)如下:

public void run() {
    HttpConnection hc = null;
    InputStream input = null;
    try {
        hc = (HttpConnection)Connector.open(url);
        hc.setRequestMethod(HttpConnection.GET); // 默認(rèn)即為GET
        hc.setRequestProperty("User-Agent", USER_AGENT);
        // get response code:
        int code = hc.getResponseCode();
        if(code!=HttpConnection.HTTP_OK) {
            listener.onError(code, hc.getResponseMessage());
            return;
        }
        // get size:
        int size = (int)hc.getLength(); // 返回響應(yīng)大小,或者-1如果大小無法確定
        listener.onSetSize(size);
        // 開始讀響應(yīng):
        input = hc.openInputStream();
        int percent = 0; // percentage
        int tmp_percent = 0;
        int index = 0; // buffer index
        int reads; // each byte
        if(size!=(-1))
            buffer = new byte[size]; // 響應(yīng)大小已知,確定緩沖區(qū)大小
        else
            buffer = new byte[MAX_LENGTH]; // 響應(yīng)大小未知,設(shè)定一個固定大小的緩沖區(qū)
        while(!cancel) {
            int len = buffer.length - index;
            len = len>128 ? 128 : len;
            reads = input.read(buffer, index, len);
            if(reads<=0)
                break;
            index += reads;
            if(size>0) { // 更新進(jìn)度
                tmp_percent = index * 100 / size;
                if(tmp_percent!=percent) {
                    percent = tmp_percent;
                    listener.onProgress(percent);
                }
            }
        }
        if(!cancel && input.available()>0) // 緩沖區(qū)已滿,無法繼續(xù)讀取
            listener.onError(601, "Buffer overflow.");
        if(!cancel) {
            if(size!=(-1) && index!=size)
                listener.onError(102, "Content-Length does not match.");
            else
                listener.onFinish(buffer, index);
        }
    }
    catch(IOException ioe) {
        listener.onError(101, "IOException: " + ioe.getMessage());
    }
    finally { // 清理資源
        if(input!=null)
            try { input.close(); } catch(IOException ioe) {}
        if(hc!=null)
            try { hc.close(); } catch(IOException ioe) {}
    }
}  當(dāng)下載完畢后,HttpWaitUI就獲得了來自服務(wù)器的數(shù)據(jù),要傳遞給下一個屏幕處理,HttpWaitUI必須包含對此屏幕的引用并通過一個setData(DataInputStream input)方法讓下一個屏幕能非常方便地讀取數(shù)據(jù)。因此,定義一個DataHandler接口:

 

public interface DataHandler {
    void setData(DataInputStream input) throws IOException;
}

  HttpWaitUI響應(yīng)HttpThread的onFinish事件并調(diào)用下一個屏幕的setData方法將數(shù)據(jù)傳遞給它并顯示下一個屏幕:

public void onFinish(byte[] buffer, int size) {
    byte[] data = buffer;
    if(size!=buffer.length) {
        data = new byte[size];
        System.arraycopy(data, 0, buffer, 0, size);
    }
    DataInputStream input = null;
    try {
        input = new DataInputStream(new ByteArrayInputStream(data));
        if(displayable instanceof DataHandler)
            ((DataHandler)displayable).setData(input);
        else
            System.err.println("[WARNING] Displayable object cannot handle data.");
        ControllerMIDlet.replace(displayable);
    }
    catch(IOException ioe) { … }
}

  以下載一則新聞為例,一個完整的HTTP GET請求過程如下:

  首先,用戶通過點(diǎn)擊某個屏幕的命令希望閱讀指定的一則新聞,在commandAction事件中,我們初始化HttpWaitUI和顯示數(shù)據(jù)的NewsUI屏幕:

public void commandAction(Command c, Displayable d) {
    HttpWaitUI wait = new HttpWaitUI("http://192.168.0.1/news.do?id=1", new NewsUI());
    ControllerMIDlet.forward(wait);
}

  NewsUI實(shí)現(xiàn)DataHandler接口并負(fù)責(zé)顯示下載的數(shù)據(jù):

public class NewsUI extends Form implements DataHandler {
    public void setData(DataInputStream input) throws IOException {
        String title = input.readUTF();
        Date date = new Date(input.readLong());
        String text = input.readUTF();
        append(new StringItem("Title", title));
        append(new StringItem("Date", date.toString()));
        append(text);
    }
}
  服務(wù)器端只要以String, long, String的順序依次寫入DataOutputStream,MIDP客戶端就可以通過DataInputStream依次取得相應(yīng)的數(shù)據(jù),完全不需要解析XML之類的文本,非常高效而且方便。

 

  需要獲得聯(lián)網(wǎng)數(shù)據(jù)的屏幕只需實(shí)現(xiàn)DataHandler接口,并向HttpWaitUI傳入一個URL即可復(fù)用上述代碼,無須關(guān)心如何連接網(wǎng)絡(luò)以及如何處理用戶中斷連接。

  使用POST發(fā)送數(shù)據(jù)

  以POST方式發(fā)送數(shù)據(jù)主要是為了向服務(wù)器發(fā)送較大量的客戶端的數(shù)據(jù),它不受URL的長度限制。POST請求將數(shù)據(jù)以URL編碼的形式放在HTTP正文中,字段形式為fieldname=value,用&分隔每個字段。注意所有的字段都被作為字符串處理。實(shí)際上我們要做的就是模擬瀏覽器POST一個表單。以下是IE發(fā)送一個登陸表單的POST請求:

POST http://127.0.0.1/login.do HTTP/1.0
Accept: image/gif, image/jpeg, image/pjpeg, */*
Accept-Language: en-us,zh-cn;q=0.5
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Content-Length: 28
\r\n
username=admin&passWord=1234

  要在MIDP應(yīng)用程序中模擬瀏覽器發(fā)送這個POST請求,首先設(shè)置HttpConnection的請求方式為POST:

hc.setRequestMethod(HttpConnection.POST);

  然后構(gòu)造出HTTP正文:

byte[] data = "username=admin&password=1234".getBytes();

  并計(jì)算正文長度,填入Content-Type和Content-Length:

hc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
hc.setRequestProperty("Content-Length", String.valueOf(data.length));

  然后打開OutputStream將正文寫入:

OutputStream output = hc.openOutputStream();
output.write(data);

  需要注意的是,數(shù)據(jù)仍需要以URL編碼格式編碼,由于MIDP庫中沒有J2SE中與之對應(yīng)的URLEncoder類,因此,需要自己動手編寫這個encode()方法,可以參考java.net.URLEncoder.java的源碼。剩下的便是讀取服務(wù)器響應(yīng),代碼與GET一致,這里就不再詳述。

  使用multipart/form-data發(fā)送文件

  如果要在MIDP客戶端向服務(wù)器上傳文件,我們就必須模擬一個POST multipart/form-data類型的請求,Content-Type必須是multipart/form-data。
  以multipart/form-data編碼的POST請求格式與application/x-www-form-urlencoded完全不同,multipart/form-data需要首先在HTTP請求頭設(shè)置一個分隔符,例如ABCD:

 

hc.setRequestProperty("Content-Type", "multipart/form-data; boundary=ABCD");

  然后,將每個字段用“--分隔符”分隔,最后一個“--分隔符--”表示結(jié)束。例如,要上傳一個title字段"Today"和一個文件C:\1.txt,HTTP正文如下:

--ABCD
Content-Disposition: form-data; name="title"
\r\n
Today
--ABCD
Content-Disposition: form-data; name="1.txt"; filename="C:\1.txt"
Content-Type: text/plain
\r\n
<這里是1.txt文件的內(nèi)容>
--ABCD--
\r\n

  請注意,每一行都必須以\r\n結(jié)束,包括最后一行。如果用Sniffer程序檢測IE發(fā)送的POST請求,可以發(fā)現(xiàn)IE的分隔符類似于---------------------------7d4a6d158c9,這是IE產(chǎn)生的一個隨機(jī)數(shù),目的是防止上傳文件中出現(xiàn)分隔符導(dǎo)致服務(wù)器無法正確識別文件起始位置。我們可以寫一個固定的分隔符,只要足夠復(fù)雜即可。

  發(fā)送文件的POST代碼如下:

String[] props = ... // 字段名
String[] values = ... // 字段值
byte[] file = ... // 文件內(nèi)容
String BOUNDARY = "---------------------------7d4a6d158c9"; // 分隔符
StringBuffer sb = new StringBuffer();
// 發(fā)送每個字段:
for(int i=0; i
    sb = sb.append("--");
    sb = sb.append(BOUNDARY);
    sb = sb.append("\r\n");
    sb = sb.append("Content-Disposition: form-data; name=\""+ props[i] + "\"\r\n\r\n");
    sb = sb.append(URLEncoder.encode(values[i]));
    sb = sb.append("\r\n");
}
// 發(fā)送文件:
sb = sb.append("--");
sb = sb.append(BOUNDARY);
sb = sb.append("\r\n");
sb = sb.append("Content-Disposition: form-data; name=\"1\"; filename=\"1.txt\"\r\n");
sb = sb.append("Content-Type: application/octet-stream\r\n\r\n");
byte[] data = sb.toString().getBytes();
byte[] end_data = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();
// 設(shè)置HTTP頭:
hc.setRequestProperty("Content-Type", MULTIPART_FORM_DATA + "; boundary=" + BOUNDARY);
hc.setRequestProperty("Content-Length", String.valueOf(data.length + file.length + end_data.length));
// 輸出:
output = hc.openOutputStream();
output.write(data);
output.write(file);
output.write(end_data);
// 讀取服務(wù)器響應(yīng):
// TODO...
 
 使用Cookie保持Session

 

  通常服務(wù)器使用Session來跟蹤會話。Session的簡單實(shí)現(xiàn)就是利用Cookie。當(dāng)客戶端第一次連接服務(wù)器時,服務(wù)器檢測到客戶端沒有相應(yīng)的Cookie字段,就發(fā)送一個包含一個識別碼的Set-Cookie字段。在此后的會話過程中,客戶端發(fā)送的請求都包含這個Cookie,因此服務(wù)器能夠識別出客戶端曾經(jīng)連接過服務(wù)器。

  要實(shí)現(xiàn)與瀏覽器一樣的效果,MIDP應(yīng)用程序必須也能識別Cookie,并在每個請求頭中包含此Cookie。

  在處理每次連接的響應(yīng)中,我們都檢查是否有Set-Cookie這個頭,如果有,則是服務(wù)器第一次發(fā)送的Session ID,或者服務(wù)器認(rèn)為會話超時,需要重新生成一個Session ID。如果檢測到Set-Cookie頭,就將其保存,并在隨后的每次請求中附加它:

String session = null;
String cookie = hc.getHeaderField("Set-Cookie");
if(cookie!=null) {
    int n = cookie.indexOf(';');
    session = cookie.substring(0, n);
}

  使用Sniffer程序可以捕獲到不同的Web服務(wù)器發(fā)送的Session。WebLogic Server 7.0返回的Session如下:

Set-Cookie: JSESSIONID=Cxp4FMwOJB06XCByBWfwZBQ0IfkroKO2W7FZpkLbmWsnERuN5u2L!-1200402410; path=/

  而Resin 2.1返回的Session則是:

Set-Cookie: JSESSIONID= aTMCmwe9F5j9; path=/

  運(yùn)行ASP.Net的IIS返回的Session:

Set-Cookie: ASPSESSIONIDQATSASQB=GNGEEJIDMDFCMOOFLEAKDGGP; path=/
 
  我們無須關(guān)心Session ID的內(nèi)容,服務(wù)器自己會識別它。我們只需在隨后的請求中附加上這個Session ID即可:

 

if(session!=null)
    hc.setRequestProperty("Cookie", session);

  對于URL重寫來保持Session的方法,在PC客戶端可能很有用,但是,由于MIDP程序很難分析出URL中有用的Session信息,因此,不推薦使用這種方法。


(出處:http://www.hackhome.com/)

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
分頁技術(shù)及其實(shí)現(xiàn)
一.HTTP協(xié)議:
讀取excel(POI)【轉(zhuǎn)換為html】 - bcoffee的專欄 - 博客頻道 - CSDN.NET
Struts 2.0的Action講解
關(guān)于web應(yīng)用程序安全的思考(一) - net點(diǎn)滴 - 博客園
A Guide to Cassandra with Java | Baeldung
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服