网站导航免费论文 原创论文 论文搜索 原创论文 网学软件 学术大家 资料中心 会员中心 问题解答 原创论文 论文素材 设计下载 最新论文 下载排行 论文上传 在线投稿 联系我们
返回网学首页
网学联系
最新论文 推荐专题 热门论文 素材专题
当前位置: 网学 > 编程文档 > JSP > 正文
J2ME 101,第 4 部分: 通用连接框架
来源:Http://myeducs.cn 联系QQ:点击这里给我发消息 作者: 用户投稿 来源: 网络 发布时间: 12/11/29
下载{$ArticleTitle}原创论文样式

  本文是由两个教程和两篇补充文章组成的、共四部分的 J2ME 101系列的最后一部分。在这最后一部分中,我们将探索在 MIDP 上使用通用连接框架(GCF)的网络通信。

  与本系列中以前的内容一样,我们首先介绍基本内容(在这里,是如何打开到远程资源的连接),但是很快就转移到更复杂的领域中。我们的第一个 MIDlet 将展示如何下载并显示一个存储在服务器上的图像,第二个 MIDlet 将使用一个 HTTP 连接下载并显示服务器上的一个文本文件,最后一个 MIDlet 将使您可以将日期和时间参数传递给 Servlet。

  请注意本文假定读者熟悉 J2ME 环境中的 MIDlet 开发。需要在系统中安装 J2ME 开发环境才能编译代码示例。有关 J2ME 101系列前三部分的链接,以及 J2ME Wireless Toolkit(WTK) 的安装指导,请参阅 参考资料

  什么是 GCF?

  GCF 是一组在 Javax.microedition.io 包中定义的接口。图 1 显示了 GCF 的类层次结构。

  图 1. 通用连接框架的类层次结构

J2ME 101,第 4 部分: 通用连接框架

  在 GCF 中共定义了七个接口 ,其中 Connection 是根。注意同时提供了对数据包(packet)和流连接的支持。正如您设想的那样,沿着层次结构向下就会发现提供更多功能的接口。例如, StreamConnection 支持输入和输出流, ContentConnection 扩展了 StreamConnection 以支持对流的内容类型、数据长度和编码格式的确定。

  Connector 类用于在 GCF 中打开类型的连接。下面可以看到 Connector 类中的 open() 方法的格式:

  Connector.Open("protocol:address;parameters");

  连接协议支持

  GCF 在支持不同的连接协议方面特别灵活。在请求打开一个连接时, Connector 类使用其 Class.forName() 方法搜索实现了所请求的协议的类。如果找到这个类,就返回一个实现了 Connection 接口的对象,如 图 1 所示。

  在清单 1 中,可以看到打开不同连接类型的代码。

  清单 1. 打开不同连接类型

Connector.Open("Socket://www.corej2me.com.com:55");
Connector.Open("http://www.corej2me.com");
Connector.Open("datagram://www.corej2me.com:1000");
Connector.Open("file://makefile.txt");

  打开一个连接

  GCF 共有七种创建连接的方法。清单 2 显示了这七种方法。

  清单 2. 七种创建连接的方法

Connector (public class Connector)
  public static Connection open(String name)
  public static Connection open(String name)
  public static Connection open(String name, int mode, boolean timeouts)
  public static DataInputStream openDataInputStream(String name)
  public static DataOutputStream openDataOutputStream(String name)
  public static InputStream openInputStream(String name)
  public static OutputStream openOutputStream(String name)

  清单 3 展示了打开一个连接并从流中读取数据的一种方法。

  清单 3. 一种打开连接的方法

// Create a ContentConnection
String url = "http://www.corej2me.com"
ContentConnection connection = (ContentConnection) Connector.open(url);
// With the connection, open a stream
InputStream iStrm = connection.openInputStream();
// ContentConnection includes a length method
int length = (int) connection.getLength();
if (length != -1)
{
 byte imageData[] = new byte[length];
 // Read the data into an array
 iStrm.read(imageData);
}

  ContentConnection 类不是我们创建连接的惟一选择。我们还可以选择直接创建 InputStream 。在清单 4 中,可以看到如果需要通过网络连接下载一幅图像、并根据下载的内容创建一个 Image 时会是什么情况。

  清单 4. 直接创建一个输入流

