1. Because wireless devices can support a very limited network protocol, limited to HTTP,SOCKET,UDP and other protocols, different manufacturers may also support other network protocols, however, MIDP
The 1.0 specification stipulates that the HTTP protocol is a protocol that must be implemented, while the implementation of other protocols is optional. Therefore, in order to be able to migrate on different types of mobile phones, we try to use HTTP as the preferred protocol for network connectivity, so that we can reuse server-side code. However, because HTTP is a text-based, less efficient protocol, you must carefully consider the content of your phone and server-side communications to maximize efficiency.
For MIDP applications, you should try to:
1. When a request is sent, a user-agent header is appended, the MIDP and its version number are passed in so that the server can identify the request from the MIDP application and send the appropriate corresponding according to the version number.
2. When you connect to the server, a download progress bar is displayed so that users can see the download progress and can disconnect at any time.
3. Because the wireless network connection is slow, it is necessary to cache some data that can be stored in memory or in RMS.
For the server side, the output response should be as far as possible:
1.
Explicitly set the Content-length field so that the MIDP application can read the HTTP headers and determine whether it is capable of processing this length of data, and if not, you can close the connection directly without continuing to read the HTTP body.
2.
The server should not send HTML content because the MIDP application is difficult to parse html,xml although it can resolve, but consumes CPU and memory resources, so you should send compact binary content, Write directly with DataOutputStream and set Content-type to Application/octet-stream.
3. Try not to redirect URLs, which can cause MIDP applications to connect to the server again, increasing user latency and network traffic.
4.
If an exception occurs, such as if the requested resource is not found, or if authentication fails, the server typically sends an error-prone page to the browser, possibly including a user-logged in form, but sending an error page to MIDP is meaningless and should send a 404 or 401 error directly, This allows the MIDP application to directly read the response code of the HTTP header to get the error message without continuing to read the corresponding content.
5.
Because the server has far more computing power than the mobile client, the task of sending different responses for different client versions should be done on the server side. For example, the client version is determined based on the user-agent headers sent by the client. This allows the lower-version client to continue to use without upgrading.
The MIDP networking Framework defines network connections for a variety of protocols, but each vendor must implement an HTTP connection, in the MIDP
The HTTPS connection that must be implemented is also added in 2.0. Therefore, it is best to use only HTTP connections to ensure that MIDP applications can be ported on different vendors ' mobile platforms. Although HTTP is a less text-based protocol, because of its extensive use, most server applications have front-end HTTP-based Web pages that maximize the reuse of server-side code. As long as the cache is well controlled, there is still a good speed.
Sun's MIDP library provides a Javax.microediton.io package that makes it easy to implement HTTP connections. However, it is important to note that network operations must be placed in a separate thread to avoid the main thread blocking causing the user interface to stop responding because of the large delay in the web. In fact, the MIDP runtime environment simply does not allow network connections to be manipulated in the main thread. Therefore, we must implement a flexible HTTP networking module, so that users can be very intuitive to see the current upload and download progress, and can cancel the connection at any time.
A complete HTTP connection is: The user initiates a connection request through a command, and then the system gives a wait ScreenTip that is connected and, when the connection is finished, advances to the next screen and processes the downloaded data. If an exception occurs during the connection process, the user is prompted and returned to the previous screen. The user can cancel and return to the previous screen at any time during the wait process.
We design a httpthread thread class that is responsible for connecting servers in the background, HTTPLISTENER Interface Implementation Observer (Observer) mode, so httpthread can prompt the viewer to download the start, download the end, update the progress bar and so on. The HttpListener interface is as follows:
Public interface HttpListener {
void onsetsize (int size);
void OnFinish (byte[] data, int size);
void onprogress (int percent);
void OnError (int code, String message);
}
Implementing the HttpListener interface is a httpwaitui screen that inherits from form, displaying a progress bar and some hint messages, and allowing users to disconnect at any time:
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 is the thread class responsible for handling HTTP connections, which accepts a URL and 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).
Getting content using get
Let's discuss the simplest get requests first. A GET request simply sends a URL to the server and then gets a response from the server. The following is implemented in the Httpthread Run () method:
public void Run () {
Httpconnection HC = NULL;
InputStream input = null;
try {
HC = (httpconnection) connector.open (URL);
Hc.setrequestmethod (Httpconnection.get); The default is 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 (); Returns the response size, or-1 if the size is not determined
Listener.onsetsize (size);
Start Read response:
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]; Response size known, determining buffer size
Else
Buffer = new Byte[max_length]; Unknown response size, set a fixed size buffer
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) {//Update progress
tmp_percent = index * 100/SIZE;
if (tmp_percent!=percent) {
Percent = Tmp_percent;
Listener.onprogress (percent);
}
}
}
if (!cancel && input.available () >0)//Buffer full, unable to continue reading
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 ("IOException:" + ioe.getmessage ());
}
Finally {//cleanup resources
if (input!=null)
try {input.close ();} catch (IOException IoE) {}
if (hc!=null)
try {hc.close ();} catch (IOException IoE) {}
}
}
When the download is complete, Httpwaitui gets the data from the server and, to pass to the next screen processing, Httpwaitui must include a reference to the screen and pass a SetData (datainputstream
Input) method makes it easy for the next screen to read data. Therefore, define a DataHandler interface:
Public interface DataHandler {
void SetData (DataInputStream input) throws IOException;
}
Httpwaitui the SetData method that responds to Httpthread's OnFinish event and calls the next screen to pass the data to it and display the next screen:
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) {...}
}
To download a news example, a complete HTTP GET request process is as follows:
First, the user wants to read a specified piece of news by clicking on a screen command, in the Commandaction event, we initialize the Httpwaitui and display the Newsui screen of the data:
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 implements the DataHandler interface and is responsible for displaying downloaded data:
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);
}
}
Server-side as long as string, long,
The sequence of strings is written to the DATAOUTPUTSTREAM,MIDP client to obtain the corresponding data by DataInputStream, and it is very efficient and convenient to parse the text without parsing the XML at all.
A screen that requires networked data needs to implement the DataHandler interface and to httpwaitui a URL to reuse the code, without concern about how to connect to the network and how to handle user-interrupted connections.
(3).
Send data using Post
Post data is sent primarily to send a larger number of client-side data to the server, which is not limited by the length of the URL. The POST request places the data in the HTTP body in the form of a URL encoding, in the form of Fieldname=value, with & separating each field. Note that all fields are treated as strings. What we're actually going to do is simulate the browser post a form. The following is an IE POST request to send a login form:
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
To simulate a browser in a MIDP application to send this post request, first set the httpconnection request to post:
Hc.setrequestmethod (Httpconnection.post);
The HTTP body is then constructed:
byte[] data = "username=admin&password=1234". GetBytes ();
and calculate the body length, fill in Content-type and content-length:
Hc.setrequestproperty ("Content-type", "application/x-www-form-urlencoded");
Hc.setrequestproperty ("Content-length", String.valueof (Data.length));
Then open OutputStream to write the body:
OutputStream output = Hc.openoutputstream ();
Output.write (data);
Note that the data still needs to be encoded in a URL encoding format, because the MIDP library does not have J2SE in the corresponding Urlencoder class, so you need to write the Encode () method, you can refer to the Java.net.URLEncoder.java source code. The rest is to read the server response, and the code is consistent with get, which is no longer detailed here.
Sending files using Multipart/form-data
If you want to upload files to the server on the MIDP client, we must simulate a post
Multipart/form-data type of request, Content-type must be multipart/form-data.
The Multipart/form-data-encoded POST request format is completely different from the application/x-www-form-urlencoded, and Multipart/form-data needs to first set a delimiter on the HTTP request header. For example, ABCD:
Hc.setrequestproperty ("Content-type", "multipart/form-data; Boundary=abcd ");
Then, each field is delimited with a "--delimiter", and the last "--delimiter--" indicates the end. For example, to upload a title field "Today" and a file C:/1.txt,http body as follows:
--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
< This is the content of 1.txt files >
--abcd--
/r/n
Note that each row must end with/r/n, including the last line. If you use the Sniffer program to detect IE sent post requests, you can find that the IE separator is similar to the---------------------------7d4a6d158c9, which is a random number generated by IE, The intent is to prevent a delimiter in the upload file from causing the server to correctly identify the starting location of the file. We can write a fixed separator, as long as it's complex enough.
Post code for the sending file is as follows:
string[] Props = ...//Field name
String[] values = ...//field value
byte[] File = ...//file content
String boundary = "---------------------------7d4a6d158c9"; Separator
StringBuffer sb = new StringBuffer ();
Send each field:
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");
}
Send file:
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 ();
To set HTTP headers:
Hc.setrequestproperty ("Content-type", Multipart_form_data +); Boundary= "+
boundary);
Hc.setrequestproperty ("Content-length", string.valueof (Data.length + file.length
+ end_data.length));
Output:
Output = Hc.openoutputstream ();
Output.write (data);
Output.write (file);
Output.write (End_data);
Read server response:
Todo...
(4).
Keep Session with Cookies
Typically, the server uses session to track sessions. The simple implementation of the session is to use cookies. When the client first connects to the server, the server detects that the client does not have a corresponding cookie field and sends a Set-cookie field containing an identifier. During this session, the client sends a request that contains this cookie, so the server can recognize that the client has been connected to the server.
To achieve the same effect as the browser, the MIDP application must also recognize the cookie and include the cookie in each request header.
In the process of processing each connection, we check whether there is a Set-cookie header, and if so, the first session that the server sends
ID, or the server considers the session timeout, you need to regenerate one. If the Set-cookie header is detected, it is saved and appended to each subsequent request:
String session = NULL;
String cookie = Hc.getheaderfield ("Set-cookie");
if (cookie!=null) {
int n = cookie.indexof (';');
Session = cookie.substring (0, N);
}
Use the sniffer program to capture sessions sent by different Web servers. WebLogic Server 7.0 returns the following session:
Set-cookie:
jsessionid=cxp4fmwojb06xcbybwfwzbq0ifkroko2w7fzpklbmwsnerun5u2l!-1200402410;
path=/
The session returned by resin 2.1 is:
Set-cookie:jsessionid= Atmcmwe9f5j9; path=/
The session returned by IIS running asp.net:
SET-COOKIE:ASPSESSIONIDQATSASQB=GNGEEJIDMDFCMOOFLEAKDGGP; path=/
We do not need to care about the contents of the session ID, the server will recognize it itself. We simply append the session ID to the subsequent request:
if (session!=null)
Hc.setrequestproperty ("Cookie", session);
The method of maintaining the session for URL rewriting may be useful on PC clients, but this method is not recommended because the MIDP program is difficult to parse out useful session information in the URL.