亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

j2me最佳聯網方案終結版

2019-11-18 16:17:11
字體:
來源:轉載
供稿:網友
(1) .由于無線設備所能支持的網絡協議非常有限,僅限于HTTP,Socket,UDP等幾種協議,不同的廠家可能還支持其他網絡協議,但是,MIDP  
1.0規范規定,HTTP協議是必須實現的協議,而其他協議的實現都是可選的。因此,為了能在不同類型的手機上移植,我們盡量采用HTTP作為網絡連接的首選協議,這樣還能重用服務器端的代碼。但是,由于HTTP是一個基于文本的效率較低的協議,因此,必須仔細考慮手機和服務器端的通信內容,盡可能地提高效率。 
  對于MIDP應用程序,應當盡量做到: 
  1.發送請求時,附加一個User-Agent頭,傳入MIDP和自身版本號,以便服務器能識別此請求來自MIDP應用程序,并且根據版本號發送相應的相應。 
 
  2.連接服務器時,顯示一個下載進度條使用戶能看到下載進度,并能隨時中斷連接。 
 
  3.由于無線網絡連接速度還很慢,因此有必要將某些數據緩存起來,可以存儲在內存中,也可以放到RMS中。 
  對于服務器端而言,其輸出響應應當盡量做到: 
  1.  
明確設置Content-Length字段,以便MIDP應用程序能讀取HTTP頭并判斷自身是否有能力處理此長度的數據,如果不能,可以直接關閉連接而不必繼續讀取HTTP正文。 
 
  2.  
服務器不應當發送Html內容,因為MIDP應用程序很難解析HTML,xml雖然能夠解析,但是耗費CPU和內存資源,因此,應當發送緊湊的二進制內容,用DataOutputStream直接寫入并設置Content-Type為application/octet-stream。 
 
  3. 盡量不要重定向URL,這樣會導致MIDP應用程序再次連接服務器,增加了用戶的等待時間和網絡流量。 
 
  4.  
如果發生異常,例如請求的資源未找到,或者身份驗證失敗,通常,服務器會向瀏覽器發送一個顯示出錯的頁面,可能還包括一個用戶登錄的Form,但是,向MIDP發送錯誤頁面毫無意義,應當直接發送一個404或401錯誤,這樣MIDP應用程序就可以直接讀取HTTP頭的響應碼獲取錯誤信息而不必繼續讀取相應內容。 
 
  5.  
由于服務器的計算能力遠遠超過手機客戶端,因此,針對不同客戶端版本發送不同響應的任務應該在服務器端完成。例如,根據客戶端傳送的User-Agent頭確定客戶端版本。這樣,低版本的客戶端不必升級也能繼續使用。 
  MIDP的聯網框架定義了多種協議的網絡連接,但是每個廠商都必須實現HTTP連接,在MIDP  
2.0中還增加了必須實現的HTTPS連接。因此,要保證MIDP應用程序能在不同廠商的手機平臺上移植,最好只使用HTTP連接。雖然HTTP是一個基于文本的效率較低的協議,但是由于使用特別廣泛,大多數服務器應用的前端都是基于HTTP的Web頁面,因此能最大限度地復用服務器端的代碼。只要控制好緩存,仍然有不錯的速度。 
  SUN的MIDP庫提供了javax.microediton.io包,能非常容易地實現HTTP連接。但是要注意,由于網絡有很大的延時,必須把聯網操作放入一個單獨的線程中,以避免主線程阻塞導致用戶界面停止響應。事實上,MIDP運行環境根本就不允許在主線程中操作網絡連接。因此,我們必須實現一個靈活的HTTP聯網模塊,能讓用戶非常直觀地看到當前上傳和下載的進度,并且能夠隨時取消連接。 
  一個完整的HTTP連接為:用戶通過某個命令發起連接請求,然后系統給出一個等待屏幕提示正在連接,當連接正常結束后,前進到下一個屏幕并處理下載的數據。如果連接過程出現異常,將給用戶提示并返回到前一個屏幕。用戶在等待過程中能夠隨時取消并返回前一個屏幕。 
  我們設計一個HttpThread線程類負責在后臺連接服務器,HttpListener接口實現Observer(觀察者)模式,以便HttpThread能提示觀察者下載開始、下載結束、更新進度條等。HttpListener接口如下: 