InputStream iStrm = (InputStream) Connector.openInputStream(url);
Image img = null;
try
{
 ByteArrayOutputStream bStrm = new ByteArrayOutputStream();
 int ch;
 while ((ch = iStrm.read()) != -1)
  bStrm.write(ch);
 // Place into image array
 byte imageData[] = bStrm.toByteArray();
 // Create the image from the byte array
 img = Image.createImage(imageData, 0, imageData.length);
}
finally
{
 // Clean up
 if (iStrm != null)
  iStrm.close();
}

  正如您注意到的,不使用 ContentConnection 使我们没有办法确定收到的数据的长度。不过,这不算是大问题,因为我们可以用 ByteArrayOutputStream 读取数据并传送给目标数组。

  DownloadImage MIDlet

  让我们编写一个在清单 4 的基础上构建的小 MIDlet。DownloadImage MIDlet 将展示在一个 MIDP 应用程序中下载并显示一幅图像的步骤。这个 MIDlet 将使用一个 ByteArrayOutputStream 下载远程数据,然后用一个 ImageItem 在 Form 上显示得到的图像。

  看一下 DownloadImage MIDlet上的完整源代码,下面将我们将对它进行详细的讨论。

  运行了这个 MIDlet 运行后,在 WTK 设备模拟器上应该出现主用户界面,如下图所示。图 2 显示了一个提示输入图像 URL 的 TextBox 。单击 View命令会启动下载

  图 2. 一个显示输入 URL 提示的文本框

J2ME 101,第 4 部分: 通用连接框架

  图像接收完后,就会显示在设备中,如图 3 所示。

  图 3. 一个示例图像显示屏幕

J2ME 101,第 4 部分: 通用连接框架

  MIDP 中的 HTTP 支持

  现在您已经看到 GCF 是如何支持不同类型的连接的,并且开发了我们的第一个连网 MIDlet,现在可以更深入地分析 MIDP 中对 HTTP 的支持。我们将首先从一个更新过的层次结构图开始,它表明了哪个类提供了对 HTTP 连接的支持。

  图 4. 支持 HTTP 的 GCF 类

J2ME 101,第 4 部分: 通用连接框架

  原来的 MIDP 1.0 规范只要求设备支持 HTTP 连接协议,而更新的 MIDP 2.0 规范要求同时支持 HTTP 和 HTTPS,后者提供了对更安全的网络连接的支持。使用这些协议的 API 分别是 HttpConnection 和 HttpConnection 。除了这些强制性的协议,设备制造商可能会选择支持更多的通信协议,如数据包或者套接字。虽然有时会方便一些,但是您应当了解使用特定于厂商的协议会影响应用程序到其他设备的可移植性。

  请求和响应协议

  HTTP 和 HTTPS 都是请求/响应协议。客户机发送请求,而服务器发送响应。在继续后面的内容之前,我们将分析客户请求和服务器响应的各个方面。

  客户请求

  客户请求(client request),有时称为请求实体,由以下三个部分组成:

  请求方法

  头

  正文

  我们将详细讨论这三个部分。

  请求方法

  请求方法(request method)确定数据如何发送给远程资源。可以使用的三种方法是 GET、 POST 和 HEADER 。使用 GET 时,数据是作为 URL 的一部分发送的。使用 POST 时,所有客户机数据都是在与建立连接的请求不同的、单独的流中发送的。 HEADER 请求不向服务器发送任何数据。相反, HEADER 请求只是描述(meta)关于远程资源的信息。

  清单 5 显示了如何用特定请求方法 GET 和一个名为 size 值为 large 的参数打开一个 HTTP 连接。

  清单 5.用 GET 打开一个 HTTP 连接

String url = "http://www.corej2me.com?size=large";
HttpConnection http = null;
http = (HttpConnection) Connector.open(url);
http.setRequestMethod(HttpConnection.GET);

  头

  头字段(Header field) 使我们可以根据自己的需要从客户机向服务器传递参数。常见的字段有 If-Modified-Since 、 Accept 和 User Agent 。用 setRequestProperty() 方法将头字段置为键-值对。清单 6 显示了一个指定只有自 2003 年 11 月 11 日以后修改的数据才送回服务器的头。

  清单 6. 一个典型的请求头

