国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

j2me最佳聯(lián)網(wǎng)方案終結(jié)版

2019-11-18 16:17:11
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
(1) .由于無(wú)線(xiàn)設(shè)備所能支持的網(wǎng)絡(luò)協(xié)議非常有限,僅限于HTTP,Socket,UDP等幾種協(xié)議,不同的廠(chǎng)家可能還支持其他網(wǎng)絡(luò)協(xié)議,但是,MIDP  
1.0規(guī)范規(guī)定,HTTP協(xié)議是必須實(shí)現(xiàn)的協(xié)議,而其他協(xié)議的實(shí)現(xiàn)都是可選的。因此,為了能在不同類(lèi)型的手機(jī)上移植,我們盡量采用HTTP作為網(wǎng)絡(luò)連接的首選協(xié)議,這樣還能重用服務(wù)器端的代碼。但是,由于HTTP是一個(gè)基于文本的效率較低的協(xié)議,因此,必須仔細(xì)考慮手機(jī)和服務(wù)器端的通信內(nèi)容,盡可能地提高效率。 
  對(duì)于MIDP應(yīng)用程序,應(yīng)當(dāng)盡量做到: 
  1.發(fā)送請(qǐng)求時(shí),附加一個(gè)User-Agent頭,傳入MIDP和自身版本號(hào),以便服務(wù)器能識(shí)別此請(qǐng)求來(lái)自MIDP應(yīng)用程序,并且根據(jù)版本號(hào)發(fā)送相應(yīng)的相應(yīng)。 
 
  2.連接服務(wù)器時(shí),顯示一個(gè)下載進(jìn)度條使用戶(hù)能看到下載進(jìn)度,并能隨時(shí)中斷連接。 
 
  3.由于無(wú)線(xiàn)網(wǎng)絡(luò)連接速度還很慢,因此有必要將某些數(shù)據(jù)緩存起來(lái),可以存儲(chǔ)在內(nèi)存中,也可以放到RMS中。 
  對(duì)于服務(wù)器端而言,其輸出響應(yīng)應(yīng)當(dāng)盡量做到: 
  1.  
明確設(shè)置Content-Length字段,以便MIDP應(yīng)用程序能讀取HTTP頭并判斷自身是否有能力處理此長(zhǎng)度的數(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直接寫(xiě)入并設(shè)置Content-Type為application/octet-stream。 
 
  3. 盡量不要重定向URL,這樣會(huì)導(dǎo)致MIDP應(yīng)用程序再次連接服務(wù)器,增加了用戶(hù)的等待時(shí)間和網(wǎng)絡(luò)流量。 
 
  4.  
如果發(fā)生異常,例如請(qǐng)求的資源未找到,或者身份驗(yàn)證失敗,通常,服務(wù)器會(huì)向?yàn)g覽器發(fā)送一個(gè)顯示出錯(cuò)的頁(yè)面,可能還包括一個(gè)用戶(hù)登錄的Form,但是,向MIDP發(fā)送錯(cuò)誤頁(yè)面毫無(wú)意義,應(yīng)當(dāng)直接發(fā)送一個(gè)404或401錯(cuò)誤,這樣MIDP應(yīng)用程序就可以直接讀取HTTP頭的響應(yīng)碼獲取錯(cuò)誤信息而不必繼續(xù)讀取相應(yīng)內(nèi)容。 
 
  5.  
由于服務(wù)器的計(jì)算能力遠(yuǎn)遠(yuǎn)超過(guò)手機(jī)客戶(hù)端,因此,針對(duì)不同客戶(hù)端版本發(fā)送不同響應(yīng)的任務(wù)應(yīng)該在服務(wù)器端完成。例如,根據(jù)客戶(hù)端傳送的User-Agent頭確定客戶(hù)端版本。這樣,低版本的客戶(hù)端不必升級(jí)也能繼續(xù)使用。 
  MIDP的聯(lián)網(wǎng)框架定義了多種協(xié)議的網(wǎng)絡(luò)連接,但是每個(gè)廠(chǎng)商都必須實(shí)現(xiàn)HTTP連接,在MIDP  