public interface HttpListener { 
  void onSetSize(int size); 
  void onFinish(byte[] data, int size); 
  void onPRogress(int percent); 
  void onError(int code, String message); 

  實現HttpListener接口的是繼承自Form的一個HttpWaitUI屏幕,它顯示一個進度條和一些提示信息,并允許用戶隨時中斷連接: 
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是負責處理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; } 

(2).  
  使用GET獲取內容 
  我們先討論最簡單的GET請求。GET請求只需向服務器發送一個URL,然后取得服務器響應即可。在HttpThread的run()方法中實現如下: 
public void run() { 
  HttpConnection hc = null; 
  InputStream input = null; 
  try { 
    hc = (HttpConnection)Connector.open(url); 
    hc.setRequestMethod(HttpConnection.GET); // 默認即為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(); // 返回響應大小,或者-1如果大小無法確定 
    listener.onSetSize(size); 
    // 開始讀響應: 
    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]; // 響應大小已知,確定緩沖區大小 
    else 
        buffer = new byte[MAX_LENGTH]; // 響應大小未知,設定一個固定大小的緩沖區 
    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) { // 更新進度 
          tmp_percent = index * 100 / size; 
          if(tmp_percent!=percent) { 
            percent = tmp_percent; 
            listener.onProgress(percent); 
          } 
        } 
    } 
    if(!cancel && input.available()>0) // 緩沖區已滿,無法繼續讀取 
        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) {} 
  } 

  當下載完畢后,HttpWaitUI就獲得了來自服務器的數據,要傳遞給下一個屏幕處理,HttpWaitUI必須包含對此屏幕的引用并通過一個setData(DataInputStream  
input)方法讓下一個屏幕能非常方便地讀取數據。因此,定義一個DataHandler接口: 
public interface DataHandler { 
  void setData(DataInputStream input) throws IOException; 

  HttpWaitUI響應HttpThread的onFinish事件并調用下一個屏幕的setData方法將數據傳遞給它并顯示下一個屏幕: 
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請求過程如下: 
  首先,用戶通過點擊某個屏幕的命令希望閱讀指定的一則新聞,在commandAction事件中,我們初始化HttpWaitUI和顯示數據的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實現DataHandler接口并負責顯示下載的數據: 
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); 
  } 

  服務器端只要以String, long,  
String的順序依次寫入DataOutputStream,MIDP客戶端就可以通過DataInputStream依次取得相應的數據,完全不需要解析XML之類的文本,非常高效而且方便。 
  需要獲得聯網數據的屏幕只需實現DataHandler接口,并向HttpWaitUI傳入一個URL即可復用上述代碼,無須關心如何連接網絡以及如何處理用戶中斷連接。 
(3). 
  使用POST發送數據 
  以POST方式發送數據主要是為了向服務器發送較大量的客戶端的數據,它不受URL的長度限制。POST請求將數據以URL編碼的形式放在HTTP正文中,字段形式為fieldname=value,用&分隔每個字段。注意所有的字段都被作為字符串處理。實際上我們要做的就是模擬瀏覽器POST一個表單。以下是IE發送一個登陸表單的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應用程序中模擬瀏覽器發送這個POST請求,首先設置HttpConnection的請求方式為POST: 
hc.setRequestMethod(HttpConnection.POST); 
  然后構造出HTTP正文: 
byte[] data = "username=admin&password=1234".getBytes(); 
  并計算正文長度,填入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); 
  需要注意的是,數據仍需要以URL編碼格式編碼,由于MIDP庫中沒有J2SE中與之對應的URLEncoder類,因此,需要自己動手編寫這個encode()方法,可以參考java.net.URLEncoder.java的源碼。剩下的便是讀取服務器響應,代碼與GET一致,這里就不再詳述。 
  使用multipart/form-data發送文件 
  如果要在MIDP客戶端向服務器上傳文件,我們就必須模擬一個POST  
multipart/form-data類型的請求,Content-Type必須是multipart/form-data。 
  以multipart/form-data編碼的POST請求格式與application/x-www-form-urlencoded完全不同,multipart/form-data需要首先在HTTP請求頭設置一個分隔符,例如ABCD: 