String url = "http://www.corej2me.comsomefile.txt";
HttpConnection http = null;
http = (HttpConnection) Connector.open(url);
http.setRequestMethod(HttpConnection.GET);
// Set header field as key-value pair
http.setRequestProperty("If-Modified-Since", "Mon, 12 Jan 2004 12:00:00 GMT");

  正文

  正文(body)包含要从客户机发送给服务器的实际内容。例如,清单 7 显示了如何指定请求方法 POST 并通过流发送客户机数据。

  清单 7. 通过流发送数据

String url = "http://www.corej2me.com",
    tmp = "test data here";
OutputStream ostrm = null;
HttpConnection http = null;
http = (HttpConnection) Connector.open(url);
http.setRequestMethod(HttpConnection.POST);
// Send client body
ostrm = http.openOutputStream();
byte bytes[] = tmp.getBytes();
for(int i = 0; i < bytes.length; i++)
{
 os.write(bytes[i]);
}
os.flush();

  除了请求方法和头字段外,通常不需要指定任何其他信息。如果需要指定其他信息,那么就在正文中加入这些数据。

  服务器响应

  当服务器收到并处理了客户请求后,它必须打包并发送响应。与客户请求一样,服务器响应有三个部分:

  状态行

  头

  正文

  状态行

  顾名思义, 服务器状态行(server status line)通知客户机其请求的结果。HTTP 将状态行代码分为以下三大类:

  1xx是提供信息 。

  2xx是成功 。

  3xx是重定向 。

  4xx是客户机错误 。

  5xx是服务器错误。

  服务器状态行包括在服务器上运行的协议版本号、状态码和表示返回代码的文字消息。下面是有效的状态行的几个例子:

  “HTTP/1.1 200 OK”

  “HTTP/1.1 400 Bad Request”

  “HTTP/1.1 500 Internal Server Error”

  头

  与客户机不同,服务器可以通过头字段发送信息。清单 8 显示了三种最常用的、提取来自服务器的头信息的方法。

  清单 8. 三种常用的 getHeaderField 方法

String
    
    getHeaderField(int n) Get header field value looking up by index
String
    
    getHeaderField(String name) Get header field value
looking up by name
String
    
    getHeaderFieldKey(int n) Get header field key using index
   

  一个服务器有可能返回多个头字段。在这种情况下,第一个方法将通过指定结果集中的索引获得头字段的值,第二个方法通过用名字查询键来获得头字段的值。如果需要得到头字段键,可以向最后一个方法传递一个参数,这个参数表示在结果集中所感兴趣的项的索引。

  表 1 显示了当服务器响应头中包含内容“ content-type=text/plain”时,清单 8 中的每一种方法会返回什么内容。这个例子假定服务结果集只包含一项。

  表 1. 提取头字段内容

方法 返回值
http.getHeaderField(0) "text-plain"
http.getHeaderField("content-type") "text-plain"
http.getHeaderFieldKey(0) "content-type"

  正文

  与客户机一样,服务器在其响应的正文中发送大量信息。用类似于 清单 7中的那些代码(在那里客户机通过一个输出流发送其正文),客户机使用一个输入流读取服务器响应。

  HttpConnection API

  正如前面提到的,我们使用 HttpConnection API 在 MIDP 上建立连接。表 2 显示了在 HttpConnection 类中所有可用的方法。

  表 2. 所有 HttpConnection 方法