2.0中還增加了必須實(shí)現(xiàn)的HTTPS連接。因此,要保證MIDP應(yīng)用程序能在不同廠(chǎng)商的手機(jī)平臺(tái)上移植,最好只使用HTTP連接。雖然HTTP是一個(gè)基于文本的效率較低的協(xié)議,但是由于使用特別廣泛,大多數(shù)服務(wù)器應(yīng)用的前端都是基于HTTP的Web頁(yè)面,因此能最大限度地復(fù)用服務(wù)器端的代碼。只要控制好緩存,仍然有不錯(cuò)的速度。 
  SUN的MIDP庫(kù)提供了javax.microediton.io包,能非常容易地實(shí)現(xiàn)HTTP連接。但是要注意,由于網(wǎng)絡(luò)有很大的延時(shí),必須把聯(lián)網(wǎng)操作放入一個(gè)單獨(dú)的線(xiàn)程中,以避免主線(xiàn)程阻塞導(dǎo)致用戶(hù)界面停止響應(yīng)。事實(shí)上,MIDP運(yùn)行環(huán)境根本就不允許在主線(xiàn)程中操作網(wǎng)絡(luò)連接。因此,我們必須實(shí)現(xiàn)一個(gè)靈活的HTTP聯(lián)網(wǎng)模塊,能讓用戶(hù)非常直觀地看到當(dāng)前上傳和下載的進(jìn)度,并且能夠隨時(shí)取消連接。 
  一個(gè)完整的HTTP連接為:用戶(hù)通過(guò)某個(gè)命令發(fā)起連接請(qǐng)求,然后系統(tǒng)給出一個(gè)等待屏幕提示正在連接,當(dāng)連接正常結(jié)束后,前進(jìn)到下一個(gè)屏幕并處理下載的數(shù)據(jù)。如果連接過(guò)程出現(xiàn)異常,將給用戶(hù)提示并返回到前一個(gè)屏幕。用戶(hù)在等待過(guò)程中能夠隨時(shí)取消并返回前一個(gè)屏幕。 
  我們?cè)O(shè)計(jì)一個(gè)HttpThread線(xiàn)程類(lèi)負(fù)責(zé)在后臺(tái)連接服務(wù)器,HttpListener接口實(shí)現(xiàn)Observer(觀察者)模式,以便HttpThread能提示觀察者下載開(kāi)始、下載結(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的一個(gè)HttpWaitUI屏幕,它顯示一個(gè)進(jìn)度條和一些提示信息,并允許用戶(hù)隨時(shí)中斷連接: 
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連接的線(xiàn)程類(lèi),它接受一個(gè)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獲取內(nèi)容 
  我們先討論最簡(jiǎn)單的GET請(qǐng)求。GET請(qǐng)求只需向服務(wù)器發(fā)送一個(gè)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如果大小無(wú)法確定 
    listener.onSetSize(size); 
    // 開(kāi)始讀響應(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è)定一個(gè)固定大小的緩沖區(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ū)已滿(mǎn),無(wú)法繼續(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就獲得了來(lái)自服務(wù)器的數(shù)據(jù),要傳遞給下一個(gè)屏幕處理,HttpWaitUI必須包含對(duì)此屏幕的引用并通過(guò)一個(gè)setData(DataInputStream  
input)方法讓下一個(gè)屏幕能非常方便地讀取數(shù)據(jù)。因此,定義一個(gè)DataHandler接口: 
public interface DataHandler { 
  void setData(DataInputStream input) throws IOException; 

  HttpWaitUI響應(yīng)HttpThread的onFinish事件并調(diào)用下一個(gè)屏幕的setData方法將數(shù)據(jù)傳遞給它并顯示下一個(gè)屏幕: 
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) { … } 

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

// 發(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... 
(4). 
  使用Cookie保持session 
  通常服務(wù)器使用Session來(lái)跟蹤會(huì)話(huà)。Session的簡(jiǎn)單實(shí)現(xiàn)就是利用Cookie。當(dāng)客戶(hù)端第一次連接服務(wù)器時(shí),服務(wù)器檢測(cè)到客戶(hù)端沒(méi)有相應(yīng)的Cookie字段,就發(fā)送一個(gè)包含一個(gè)識(shí)別碼的Set-Cookie字段。在此后的會(huì)話(huà)過(guò)程中,客戶(hù)端發(fā)送的請(qǐng)求都包含這個(gè)Cookie,因此服務(wù)器能夠識(shí)別出客戶(hù)端曾經(jīng)連接過(guò)服務(wù)器。 
  要實(shí)現(xiàn)與瀏覽器一樣的效果,MIDP應(yīng)用程序必須也能識(shí)別Cookie,并在每個(gè)請(qǐng)求頭中包含此Cookie。 
  在處理每次連接的響應(yīng)中,我們都檢查是否有Set-Cookie這個(gè)頭,如果有,則是服務(wù)器第一次發(fā)送的Session  
ID,或者服務(wù)器認(rèn)為會(huì)話(huà)超時(shí),需要重新生成一個(gè)Session ID。如果檢測(cè)到Set-Cookie頭,就將其保存,并在隨后的每次請(qǐng)求中附加它: 
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=/ 
  我們無(wú)須關(guān)心Session ID的內(nèi)容,服務(wù)器自己會(huì)識(shí)別它。我們只需在隨后的請(qǐng)求中附加上這個(gè)Session ID即可: 
if(session!=null) 
  hc.setRequestProperty("Cookie", session); 
  對(duì)于URL重寫(xiě)來(lái)保持Session的方法,在PC客戶(hù)端可能很有用,但是,由于MIDP程序很難分析出URL中有用的Session信息,因此,不推薦使用這種方法。

(出處:http://m.survivalescaperooms.com)



發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 辽中县| 太谷县| 裕民县| 新干县| 黑山县| 积石山| 乃东县| 平邑县| 攀枝花市| 济阳县| 手游| 栾川县| 开江县| 铜梁县| 图木舒克市| 宜春市| 大石桥市| 双鸭山市| 天气| 南和县| 静海县| 宜川县| 通许县| 嫩江县| 措勤县| 江陵县| 乡城县| 绥滨县| 姚安县| 准格尔旗| 惠水县| 罗田县| 三门峡市| 萨嘎县| 洛阳市| 蒙自县| 镶黄旗| 茌平县| 工布江达县| 依安县| 西峡县|