hc.setRequestProperty("Content-Type", "multipart/form-data; boundary=ABCD"); 
  然后,將每個字段用“--分隔符”分隔,最后一個“--分隔符--”表示結束。例如,要上傳一個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文件的內容> 
--ABCD-- 
/r/n 
  請注意,每一行都必須以/r/n結束,包括最后一行。如果用Sniffer程序檢測IE發送的POST請求,可以發現IE的分隔符類似于---------------------------7d4a6d158c9,這是IE產生的一個隨機數,目的是防止上傳文件中出現分隔符導致服務器無法正確識別文件起始位置。我們可以寫一個固定的分隔符,只要足夠復雜即可。 
  發送文件的POST代碼如下: 
String[] props = ... // 字段名 
String[] values = ... // 字段值 
byte[] file = ... // 文件內容 
String BOUNDARY = "---------------------------7d4a6d158c9"; // 分隔符 
StringBuffer sb = new StringBuffer(); 
// 發送每個字段: 
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 +  
"/"/r/n/r/n"); 
  sb = sb.append(URLEncoder.encode(values)); 
  sb = sb.append("/r/n"); 

// 發送文件: 
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(); 
// 設置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); 
// 讀取服務器響應: 
// TODO... 
(4). 
  使用Cookie保持session 
  通常服務器使用Session來跟蹤會話。Session的簡單實現就是利用Cookie。當客戶端第一次連接服務器時,服務器檢測到客戶端沒有相應的Cookie字段,就發送一個包含一個識別碼的Set-Cookie字段。在此后的會話過程中,客戶端發送的請求都包含這個Cookie,因此服務器能夠識別出客戶端曾經連接過服務器。 
  要實現與瀏覽器一樣的效果,MIDP應用程序必須也能識別Cookie,并在每個請求頭中包含此Cookie。 
  在處理每次連接的響應中,我們都檢查是否有Set-Cookie這個頭,如果有,則是服務器第一次發送的Session  
ID,或者服務器認為會話超時,需要重新生成一個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服務器發送的Session。WebLogic Server 7.0返回的Session如下: 
Set-Cookie:  
JSESSIONID=CXP4FMwOJB06XCByBWfwZBQ0IfkroKO2W7FZpkLbmWsnERuN5u2L!-1200402410;  
path=/ 
  而Resin 2.1返回的Session則是: 
Set-Cookie: JSESSIONID= aTMCmwe9F5j9; path=/ 
  運行asp.net的IIS返回的Session: 
Set-Cookie: ASPSESSIONIDQATSASQB=GNGEEJIDMDFCMOOFLEAKDGGP; path=/ 
  我們無須關心Session ID的內容,服務器自己會識別它。我們只需在隨后的請求中附加上這個Session ID即可: 
if(session!=null) 
  hc.setRequestProperty("Cookie", session); 
  對于URL重寫來保持Session的方法,在PC客戶端可能很有用,但是,由于MIDP程序很難分析出URL中有用的Session信息,因此,不推薦使用這種方法。