方法 说明
long getDate() 得到头字段日期
long getExpiration() 得到头字段失效时间
String getFile()> 从 URL 得到文件名
int getHeaderField(int n) 通过查询索引得到头字段值
String getHeaderField(String name) 通过查询名字得到头字段值
long getHeaderFieldDate(String name, long def) 得到作为 long 型的指定字段(表示日期)
int getHeaderFieldInt(String name, int def) 得到作为整数的指定字段
String getHeaderFieldKey(int n) 使用索引得到头字段键
String getHost() 根据 URL 得到主机
long getLastModified() 得到最后修改的字段值
String getPort() 从 URL 得到端口
String getProtocol() 从 URL 得到 协议
String getQuery() 得到查询字符串(只对 GET 请求有效)
String getRef() 得到 URL 的引用部分
String getRequestMethod() 得到请求方法的当前设置( GET 、 POST 或者 HEAD )
String getRequestProperty(String key) 得到一种请求属性的当前设置
int getResponseCode() 得到响应码(数字值)
String getResponseMessage() 得到响应消息(文字值)
String getURL() 得到整个 URL
void setRequestMethod(String method) 设置请求方法( GET 、 POST 或者 HEAD )
void setRequestProperty(String key, String value) 设置请求属性(头信息)

  FileViewer MIDlet

  我们的下一个 MIDlet 将使用一个 HttpConnection 下载并查看一个文本文件的内容。这个短的例子可以使您了解如何发送客户机信息并解释服务器响应的一些内部情况。这个 MIDlet 还包含对在 HttpConnection 类中不同方法的调用,这可帮助我们收集有关服务器主机、端口和返回的类型的信息。

  看一下 FileViewer MIDlet的完整源代码,我们将在下面详细讨论它。

  现在,让我们观察这个 MIDlet 的输出。图 5 左边的屏幕快照显示了开始时的快照。选择了 View 命令后,就会下载这个文件并显示在设备上,如图 5 右边的屏幕快照所示。

  图 5. 启动 FileViewer MIDlet

J2ME 101,第 4 部分: 通用连接框架

  图 6 显示了从服务器返回的字段,它们的值写到了控制台中。

J2ME 101,第 4 部分: 通用连接框架

  注意显示了状态行(消息和代码)和所有头字段 键-值 对。 HttpConnection 还包含在清单 9 中显示的方法以获得服务器信息,包括主机、端口和返回的内容类型。在图 6 中,这些用于 FileViewer MIDlet 的值显示在靠近底部的地方。

  清单 9. 获得服务器信息的方法

String
    
    getHost()
        
    
String
    
    getPort()
        
    
String
    
    getType()
  

  访问 Java servlet

  结束 J2ME 101系列之前,我们要开发最后一个 MIDlet。DateFormat MIDlet(请参阅完整的 源代码)将展示从一个 MIDlet 中访问 Java servlet 的必要步骤。与到目前为止学习的其他内容相比,这是一个相当复杂的操作,不过我们将在下面几节中完成它。

  我们首先向 servlet 传递参数,请求返回特定的日期和时间格式。这个 servlet 会根据参数创建正确编排格式的字符串并将结果发送回客户机,它会显示在 WTK 设备模拟器上。表 3 列出了可用参数及其含义,以及一个简单的例子。

  表 3. Servlet 参数

符号 意义 例子
G 公元标志AD
y 1996
M 07
d 10
h 分上下午的小时时间(1~12)12
H 全天中的小时时间(0~23)0
m 30
s 55
S 微秒978
E 一周中的日子Tuesday
D 一年中的日子189
F 一个月中的周日期2 (七月的第 2 个星期三)
w 一年中的周数27
W 月中的周数2
a am/pm 标记PM
k 一天中的小时 (1~24)24
K am/pm 中的小时(0~11)0
z 时区Pacific Standard Time
' 文本中的转义字符'at'
'' 显示单引号'

  图 7 和图 8 显示了两个正确编排了格式的参数和从 servlet 返回的结果。

  图 7. 编排字符串格式为 yyyy.MM.dd+'at'+hh:mm:ss+zzz" 的结果

J2ME 101,第 4 部分: 通用连接框架

  图 8. 编排字符串格式为 MMMM.dd.yyyy+'-'+hh:mm+aa" 的结果

J2ME 101,第 4 部分: 通用连接框架

  这个 servlet 位于 http://www.myCGIserver.com 上。这是位于奥地利的一个免费主机服务,所以如果日期格式与您的母语不相同时不要感到意外。例如,在图 7 中,我们通过指定时区参数“ zzz”所请求的时区显示为 CET,即中欧时间(Central European Time)。不过,如果我们将时区参数改为“ zzzz”,就会得到全文本版本的时区,输出如图 9 所示,这可能不是您所期望的。

  图 9. 编排字符串格式为 yyyy.MM.dd+'at'+hh:mm:ss+zzzz" 的结果

