J2ME最佳聯網方案終結版
作者:佚名 文章來源:轉載 更新時間:2006-12-1 13:35:55
478
(1) .由於無線設備所能支持的網絡協議非常有限,僅限於HTTP,Socket,UDP等幾種協議,不同的廠家可能還支持其他網絡協議,但是,MIDP 無線論壇[http://www.J2ME.com.cn/bbs]
1.0規范規定,HTTP協議是必須實現的協議,而其他協議的實現都是可選的。因此,為了能在不同類型的手機上移植,我們盡量采用HTTP作為網絡連接的首選協議,這樣還能重用服務器端的代碼。但是,由於HTTP是一個基於文本的效率較低的協議,因此,必須仔細考慮手機和服務器端的通信內容,盡可能地提高效率。無線論壇[http://www.J2ME.com.cn/bbs]
對於MIDP應用程序,應當盡量做到:無線論壇[http://www.J2ME.com.cn/bbs]
1.發送請求時,附加一個User-Agent頭,傳入MIDP和自身版本號,以便服務器能識別此請求來自MIDP應用程序,並且根據版本號發送相應的相應。無線論壇[http://www.J2ME.com.cn/bbs]
無線論壇[http://www.J2ME.com.cn/bbs]
2.連接服務器時,顯示一個下載進度條使用戶能看到下載進度,並能隨時中斷連接。無線論壇[http://www.J2ME.com.cn/bbs]
無線論壇[http://www.J2ME.com.cn/bbs]
3.由於無線網絡連接速度還很慢,因此有必要將某些數據緩存起來,可以存儲在內存中,也可以放到RMS中。無線論壇[http://www.J2ME.com.cn/bbs]
對於服務器端而言,其輸出響應應當盡量做到:無線論壇[http://www.J2ME.com.cn/bbs]
1. 無線論壇[http://www.J2ME.com.cn/bbs]
明確設置Content-Length字段,以便MIDP應用程序能讀取HTTP頭並判斷自身是否有能力處理此長度的數據,如果不能,可以直接關閉連接而不必繼續讀取HTTP正文。無線論壇[http://www.J2ME.com.cn/bbs]
無線論壇[http://www.J2ME.com.cn/bbs]
2. 無線論壇[http://www.J2ME.com.cn/bbs]
服務器不應當發送HTML內容,因為MIDP應用程序很難解析Html,XML雖然能夠解析,但是耗費CPU和內存資源,因此,應當發送緊湊的二進制內容,用DataOutputStream直接寫入並設置Content-Type為application/octet-stream。無線論壇[http://www.J2ME.com.cn/bbs]
無線論壇[http://www.J2ME.com.cn/bbs]
3. 盡量不要重定向URL,這樣會導致MIDP應用程序再次連接服務器,增加了用戶的等待時間和網絡流量。無線論壇[http://www.J2ME.com.cn/bbs]
無線論壇[http://www.J2ME.com.cn/bbs]
4. 無線論壇[http://www.J2ME.com.cn/bbs]
如果發生異常,例如請求的資源未找到,或者身份驗證失敗,通常,服務器會向浏覽器發送一個顯示出錯的頁面,可能還包括一個用戶登錄的Form,但是,向MIDP發送錯誤頁面毫無意義,應當直接發送一個404或401錯誤,這樣MIDP應用程序就可以直接讀取HTTP頭的響應碼獲取錯誤信息而不必繼續讀取相應內容。無線論壇[http://www.J2ME.com.cn/bbs]
無線論壇[http://www.J2ME.com.cn/bbs]
5. 無線論壇[http://www.J2ME.com.cn/bbs]
由於服務器的計算能力遠遠超過手機客戶端,因此,針對不同客戶端版本發送不同響應的任務應該在服務器端完成。例如,根據客戶端傳送的User-Agent頭確定客戶端版本。這樣,低版本的客戶端不必升級也能繼續使用。無線論壇[http://www.J2ME.com.cn/bbs]
MIDP的聯網框架定義了多種協議的網絡連接,但是每個廠商都必須實現HTTP連接,在MIDP 無線論壇[http://www.J2ME.com.cn/bbs]
2.0中還增加了必須實現的HTTPS連接。因此,要保證MIDP應用程序能在不同廠商的手機平台上移植,最好只使用HTTP連接。雖然HTTP是一個基於文本的效率較低的協議,但是由於使用特別廣泛,大多數服務器應用的前端都是基於HTTP的Web頁面,因此能最大限度地復用服務器端的代碼。只要控制好緩存,仍然有不錯的速度。無線論壇[http://www.J2ME.com.cn/bbs]
SUN的MIDP庫提供了Javax.microediton.io包,能非常容易地實現HTTP連接。但是要注意,由於網絡有很大的延時,必須把聯網操作放入一個單獨的線程中,以避免主線程阻塞導致用戶界面停止響應。事實上,MIDP運行環境根本就不允許在主線程中操作網絡連接。因此,我們必須實現一個靈活的HTTP聯網模塊,能讓用戶非常直觀地看到當前上傳和下載的進度,並且能夠隨時取消連接。無線論壇[http://www.J2ME.com.cn/bbs]
一個完整的HTTP連接為:用戶通過某個命令發起連接請求,然後系統給出一個等待屏幕提示正在連接,當連接正常結束後,前進到下一個屏幕並處理下載的數據。如果連接過程出現異常,將給用戶提示並返回到前一個屏幕。用戶在等待過程中能夠隨時取消並返回前一個屏幕。無線論壇[http://www.J2ME.com.cn/bbs]
我們設計一個HttpThread線程類負責在後台連接服務器,HttpListener接口實現Observer(觀察者)模式,以便HttpThread能提示觀察者下載開始、下載結束、更新進度條等。HttpListener接口如下:無線論壇[http://www.J2ME.com.cn/bbs]
public interface HttpListener {無線論壇[http://www.J2ME.com.cn/bbs]
void onSetSize(int size);無線論壇[http://www.J2ME.com.cn/bbs]
void onFinish(byte[] data, int size);無線論壇[http://www.J2ME.com.cn/bbs]
void onProgress(int percent);無線論壇[http://www.J2ME.com.cn/bbs]
void onError(int code, String message);無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
實現HttpListener接口的是繼承自Form的一個HttpWaitUI屏幕,它顯示一個進度條和一些提示信息,並允許用戶隨時中斷連接:無線論壇[http://www.J2ME.com.cn/bbs]
public class HttpWaitUI extends Form implements CommandListener, HttpListener {無線論壇[http://www.J2ME.com.cn/bbs]
private Gauge gauge;無線論壇[http://www.J2ME.com.cn/bbs]
private Command cancel;無線論壇[http://www.J2ME.com.cn/bbs]
private HttpThread downloader;無線論壇[http://www.J2ME.com.cn/bbs]
private Displayable displayable;無線論壇[http://www.J2ME.com.cn/bbs]
public HttpWaitUI(String url, Displayable displayable) {無線論壇[http://www.J2ME.com.cn/bbs]
super("Connecting");無線論壇[http://www.J2ME.com.cn/bbs]
this.gauge = new Gauge("Progress", false, 100, 0);無線論壇[http://www.J2ME.com.cn/bbs]
this.cancel = new Command("Cancel", Command.CANCEL, 0);無線論壇[http://www.J2ME.com.cn/bbs]
append(gauge);無線論壇[http://www.J2ME.com.cn/bbs]
addCommand(cancel);無線論壇[http://www.J2ME.com.cn/bbs]
setCommandListener(this);無線論壇[http://www.J2ME.com.cn/bbs]
downloader = new HttpThread(url, this);無線論壇[http://www.J2ME.com.cn/bbs]
downloader.start();無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
public void commandAction(Command c, Displayable d) {無線論壇[http://www.J2ME.com.cn/bbs]
if(c==cancel) {無線論壇[http://www.J2ME.com.cn/bbs]
downloader.cancel();無線論壇[http://www.J2ME.com.cn/bbs]
ControllerMIDlet.goBack();無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
public void onFinish(byte[] buffer, int size) { … }無線論壇[http://www.J2ME.com.cn/bbs]
public void onError(int code, String message) { … }無線論壇[http://www.J2ME.com.cn/bbs]
public void onProgress(int percent) { … }無線論壇[http://www.J2ME.com.cn/bbs]
public void onSetSize(int size) { … }無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
HttpThread是負責處理Http連接的線程類,它接受一個URL和HttpListener:無線論壇[http://www.J2ME.com.cn/bbs]
class HttpThread extends Thread {無線論壇[http://www.J2ME.com.cn/bbs]
private static final int MAX_LENGTH = 20 * 1024; // 20K無線論壇[http://www.J2ME.com.cn/bbs]
private boolean cancel = false;無線論壇[http://www.J2ME.com.cn/bbs]
private String url;無線論壇[http://www.J2ME.com.cn/bbs]
private byte[] buffer = null;無線論壇[http://www.J2ME.com.cn/bbs]
private HttpListener listener;無線論壇[http://www.J2ME.com.cn/bbs]
public HttpThread(String url, HttpListener listener) {無線論壇[http://www.J2ME.com.cn/bbs]
this.url = url;無線論壇[http://www.J2ME.com.cn/bbs]
this.listener = listener;無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
public void cancel() { cancel = true; }無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
(2). 無線論壇[http://www.J2ME.com.cn/bbs]
使用GET獲取內容無線論壇[http://www.J2ME.com.cn/bbs]
我們先討論最簡單的GET請求。GET請求只需向服務器發送一個URL,然後取得服務器響應即可。在HttpThread的run()方法中實現如下:無線論壇[http://www.J2ME.com.cn/bbs]
public void run() {無線論壇[http://www.J2ME.com.cn/bbs]
HttpConnection hc = null;無線論壇[http://www.J2ME.com.cn/bbs]
InputStream input = null;無線論壇[http://www.J2ME.com.cn/bbs]
try {無線論壇[http://www.J2ME.com.cn/bbs]
hc = (HttpConnection)Connector.open(url);無線論壇[http://www.J2ME.com.cn/bbs]
hc.setRequestMethod(HttpConnection.GET); // 默認即為GET無線論壇[http://www.J2ME.com.cn/bbs]
hc.setRequestProperty("User-Agent", USER_AGENT);無線論壇[http://www.J2ME.com.cn/bbs]
// get response code:無線論壇[http://www.J2ME.com.cn/bbs]
int code = hc.getResponseCode();無線論壇[http://www.J2ME.com.cn/bbs]
if(code!=HttpConnection.HTTP_OK) {無線論壇[http://www.J2ME.com.cn/bbs]
listener.onError(code, hc.getResponseMessage());無線論壇[http://www.J2ME.com.cn/bbs]
return;無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
// get size:無線論壇[http://www.J2ME.com.cn/bbs]
int size = (int)hc.getLength(); // 返回響應大小,或者-1如果大小無法確定無線論壇[http://www.J2ME.com.cn/bbs]
listener.onSetSize(size);無線論壇[http://www.J2ME.com.cn/bbs]
// 開始讀響應:無線論壇[http://www.J2ME.com.cn/bbs]
input = hc.openInputStream();無線論壇[http://www.J2ME.com.cn/bbs]
int percent = 0; // percentage無線論壇[http://www.J2ME.com.cn/bbs]
int tmp_percent = 0;無線論壇[http://www.J2ME.com.cn/bbs]
int index = 0; // buffer index無線論壇[http://www.J2ME.com.cn/bbs]
int reads; // each byte無線論壇[http://www.J2ME.com.cn/bbs]
if(size!=(-1))無線論壇[http://www.J2ME.com.cn/bbs]
buffer = new byte[size]; // 響應大小已知,確定緩沖區大小無線論壇[http://www.J2ME.com.cn/bbs]
else無線論壇[http://www.J2ME.com.cn/bbs]
buffer = new byte[MAX_LENGTH]; // 響應大小未知,設定一個固定大小的緩沖區無線論壇[http://www.J2ME.com.cn/bbs]
while(!cancel) {無線論壇[http://www.J2ME.com.cn/bbs]
int len = buffer.length - index;無線論壇[http://www.J2ME.com.cn/bbs]
len = len>128 ? 128 : len;無線論壇[http://www.J2ME.com.cn/bbs]
reads = input.read(buffer, index, len);無線論壇[http://www.J2ME.com.cn/bbs]
if(reads<=0)無線論壇[http://www.J2ME.com.cn/bbs]
break;無線論壇[http://www.J2ME.com.cn/bbs]
index += reads;無線論壇[http://www.J2ME.com.cn/bbs]
if(size>0) { // 更新進度無線論壇[http://www.J2ME.com.cn/bbs]
tmp_percent = index * 100 / size;無線論壇[http://www.J2ME.com.cn/bbs]
if(tmp_percent!=percent) {無線論壇[http://www.J2ME.com.cn/bbs]
percent = tmp_percent;無線論壇[http://www.J2ME.com.cn/bbs]
listener.onProgress(percent);無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
if(!cancel && input.available()>0) // 緩沖區已滿,無法繼續讀取無線論壇[http://www.J2ME.com.cn/bbs]
listener.onError(601, "Buffer overflow.");無線論壇[http://www.J2ME.com.cn/bbs]
if(!cancel) {無線論壇[http://www.J2ME.com.cn/bbs]
if(size!=(-1) && index!=size)無線論壇[http://www.J2ME.com.cn/bbs]
listener.onError(102, "Content-Length does not match.");無線論壇[http://www.J2ME.com.cn/bbs]
else無線論壇[http://www.J2ME.com.cn/bbs]
listener.onFinish(buffer, index);無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
catch(IOException ioe) {無線論壇[http://www.J2ME.com.cn/bbs]
listener.onError(101, "IOException: " + ioe.getMessage());無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
finally { // 清理資源無線論壇[http://www.J2ME.com.cn/bbs]
if(input!=null)無線論壇[http://www.J2ME.com.cn/bbs]
try { input.close(); } catch(IOException ioe) {}無線論壇[http://www.J2ME.com.cn/bbs]
if(hc!=null)無線論壇[http://www.J2ME.com.cn/bbs]
try { hc.close(); } catch(IOException ioe) {}無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
當下載完畢後,HttpWaitUI就獲得了來自服務器的數據,要傳遞給下一個屏幕處理,HttpWaitUI必須包含對此屏幕的引用並通過一個setData(DataInputStream 無線論壇[http://www.J2ME.com.cn/bbs]
input)方法讓下一個屏幕能非常方便地讀取數據。因此,定義一個DataHandler接口:無線論壇[http://www.J2ME.com.cn/bbs]
public interface DataHandler {無線論壇[http://www.J2ME.com.cn/bbs]
void setData(DataInputStream input) throws IOException;無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
HttpWaitUI響應HttpThread的onFinish事件並調用下一個屏幕的setData方法將數據傳遞給它並顯示下一個屏幕:無線論壇[http://www.J2ME.com.cn/bbs]
public void onFinish(byte[] buffer, int size) {無線論壇[http://www.J2ME.com.cn/bbs]
byte[] data = buffer;無線論壇[http://www.J2ME.com.cn/bbs]
if(size!=buffer.length) {無線論壇[http://www.J2ME.com.cn/bbs]
data = new byte[size];無線論壇[http://www.J2ME.com.cn/bbs]
System.arraycopy(data, 0, buffer, 0, size);無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
DataInputStream input = null;無線論壇[http://www.J2ME.com.cn/bbs]
try {無線論壇[http://www.J2ME.com.cn/bbs]
input = new DataInputStream(new ByteArrayInputStream(data));無線論壇[http://www.J2ME.com.cn/bbs]
if(displayable instanceof DataHandler)無線論壇[http://www.J2ME.com.cn/bbs]
((DataHandler)displayable).setData(input);無線論壇[http://www.J2ME.com.cn/bbs]
else無線論壇[http://www.J2ME.com.cn/bbs]
System.err.println("[WARNING] Displayable object cannot handle 無線論壇[http://www.J2ME.com.cn/bbs]
data.");無線論壇[http://www.J2ME.com.cn/bbs]
ControllerMIDlet.replace(displayable);無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
catch(IOException ioe) { … }無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
以下載一則新聞為例,一個完整的HTTP GET請求過程如下:無線論壇[http://www.J2ME.com.cn/bbs]
首先,用戶通過點擊某個屏幕的命令希望閱讀指定的一則新聞,在commandAction事件中,我們初始化HttpWaitUI和顯示數據的NewsUI屏幕:無線論壇[http://www.J2ME.com.cn/bbs]
public void commandAction(Command c, Displayable d) {無線論壇[http://www.J2ME.com.cn/bbs]
HttpWaitUI wait = new HttpWaitUI("http://192.168.0.1/news.do?id=1", new 無線論壇[http://www.J2ME.com.cn/bbs]
NewsUI());無線論壇[http://www.J2ME.com.cn/bbs]
ControllerMIDlet.forward(wait);無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
NewsUI實現DataHandler接口並負責顯示下載的數據:無線論壇[http://www.J2ME.com.cn/bbs]
public class NewsUI extends Form implements DataHandler {無線論壇[http://www.J2ME.com.cn/bbs]
public void setData(DataInputStream input) throws IOException {無線論壇[http://www.J2ME.com.cn/bbs]
String title = input.readUTF();無線論壇[http://www.J2ME.com.cn/bbs]
Date date = new Date(input.readLong());無線論壇[http://www.J2ME.com.cn/bbs]
String text = input.readUTF();無線論壇[http://www.J2ME.com.cn/bbs]
append(new StringItem("Title", title));無線論壇[http://www.J2ME.com.cn/bbs]
append(new StringItem("Date", date.toString()));無線論壇[http://www.J2ME.com.cn/bbs]
append(text);無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
服務器端只要以String, long, 無線論壇[http://www.J2ME.com.cn/bbs]
String的順序依次寫入DataOutputStream,MIDP客戶端就可以通過DataInputStream依次取得相應的數據,完全不需要解析XML之類的文本,非常高效而且方便。無線論壇[http://www.J2ME.com.cn/bbs]
需要獲得聯網數據的屏幕只需實現DataHandler接口,並向HttpWaitUI傳入一個URL即可復用上述代碼,無須關心如何連接網絡以及如何處理用戶中斷連接。無線論壇[http://www.J2ME.com.cn/bbs]
(3).無線論壇[http://www.J2ME.com.cn/bbs]
使用POST發送數據無線論壇[http://www.J2ME.com.cn/bbs]
以POST方式發送數據主要是為了向服務器發送較大量的客戶端的數據,它不受URL的長度限制。POST請求將數據以URL編碼的形式放在HTTP正文中,字段形式為fIEldname=value,用&分隔每個字段。注意所有的字段都被作為字符串處理。實際上我們要做的就是模擬浏覽器POST一個表單。以下是IE發送一個登陸表單的POST請求:無線論壇[http://www.J2ME.com.cn/bbs]
POST http://127.0.0.1/login.do HTTP/1.0無線論壇[http://www.J2ME.com.cn/bbs]
Accept: image/gif, image/jpeg, image/pjpeg, */*無線論壇[http://www.J2ME.com.cn/bbs]
Accept-Language: en-us,zh-cn;q=0.5無線論壇[http://www.J2ME.com.cn/bbs]
Content-Type: application/x-www-form-urlencoded無線論壇[http://www.J2ME.com.cn/bbs]
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)無線論壇[http://www.J2ME.com.cn/bbs]
Content-Length: 28無線論壇[http://www.J2ME.com.cn/bbs]
\r\n無線論壇[http://www.J2ME.com.cn/bbs]
username=admin&passWord=1234無線論壇[http://www.J2ME.com.cn/bbs]
要在MIDP應用程序中模擬浏覽器發送這個POST請求,首先設置HttpConnection的請求方式為POST:無線論壇[http://www.J2ME.com.cn/bbs]
hc.setRequestMethod(HttpConnection.POST);無線論壇[http://www.J2ME.com.cn/bbs]
然後構造出HTTP正文:無線論壇[http://www.J2ME.com.cn/bbs]
byte[] data = "username=admin&passWord=1234".getBytes();無線論壇[http://www.J2ME.com.cn/bbs]
並計算正文長度,填入Content-Type和Content-Length:無線論壇[http://www.J2ME.com.cn/bbs]
hc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");無線論壇[http://www.J2ME.com.cn/bbs]
hc.setRequestProperty("Content-Length", String.valueOf(data.length));無線論壇[http://www.J2ME.com.cn/bbs]
然後打開OutputStream將正文寫入:無線論壇[http://www.J2ME.com.cn/bbs]
OutputStream output = hc.openOutputStream();無線論壇[http://www.J2ME.com.cn/bbs]
output.write(data);無線論壇[http://www.J2ME.com.cn/bbs]
需要注意的是,數據仍需要以URL編碼格式編碼,由於MIDP庫中沒有J2SE中與之對應的URLEncoder類,因此,需要自己動手編寫這個encode()方法,可以參考Java.Net.URLEncoder.Java的源碼。剩下的便是讀取服務器響應,代碼與GET一致,這裡就不再詳述。無線論壇[http://www.J2ME.com.cn/bbs]
使用multipart/form-data發送文件無線論壇[http://www.J2ME.com.cn/bbs]
如果要在MIDP客戶端向服務器上傳文件,我們就必須模擬一個POST 無線論壇[http://www.J2ME.com.cn/bbs]
multipart/form-data類型的請求,Content-Type必須是multipart/form-data。無線論壇[http://www.J2ME.com.cn/bbs]
以multipart/form-data編碼的POST請求格式與application/x-www-form-urlencoded完全不同,multipart/form-data需要首先在HTTP請求頭設置一個分隔符,例如ABCD:無線論壇[http://www.J2ME.com.cn/bbs]
hc.setRequestProperty("Content-Type", "multipart/form-data; boundary=ABCD");無線論壇[http://www.J2ME.com.cn/bbs]
然後,將每個字段用“--分隔符”分隔,最後一個“--分隔符--”表示結束。例如,要上傳一個title字段"Today"和一個文件C:\1.txt,HTTP正文如下:無線論壇[http://www.J2ME.com.cn/bbs]
--ABCD無線論壇[http://www.J2ME.com.cn/bbs]
Content-Disposition: form-data; name="title"無線論壇[http://www.J2ME.com.cn/bbs]
\r\n無線論壇[http://www.J2ME.com.cn/bbs]
Today無線論壇[http://www.J2ME.com.cn/bbs]
--ABCD無線論壇[http://www.J2ME.com.cn/bbs]
Content-Disposition: form-data; name="1.txt"; filename="C:\1.txt"無線論壇[http://www.J2ME.com.cn/bbs]
Content-Type: text/plain無線論壇[http://www.J2ME.com.cn/bbs]
\r\n無線論壇[http://www.J2ME.com.cn/bbs]
<這裡是1.txt文件的內容>無線論壇[http://www.J2ME.com.cn/bbs]
--ABCD--無線論壇[http://www.J2ME.com.cn/bbs]
\r\n無線論壇[http://www.J2ME.com.cn/bbs]
請注意,每一行都必須以\r\n結束,包括最後一行。如果用Sniffer程序檢測IE發送的POST請求,可以發現IE的分隔符類似於---------------------------7d4a6d158c9,這是IE產生的一個隨機數,目的是防止上傳文件中出現分隔符導致服務器無法正確識別文件起始位置。我們可以寫一個固定的分隔符,只要足夠復雜即可。無線論壇[http://www.J2ME.com.cn/bbs]
發送文件的POST代碼如下:無線論壇[http://www.J2ME.com.cn/bbs]
String[] props = ... // 字段名無線論壇[http://www.J2ME.com.cn/bbs]
String[] values = ... // 字段值無線論壇[http://www.J2ME.com.cn/bbs]
byte[] file = ... // 文件內容無線論壇[http://www.J2ME.com.cn/bbs]
String BOUNDARY = "---------------------------7d4a6d158c9"; // 分隔符無線論壇[http://www.J2ME.com.cn/bbs]
StringBuffer sb = new StringBuffer();無線論壇[http://www.J2ME.com.cn/bbs]
// 發送每個字段:無線論壇[http://www.J2ME.com.cn/bbs]
for(int i=0; i sb = sb.append("--");無線論壇[http://www.J2ME.com.cn/bbs]
sb = sb.append(BOUNDARY);無線論壇[http://www.J2ME.com.cn/bbs]
sb = sb.append("\r\n");無線論壇[http://www.J2ME.com.cn/bbs]
sb = sb.append("Content-Disposition: form-data; name=\""+ props + 無線論壇[http://www.J2ME.com.cn/bbs]
"\"\r\n\r\n");無線論壇[http://www.J2ME.com.cn/bbs]
sb = sb.append(URLEncoder.encode(values));無線論壇[http://www.J2ME.com.cn/bbs]
sb = sb.append("\r\n");無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
// 發送文件:無線論壇[http://www.J2ME.com.cn/bbs]
sb = sb.append("--");無線論壇[http://www.J2ME.com.cn/bbs]
sb = sb.append(BOUNDARY);無線論壇[http://www.J2ME.com.cn/bbs]
sb = sb.append("\r\n");無線論壇[http://www.J2ME.com.cn/bbs]
sb = sb.append("Content-Disposition: form-data; name=\"1\"; 無線論壇[http://www.J2ME.com.cn/bbs]
filename=\"1.txt\"\r\n");無線論壇[http://www.J2ME.com.cn/bbs]
sb = sb.append("Content-Type: application/octet-stream\r\n\r\n");無線論壇[http://www.J2ME.com.cn/bbs]
byte[] data = sb.toString().getBytes();無線論壇[http://www.J2ME.com.cn/bbs]
byte[] end_data = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();無線論壇[http://www.J2ME.com.cn/bbs]
// 設置HTTP頭:無線論壇[http://www.J2ME.com.cn/bbs]
hc.setRequestProperty("Content-Type", MULTIPART_FORM_DATA + "; boundary=" + 無線論壇[http://www.J2ME.com.cn/bbs]
BOUNDARY);無線論壇[http://www.J2ME.com.cn/bbs]
hc.setRequestProperty("Content-Length", String.valueOf(data.length + file.length 無線論壇[http://www.J2ME.com.cn/bbs]
+ end_data.length));無線論壇[http://www.J2ME.com.cn/bbs]
// 輸出:無線論壇[http://www.J2ME.com.cn/bbs]
output = hc.openOutputStream();無線論壇[http://www.J2ME.com.cn/bbs]
output.write(data);無線論壇[http://www.J2ME.com.cn/bbs]
output.write(file);無線論壇[http://www.J2ME.com.cn/bbs]
output.write(end_data);無線論壇[http://www.J2ME.com.cn/bbs]
// 讀取服務器響應:無線論壇[http://www.J2ME.com.cn/bbs]
// TODO...無線論壇[http://www.J2ME.com.cn/bbs]
(4).無線論壇[http://www.J2ME.com.cn/bbs]
使用CookIE保持Session無線論壇[http://www.J2ME.com.cn/bbs]
通常服務器使用Session來跟蹤會話。Session的簡單實現就是利用Cookie。當客戶端第一次連接服務器時,服務器檢測到客戶端沒有相應的Cookie字段,就發送一個包含一個識別碼的Set-Cookie字段。在此後的會話過程中,客戶端發送的請求都包含這個CookIE,因此服務器能夠識別出客戶端曾經連接過服務器。無線論壇[http://www.J2ME.com.cn/bbs]
要實現與浏覽器一樣的效果,MIDP應用程序必須也能識別Cookie,並在每個請求頭中包含此CookIE。無線論壇[http://www.J2ME.com.cn/bbs]
在處理每次連接的響應中,我們都檢查是否有Set-CookIE這個頭,如果有,則是服務器第一次發送的Session 無線論壇[http://www.J2ME.com.cn/bbs]
ID,或者服務器認為會話超時,需要重新生成一個Session ID。如果檢測到Set-CookIE頭,就將其保存,並在隨後的每次請求中附加它:無線論壇[http://www.J2ME.com.cn/bbs]
String session = null;無線論壇[http://www.J2ME.com.cn/bbs]
String cookie = hc.getHeaderField("Set-CookIE");無線論壇[http://www.J2ME.com.cn/bbs]
if(cookIE!=null) {無線論壇[http://www.J2ME.com.cn/bbs]
int n = cookIE.indexOf(';');無線論壇[http://www.J2ME.com.cn/bbs]
session = cookIE.substring(0, n);無線論壇[http://www.J2ME.com.cn/bbs]
}無線論壇[http://www.J2ME.com.cn/bbs]
使用Sniffer程序可以捕獲到不同的Web服務器發送的Session。WebLogic Server 7.0返回的Session如下:無線論壇[http://www.J2ME.com.cn/bbs]
Set-CookIE: 無線論壇[http://www.J2ME.com.cn/bbs]
JSESSIONID=CxP4FMwOJB06XCByBWfwZBQ0IfkroKO2W7FZpkLbmWsnERuN5u2L!-1200402410; 無線論壇[http://www.J2ME.com.cn/bbs]
path=/無線論壇[http://www.J2ME.com.cn/bbs]
而Resin 2.1返回的Session則是:無線論壇[http://www.J2ME.com.cn/bbs]
Set-CookIE: JSESSIONID= aTMCmwe9F5j9; path=/無線論壇[http://www.J2ME.com.cn/bbs]
運行ASP.Net的IIS返回的Session:無線論壇[http://www.J2ME.com.cn/bbs]
Set-CookIE: ASPSESSIONIDQATSASQB=GNGEEJIDMDFCMOOFLEAKDGGP; path=/無線論壇[http://www.J2ME.com.cn/bbs]
我們無須關心Session ID的內容,服務器自己會識別它。我們只需在隨後的請求中附加上這個Session ID即可:無線論壇[http://www.J2ME.com.cn/bbs]
if(session!=null)無線論壇[http://www.J2ME.com.cn/bbs]
hc.setRequestProperty("CookIE", session);無線論壇[http://www.J2ME.com.cn/bbs]
對於URL重寫來保持Session的方法,在PC客戶端可能很有用,但是,由於MIDP程序很難分析出URL中有用的Session信息,因此,不推薦使用這種方法。