(出處:http://www.49028c.com)



發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
中文字幕一精品亚洲无线一区| 亚洲精品一区二三区不卡| 欧美日韩国产成人高清视频| www.久久久久久.com| 亚洲第一在线视频| 欧美激情国产精品| 亚洲国产精品va在线看黑人动漫| 色偷偷偷综合中文字幕;dd| 成人激情春色网| 欧美亚洲国产视频小说| 日韩成人av在线| 亚洲欧美一区二区三区在线| 久久久久久久久电影| 亚洲国产成人久久| 精品国产一区二区三区久久久狼| 久久九九免费视频| 亚洲三级免费看| 亚洲天堂av在线播放| 亚洲a在线观看| 欧美国产视频一区二区| 国产精品丝袜久久久久久不卡| 91av在线影院| 久久久最新网址| 欧美国产日韩一区二区| 亚洲伦理中文字幕| 久久视频在线看| 亚洲综合中文字幕68页| 欧美精品成人91久久久久久久| 国产精品男女猛烈高潮激情| 亚洲无线码在线一区观看| 国产精品色悠悠| 久久韩国免费视频| 亚洲图片制服诱惑| 欧美孕妇毛茸茸xxxx| 成人网欧美在线视频| 亚洲一区第一页| 久久久久中文字幕| 久久噜噜噜精品国产亚洲综合| 韩国视频理论视频久久| 日本高清不卡在线| 亚洲综合第一页| 亚洲欧美第一页| 欧美日韩国产成人在线| 国产精品日韩电影| 91精品国产色综合| 成人精品福利视频| 欧美大片在线影院| 免费99精品国产自在在线| 日韩精品在线观| 91国内在线视频| 最近2019中文免费高清视频观看www99| 欧美日韩国产在线| 欧美日韩精品二区| 久久天天躁狠狠躁夜夜躁| 羞羞色国产精品| 国产精品狠色婷| 日韩电影在线观看中文字幕| 欧美精品videossex88| 亚洲成人久久久久| 人人澡人人澡人人看欧美| 国产在线久久久| 伊人久久大香线蕉av一区二区| 日av在线播放中文不卡| 岛国视频午夜一区免费在线观看| 久久九九全国免费精品观看| 亚洲一区999| 欧美三级xxx| 欧美日韩中文字幕在线| 国产精品亚洲第一区| 成人a视频在线观看| 久久久久久国产精品美女| 九九热视频这里只有精品| 久久国产精品99国产精| 亚洲国产精品99久久| 91精品国产网站| 久久久久久成人精品| 日韩激情视频在线播放| 欧美极品少妇xxxxx| 欧美一级大胆视频| 亚洲国产成人一区| 91亚洲人电影| 欧美剧在线观看| 久久琪琪电影院| 疯狂做受xxxx欧美肥白少妇| 久久久久久久久久久免费| 日韩视频免费在线观看| 欧美综合在线第二页| 欧美一级在线亚洲天堂| 亚洲精品美女在线观看播放| 午夜精品一区二区三区在线播放| 国产精品自产拍在线观| 国产综合在线看| 色阁综合伊人av| 国产亚洲精品久久久久久| 久久中文字幕视频| 欧美激情视频在线观看| 日韩欧美精品免费在线| 国产精品国产三级国产aⅴ浪潮| 国产亚洲一级高清| 日韩激情视频在线播放| 亚洲色图50p| 18一19gay欧美视频网站| 97超视频免费观看| 国产一区二区激情| 91精品久久久久| 91在线观看免费观看| 亚洲欧美日韩第一区| 91高清在线免费观看| 国产亚洲美女精品久久久| 国语自产偷拍精品视频偷| 欧美成人精品h版在线观看| 日韩在线视频一区| 亚洲美女又黄又爽在线观看| www国产精品com| 国产精品美乳一区二区免费| 久久精品国产亚洲精品2020| 亚洲精品99999| 国产精品久久久一区| 亚洲综合成人婷婷小说| 国产丝袜一区二区| 日韩免费在线视频| 国产热re99久久6国产精品| 亚洲精品欧美极品| 91超碰中文字幕久久精品| www国产亚洲精品久久网站| 大量国产精品视频| 黄色精品一区二区| 亚洲精品日产aⅴ| 国产日韩欧美中文在线播放| 亚洲一区精品电影| 国产一区二区激情| 国产综合在线观看视频| 欧美亚洲视频一区二区| 亚洲欧美国产另类| 日本久久中文字幕| 色999日韩欧美国产| 国产亚洲精品久久久久久牛牛| 国产亚洲精品久久久久久777| 亚洲国产私拍精品国模在线观看| 91精品国产综合久久男男| 国产免费一区二区三区在线观看| 欧美激情中文网| 日韩专区在线播放| 久久大大胆人体| 粗暴蹂躏中文一区二区三区| 亚洲欧美精品suv| 亚洲国产成人久久| 欧美中文字幕在线观看| 欧美老女人www| 成人乱色短篇合集| 国产一区香蕉久久| 欧美制服第一页| 亚洲欧美日韩一区在线| 久久久中文字幕| 全球成人中文在线| 日韩av免费看网站| 2020国产精品视频| 国产精品高清在线观看| 日韩精品在线观看网站| 欧美性xxxx在线播放| 1769国产精品| 精品香蕉一区二区三区| 久久久久国产视频| 亚洲女人被黑人巨大进入|