J2ME 101,第 4 部分: 通用连接框架

  调用 servlet

  对 servlet 的调用请求是在 commandAction() 方法中调用 cmRqst 命令时开始的,如清单 10 所示。

  清单 10. 事件处理

/*--------------------------------------------------
* Call the servlet
*-------------------------------------------------*/
public void commandAction(Command c, Displayable s)
{
 if (c == cmRqst)
 {
  try
  {
   serverMsg = null;
   callServlet();
   if (serverMsg != null)
    fmMain.append(serverMsg);
  }
  catch (Exception e)
  {
   System.err.println("Msg: " + e.toString());
  }
 }
 else if (c == cmExit)
  {
   destroyApp(false);
   notifyDestroyed();
  }
}

  callServlet 的代码如清单 11 所示。注意客户端的代码只包含设置请求方法为 GET 的内容。对这个 MIDlet 来说不需要头或者正文信息。在下面清单 11 中您将学到如何解释服务器响应。

  清单 11. 调用 servlet 并处理响应

/*--------------------------------------------------
* Call the servlet
*-------------------------------------------------*/
private void callServlet() throws IOException
{
 HttpConnection http = null;
 InputStream iStrm = null;
 boolean ret = false;
 // Examples - Data is passed at the end of url for GET
 String url = "http://www.mycGIServer.com/servlet/corej2me.DateFormatServlet?
        format=MMMM.dd.yyyy+'-'+hh:mm+aa";
 // String url = "http://www.mycgiserver.com/servlet/corej2me.DateFormatServlet?
 //        format=yyyy.MM.dd+'at'+hh:mm:ss+zzz";
 try
 {
  http = (HttpConnection) Connector.open(url);
  //----------------
  // Client Request
  //----------------
  // 1) Send request method
  http.setRequestMethod(HttpConnection.GET);
  // 2) Send header information - none
  // 3) Send body/data - data is at the end of URL
  //----------------
  // Server Response
  //----------------
  iStrm = http.openInputStream();
  // Three steps are processed in this method call
  ret = processServerResponse(http, iStrm);
 }
 finally
 {
  // Clean up
  if (iStrm != null)
   iStrm.close();
  if (http != null)
   http.close();
 }
 // Process request failed, show alert
 if (ret == false)
  showAlert(errorMsg);
}

  指定 URL 的两行代码显示了应如何从 MIDlet 中调用这个 DateFormatServlet servlet。只要观察 sevlet 是如何调用的,就可以知道我们要使用 GET 请求方法,因为所有参数都是作为 URL 的一部分传递,而不是在一个单独的流中发送的。

  在清单 12 中可以看到我们是如何从客户机向服务器传递名字为 format 的参数的。servlet 会提取这个参数的值以确定如何编排所请求的日期的格式。

  清单 12. 传递格式参数

http://www.mycgiserver.com/servlet/corej2me.DateFormatServlet?format=
MMMM.dd.yyyy+'-'+hh:mm+aa;
http://www.mycgiserver.com/servlet/corej2me.DateFormatServlet?format=
yyyy.MM.dd+'at'+hh:mm:ss+zzz;

  servlet 代码

  servlet 代码相当简单。我们所讨论的主要内容是在方法 doPost() 中。我们首先在变量 format 中存储客户机请求的格式。然后,调用 SimpleDateFormat 以创建所选择的日期格式对象,然后用一个调用创建一个新的 Date 对象。我们创建一个 PrintWriter 对象作为输出流,设置其内容类型为 text/html 并以所请求的格式输出日期,如清单 13 所示。

  清单 13. servlet 代码

package corej2me;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import java.util.Locale;
public class DateFormatServlet extends HttpServlet
{
   public void init(ServletConfig config) throws ServletException
   {
    super.init(config);
   }
   public void doGet(HttpServletRequest request, HttpServletResponse response)
     throws ServletException, IOException
   {
    doPost(request, response);
   }
   public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException
   {
    String format = request.getParameter("format");
    SimpleDateFormat simpleDate = new SimpleDateFormat(format);
    Date dt = new Date();
    PrintWriter out = response.getWriter();
    response.setContentType("text/html");
    out.println(simpleDate.format(dt));
    out.close();
   }
   public String getServletInfo()
   {
    return "DateFormatServlet";
   }
}

  解析 servlet 的结果

  解释服务器响应的代码如清单 14 所示。首先检查状态行以确定是否成功处理了客户机请求并返回一个结果( HTTP_OK )。因为没有头信息,所以我们直接开始从输入流中读取服务器数据,将结果存储在变量 serverMsg 中。

  清单 14. 解析 servlet 结果

/*--------------------------------------------------
* Process a response from a server
*-------------------------------------------------*/
private boolean processServerResponse(HttpConnection http, InputStream iStrm)
throws IOException
{
 //Reset error message
 errorMsg = null;
 // 1) Get status Line
 if (http.getResponseCode() == HttpConnection.HTTP_OK)
 {
  // 2) Get header information - none
  // 3) Get body (data)
  int length = (int) http.getLength();
  String str;
  if (length != -1)
  {
   byte servletData[] = new byte[length];
   iStrm.read(servletData);
   str = new String(servletData);
  }
  else // Length not available...
  {
   ByteArrayOutputStream bStrm = new ByteArrayOutputStream();
   int ch;
   while ((ch = iStrm.read()) != -1)
    bStrm.write(ch);
   str = new String(bStrm.toByteArray());
   bStrm.close();
  }
  // Save the server message
  serverMsg = str;
  return true;
 }
 else
  // Use message from the servlet
  errorMsg = new String( http.getResponseMessage());
 return false;
}

  下一步,我们需要回到 commandAction() 中的代码。在成功调用 callServlet() 后,我们将从服务器返回的、已经保存的字符串附加到 Form (我们的主用户界面组件)上,它会在设备上显示这个结果,如清单 15 所示。

  清单 15. 进一步分析 commandAction()

/*--------------------------------------------------
* Call the servlet
*-------------------------------------------------*/
public void commandAction(Command c, Displayable s)
{
 if (c == cmRqst)
 {
  try
  {
   serverMsg = null;
   callServlet();
   if (serverMsg != null)
    fmMain.append(serverMsg);
  }
 ...
}

  同样,这里是 DateFormat MIDlet的完整源代码。

  结束语

  在 J2ME 101系列的最后一篇文章中,我们有机会探索 MIDP 中支持的网络通信。我们首先介绍了通用连接框架,这是在 MIDP 中创建连网的应用程序的基础。然后讨论了 HTTP 支持,包括如何打包客户机请求,以及如何解释服务器响应。为了展示所介绍的概念,我们编写了一个 FileViewer MIDlet,它可下载并显示一个文本文件。

  在本文的最后一节,我们编写了一个访问 Java servlet 的完整 MIDlet。它包括范围广泛的网络通信操作,如向 servlet 传递参数、在服务器端解释并处理这些参数,以及最后在客户机上提取并显示服务器响应。

  在这个由四部分组成的系列中,我们有机会详细分析了 MIDP 的所有主要组件,即高层和低层的用户界面组件、持久性存储和网络支持。完成了这个全面的综述后,您现在已掌握了足以开始探索和开发自己的 J2ME 应用程序的必要信息!

(责任编辑:admin)

  • 上一篇资讯: Starting uggs from
  • 下一篇资讯: J2ME 记录管理存储
  • 网学推荐

    免费论文

    原创论文

    浏览:
    设为首页 | 加入收藏 | 论文首页 | 论文专题 | 设计下载 | 网学软件 | 论文模板 | 论文资源 | 程序设计 | 关于网学 | 站内搜索 | 网学留言 | 友情链接 | 资料中心
    版权所有 QQ:3710167 邮箱:3710167@qq.com 网学网 [Myeducs.cn] 您电脑的分辨率是 像素
    Copyright 2008-2015 myeducs.Cn www.myeducs.Cn All Rights Reserved
    湘ICP备09003080号