WAP建站入门

摘自http://blog.shouji56.com/Article/51.html

 

WAP的编程模型是以Web编程模型为基础的。因此在阅读本章之前,读者应该对Web编程模型有一定的了解。下面介绍WAP网络构架、如何搭建开发与测试环境和使用WAP的开发工具。在对WAP的概念有了初步了解之后,再来学习书写静态WML页面、使用服务器端脚本语言动态输出WML,在WML页面中显示中文和动态输出WBMP图像。

一、WAP整体简介

    WAP应用环境是一个通用的应用开发环境。它支持在不同无线承载网络上开发和运行WAP应用服务。这个应用环境的基础是现有的Internet技术。

l         WAP应用环境

    WAP体现为一种全面的和可扩展的协议,可应用于:

    • 任何具有支持WAP协议的移动设备,如各种手持无线设备。

    • 任何现有或正在设计中的无线服务,如SMSCSDUSSDGPRS

    • 任何移动网络标准,如CDMAGSMUMTS

    • 大部分现有的Internet应用服务。

    WAP应用环境是一个通用的应用开发环境。它支持在不同无线承载网络上开发和运行WAP应用服务。这个应用环境的基础是现有的Internet技术。整个环境如图2-1所示。


2-1 WAP应用环境

    WAP应用环境分为三个部分:WAP移动设备、WAP网关和信息服务器。

    • WAP移动设备:

    WAP移动设备就是指支持WAP协议的移动用户终端。在它内部装有微型浏览器,用户可以采用简单的选择键来实现服务请求,并以无线方式发送和接收所需要的信息。WAP移动设备显示按照WMLWireless Markup Language,无线标记语言)格式化后的各种文字图像数据。一个典型的WAP移动设备就是WAP手机。与普通手机相比,WAP手机除了内置的微型浏览器以外,还有内置的Modem。用户是通过拨号的方式连接到WAP网关的。这个过程与使用个人电脑和Modem连接到Internet没有什么大的区别。因此在使用WAP手机的过程中,设定拨号号码和网关的IP地址是不可缺少的。

    • WAP网关:

    它主要完成两个功能:实现WAP协议与Internet协议之间的转换;WML内容编码和解码。通过将WAP用户的请求转换为HTTP请求完成请求代理过程,通过对返回的内容进行编码压缩来减少网络数据的流量。既然WAP手机使用的是拨号连接网关,那么也就意味着不一定要连接到移动提供商的网关上才可以连接到Internet。对于有条件的个人、公司或企业完全可以建立自己内部的WAP网络。

    • 信息服务器:

    信息服务器为客户提供基于WAP的各种服务。现在Internet上的很多应用已经成功地移植到WAP上。例如:股票交易、天气预报、车船时刻表等。一些专业应用也正在向WAP转移。例如:SMTP服务、POP3服务、Telnet服务、FTP服务等。
    WAP
应用环境在于为应用开发人员和营运商提供了在不同类型的网络、数据、终端上进行服务的新途径。WAP标准在设计时将应用要素与使用的传输数据类型独立开来,使一些应用的承载网络的转移(如从SMSCSD转为GPRS)成为可能。

 

l         WAPInternet的对比


2-2 各个层次之间的对比


2-3 各个层次之间的相互关系

2-2所示左边是Internet构架的各个层次,右边是WAP协议的各个层次。层叠排列的WAP协议体系为无线装置的应用开发提供了可扩展、可延伸的环境。每一层协议或其他服务和应用程序可与它下一层协议直接对话。通过精心设置的一系列接口,外围服务和应用程序可以利用WAP体系提供的各种功能,包括直接使用会话层、交易层、安全层、传输层等。

    • Wireless Application EnvironmentWAE)无线应用环境。

    WAE是建立在移动技术与WWW结合基础之上的应用环境,目的是为营运商、服务提供商的服务和应用程序建立一个交互操作环境,使他们可以灵活地接入不同平台。WAE包含一个微型浏览器,具有以下功能:
    ·
无线标记语言WML:一种轻型标记语言,类似HTML,专为手持终端做了优化。
    · WMLScript
:轻型的脚本语言,类似于JavaScript
    ·
无线电话应用:电话服务及其编程接口。
    ·
内容格式:精心设计的数据格式,包括图像、电话号码簿和日历信息。

    • Wireless Session LayerWSP)无线会话层。

    WSPWAP应用层提供两个会话服务的统一接口:一个是在传输层协议WTP之上的面向连接的服务,另一个是在安全或非安全数据报服务(WDP)之上的非连接服务。WSP系列协议针对低功率、长等待时间的网络数据载体进行了优化,它现在由浏览应用的服务组成(WSP/Browsing,WSP/B允许使用WAP Proxy连接WSP/B的客户端和标准的HTTP服务器,具有以下功能:
    ·
无线编码中的HTTP/1.1功能和语法。
    ·
较长时间的会话状态。
    ·
会话随着会话者移动而暂停或继续。
    ·
建立一个传送可靠或不可靠的数据的通用设备。
    ·
协议的协商。

    • Wireless Transaction ProtocolWTP)无线传输协议。

    WTP运行于数据服务之上,提供了一个适用于轻体客户(移动终端)的面向传输的轻型协议。WTP可有效地运行于安全或非安全的无线数据网络,具有以下功能:
    ·
三个等级的传输服务;
    ·
不可靠的单向请求。
    ·
可靠的单向请求。
    ·
可靠的双向请求-回答传输。
    ·
可选择的用户-用户连接,WTP用户自行确认每次收到的消息。
    ·
可选择的带外数据确认。
    · PDU
连接和延时确认,以减少传送的消息数量。
    ·
异步传输。

    • Wireless Transport Layer SecurityWTLS)无线传输安全层。

    WTLS是基于工业标准——TLS(以前称为SSL)上的安全协议,同样针对移动通信使用的窄频信道进行了优化,它应该与WAP传输协议同时使用。应用程序可视自己的安全要求和网络特点,选择启用或不启用WTLS功能。WTLS的功能如下:
    ·
数据的完整性:WTLS具有保证终端与服务器间传送的数据前后一致且不会损毁。
    ·
传输的保密性:WTLS保证端到端数据的保密性,并可为数据传输过程的中介方读取。
    ·
认证:终端至服务器的校验。
    · “
拒绝服务保护:检验和拒绝重复和未正确识别的数据,以保护上层协议。
   
此外,WTLS也可用于终端和终端之间的安全通信,如为交换电子交易卡提供认证。

    • Wireless Datagram ProtocolWDP)无线数据报协议

    WDP可运行于各种网络的数据载体。作为一种通用的传输协议,它将传输端口根据底层数据载体进行改造,从而为WAP体系中的上层协议提供统一的接口,使会话层、应用层独立于底层的无线网络。如能保持传输层接口和基本功能的一致性,就可通过中介网关使广泛的交互操作得以实现。

    • Bearer 数据载体

    WAP协议在设计时的目的是使它可独立运作于各种不同的数据载体之上,如SMS(短消息、CSD、封包数据等)。由于数据载体因承载量、容错率和延迟的不同而有不同的质量,WAP协议就需要补偿或容忍这些特点。WDP集中处理体系中其他层次协议与数据载体的交流,除现在已经可以支持的数据类型外,随着新的数据服务在移动市场出现,它也会不断发展以支持更多的数据类型。

    • Other Services and Applications 其他服务与应用

    通过一系列精心设置的接口,WAP协议还可以支持其他服务和应用程序使用WAP提供的功能。外围应用程序可直接切入会话层、交易层、安全层和传输层,虽然这类有价值的应用未在WAP标准中界定。WAP协议可用来开发诸如电子邮件、日历、电话号码本、手写板、电子商务和黄页、白页等各类服务等。

    • Wireless Telephony Application 无线电话应用。

    WAP标准还定义了一个名为WTA的协议。它是一个面向通信的技术,使WAP能够融合电信网络中先进服务,比如智能网络(Intelligent Networks)。在融合基于浏览器的WAP用户界面,WTA能够不用修改终端就直接享受基于智能网络的服务。

    虽然WAP的整个结构层次比较复杂,但是由于底层的大部分工作都是由电信部门和移动提供商来完成的,因此总的来说只需要关心应用层的开发工作。在进行WAP开发时,各个部分的协议与Internet上有着一定的对应关系(如图2-3所示)。这使得可以利用现有的Internet服务器也能实现WAP相关服务。

 

l         服务途径


2-4 服务途径

    有以下几个途径来实现WAP服务(如图2-4所示):

    • Web服务器传送原有的HTML网页,由HTML过滤器转换成WML格式,再由WAP 代理服务器处理后形成二进制的WML数据流送往客户端。当然,WAP代理服务器可以集成HTML过滤器的功能。

    • Web服务器直接将WML网页传送到WAP代理服务器,由服务器处理后形成二进制的WML数据流送往客户端。

    • 对于前面的情况,WAP代理服务器是在移动提供商一方。其实WAP代理服务器也可以在内容提供商一方。内容提供方式还是和上面所讲的方式相同。

    • WTA 服务器直接将二进制的WML数据流送往客户端。这种方式主要是用于提供电话呼叫服务。

    对于内容服务商来说,主要是考虑内容的提供方式。第一种方式的优势是不需要对原有的网络及其网页进行过多的修改。HTMLWML的工作由HTML过滤器直接完成。但是缺点也是很明显的。例如:

    • 现有的过滤器只能做有限的转换,复杂的HTML网页就很难满意地完成。

    • 由于HTML网页比WML网页复杂,因此传输的效率不是很高。

    • 增加中间环节,降低了反应速度和工作效率。

    采取第二种方法就需要编写专门的WML页面。这并不意味因为需要编写专门的WAP服务程序,就必须将所有服务程序改写。完全可以利用以前的Web服务程序,做适当的修改就可以达到目的。
对于内容服务商来说,在选择WAP的时候经常会遇到的两个问题就是:

    • 需要特殊的WAP服务器吗?

    • 需要特殊的WAP代理服务器吗?

    回答是:既不需要特殊的WAP服务器,也不需要特殊的WAP代理服务器。其实可以通过配置Web服务器来实现WAP服务,因此这个时候可以将Web服务器称为“WAP服务器。如果没有特殊的要求,那么完全可以使用由移动商提供的WAP代理服务器。若想实现企业内部的 WAP网络或者考虑到安全问题的时候,就需要考虑采用独立的WAP代理服务器。采用独立的WAP代理服务器将会给用户带来麻烦。因为他们不得不频繁地更改手机上的设置,来进行服务切换。本质上来说,WAP设备只要接收到可以被解释的二进制WML数据流,那么无须考虑这个数据流的提供者到底是谁。

 

l         网络应用构架


2-5 网络应用构架

    2-5是一个抽象的WAP网络应用构架。WAP网络应用构架还是沿袭传统的服务器客户端模式,只是中间增加了一个网关。
   
客户端一般都是WAP移动设备,很可能是一部WAP手机。它们由不同的公司生产,具有各自的特点,对WMLWMLScript的支持情况也千差万别。现在已经上市的WAP手机有Nokia 7110Ericsson R380Motorola L2000www。以后随着更多厂家的加入,将有更多的WAP手机出现。一般来说手机的屏幕希望足够的大,分辨率足够高,否则浏览网页也将是十分困难的。由于屏幕大小的不一样,对WMLWMLScript的支持情况大不一样(就如同Internet ExploreNetscapeHTML标记的支持也是不一样的),在某种意义上需要针对不同的手机制作不同的网页,使得客户能够舒适地浏览页面。另外,WAP手机上网存在的安全隐患也需要加以考虑:散射在空间中的电波也会将用户的口令、密码等等敏感信息散射在空间中。
    WAP
网关就是在前面所提到的WAP代理服务器。它负责HTTP协议与WAP协议之间的互相转换,其主要功能有两个:

    • 将取得的WML页面编译压缩成二进制WML流并发送给客户端。

    • 将从客户端过来的二进制请求翻译成HTTP请求并执行该请求。

    网关在整个构架中有着十分关键的作用,是连接客户和服务的桥梁。它可以在移动提供商一方(如图2-6所示)或者在WAP服务提供商一方(如图2-7所示)与应用服务器集成在一起。这两种方式都要根据具体的情况加以采用和实施。


2-6 WAP网关在移动提供商一方


2-7 WAP网关在服务提供商一方

在服务端开发应用程序和以前的Web服务开发没有太大的区别。只要符合CGICommon Gateway Interface)标准就可以,无论是用ASPJSPPHPPerlISAPINSAPICC++BasicDelphi或者Servlet都是一样的。因此这给开发人员以很大的自由。其中的具体细节将在后面的章节中讲解。

二、测试环境

    WAP开发中的一个重要环节就是搭建测试环境。对于WAP初学者来说,了解测试环境是十分必要的。WAP的测试环境可以分为三种:浏览环境、模拟环境和实际环境。以下就分别介绍这三种测试环境。

l         浏览环境

    建立浏览环境十分简单,网上提供很多免费下载的WML浏览器。例如:WinWAP就是一个在Windows操作系统下运行的WML浏览器。只要输入提供WML内容服务的URL地址,就可以浏览WML页面。操作过程和Internet Explore十分像,界面如图2-8所示。


2-8 WinWAP 2.2的界面

    这种方式的优点是实施简单迅速,操作简单易学。但是由于是Windows下的浏览器,支持大部分的WML标记,窗口界面可以扩大和缩小,因此所看到的情况和手机上的模拟差别很大。另外也不提供编辑、编译和调试集成环境。

 

l         模拟环境

    模拟环境是通过使用由移动电话公司所提供的WAP手机模拟器来实现WML浏览。在网上能免费下载的模拟器有Nokia WAP ToolkitEricsson R380 Emulator(图2-9)、Ericsson WapIDEUpPhone UP SimulatorMotorola Mobile ADK等。其中NokiaMotorolaEricsson提供了比较完整的集成开发环境。


2-9 EPOC Emulator

    这种方式提供了集成环境和WAP手机基本一致的模拟器。但是很难说所使用的模拟器和其产品完全一致的,并且没有网关的参与(有的模拟器可以设置网关),因此这是一个不完备的检测,特别是与WTAWireless Telephony Application)相关的服务没有办法进行检测。对于开发阶段来说,这样的环境就已经足够。

l         实际环境

    根据网关所在的位置不同,实际环境的建立分为三种:

    • 使用移动营运商的拨号和网关。

    • 使用公共网关(在国际互连网上,有一些这样的公共网关)。

    • 使用自己设立的网关(通过拨号上网,使用简单的拨号接入软件和免费的网关来实现)。

    但是相比之下,使用移动营运商提供的WAP网关,费用要比其他两种方式要少。例如:移动营运商规定WAP上网费用是0.2/分钟。如果采用直接拨号到自己设立的网关,那么至少要交纳市话费用0.5/分钟。除非有特殊的要求(例如:建立企业内部的WAP网络或者考虑到安全问题),否则还是捡便宜的用。
   
在国内根据所使用的SIM卡不同可以分为:联通移动(原中国电信的一部分)。由于国内各个省市的电信部门各自布置有关的WAP设备,因此即使是属于一家,所采用的WAP设备也不尽相同。
   
实际环境的建立和以上的情况很相似。购买一些主流的WAP手机,开通WAP服务。在Internet上有一台Web服务器可供测试使用。将WAP手机连接到现有的网关,就可以真正地享受WAP服务了。当然,目前每一次测试可能要交纳一定的费用。
   
由于国内的WAP网络还处在实验阶段,并没有真正地推向市场。因此网络的稳定性比较难保证。在开发阶段,如果能有移动营运商的技术合作那是最好的。这样一旦有问题可以随时询问。

三、书写你的第一个WML页面

  • 这里假设读者已经拥有了一台电脑,并且已经安装了Windows NT Server 4.0版本(对于个人来说一般选择独立域服务器安装),通过电话线能够接入到Internet。在这个基础上来讲述如何搭建WAP平台。

  • 首先要给Windows NT Server打上Server Patch 4.0。一般在网络上可以找到Server Pack 4.0版本。然后安装Option Pack 4.0,里面有Internet Information Server 4.0(以下简称为IIS)。如果拥有Visual Studio 6.0企业版,这个的问题就很简单了:直接安装Back Office就可以解决问题。Back Office里面含有很多服务组件。其中就有Internet Information ServerFrontPage Server Extension。安装完成之后,现在的电脑就是一台为开发而使用的服务器。

  • 翻到本书后面的WAP资源一章,找到下载WinWAP 2.2的网址。从网上下载一个WinWap 2.2,将它安装在电脑上。在完成WML浏览器安装后,试着启动它。如果一切正常,它将直接通过Internet连接到提供商的WML主页上。在菜单里可以找到 “View Source”(查看源文件)这个选项。通过阅读别人的WML页面进行学习,是一种很好的学习方式。

  • 使用记事本(或者读者喜欢的文本编辑器)输入一个最小的WML页面,如图2-10所示。输入的时候注意,第一行要顶着开头写,不要留下空白或者空行。否则,在其他模拟器浏览下就会出现问题。WML对页面书写的要求比较严格,因此最好按照要求来做,否则后面很麻烦。

  1. <?xml version="1.0"?>
  2. <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
  3. "http://www.wapforum.org/DTD/wml_1.1.xml">
  4. <wml>
  5. <card id="main" title=”First WML”>
  6. <p>
  7. Hello World !
  8. </p>
  9. </card>
  10. </wml>

2-10 输入Hello.wml
写好后将它保存为hello.wml。注意:如果使用记事本,那么它将会无视输入的文件扩展名,而直接以txt作为文件的扩展名。因此还必须手工修改文件的扩展名。

 

  • 启动IIS服务器管理(图2-11),展开服务器下的子目录(如果无法展开,可能是WWW服务没有启动。从服务中启动WWW服务,或者直接在IIS Console中启动服务)。

    2-11 IIS服务器管理

  • 找到默认的Web站点,用鼠标右键点击(图2-12)。在弹出菜单中找到“New”(可能由于中文或者英文版本而略有不同),再在子菜单里找到虚拟目录


2-12 建立虚拟目录

  • 点击它,就会弹出一个对话框。第一页是输入别名(图2-13),例如:wap。第二页是确定hello.wml所在的子目录(如图2-14)。第三页是确定访问的权限(图2-15),可以先跳过这一页。现在可以点击“Finish”,虚拟目录就建设完成了。


2-13 输入虚拟目录的名称

2-14 指定虚拟目录所对应的目录

2-15 确定访问权限

  • 默认的Web站点下就会多出一个“wap”目录。用鼠标右键点击它,在弹出菜单中选择属性“Properties”,就会弹出一个对话框(图2-16),里面有好多配置选项。选中“HTTP标题,在下面有MIME映射类型“MIME Map”,点一下文件类型按钮,就会弹出一个配置菜单。点新增加类型,在对话框(图2-17)的第一行输入:“wml”,第二行输入:“text/vnd.wap.wml”,然后按确认按钮。在对话框中就多了一个:“.wml text/vnd.wap.wml”。好了,确定退出整个IIS Console。以后所有与WAP相关的文件映射都是从这里输入的。


2-16 虚拟目录的属性对话框

2-17 增加文件映射类型

  • 在确保WWW服务已经启动的情况下,打开WinWAP,马上Stop它。在URL栏里输入:http://127.0.0.1/wap/hello.wml,并按下回车。等着欣赏第一个WML页面(图2-18)。(注:127.0.0.1是本机缺省的IP地址,并不需要对计算机进行特殊配置)


2-18 第一个WML页面

    如果顺利地看到了“Hello World !”说明已经成功地走出了第一步。下面就要对WML做一个初步的讲解。在讲解之前,先来认识一下模拟器。本书后面的程序都将以Nokia WAP Toolkit 1.2为主要的测试工具。

 

四、简单地介绍Nokia WAP Toolkit的安装与使用

l         整体介绍

    NokiaWAP工具包有图形开发环境,浏览器和WML/WMLScript编译器。Nokia工具包目前只能在Windows NT 4.0上运行。要注意的是Nokia WAP Toolkit需要首先运行Java 2 runtime才能使用,所以需要先到SunJava站点(java.sun.com)去下载一个Java 2 SDK或者Java 2 Runtime EnvironmentJRE)才可以安装使用Nokia WAP Toolkit
   
安装完之后,可以看到WAP工具包程序组中包括一个工具包IDE,一个很好的关于WAPWMLWMLScript的详细文档以及工具包本身。该工具包可以在同一个应用环境下进行编辑、修改和测试WML/WMLScript文件等工作。
   
用户可以通过直接装载WAP应用的URLs或通过WAP网关进行浏览网页。Nokia还出售一个独立的基于WAP ServerJava Servlet。该产品将应用服务器功能和WAP网关功能集成到一个产品中。总体来说,Nokia WAP Toolkit相对显得较成熟、实用。

l         安装指南

    在从SunJava站点下载了Java运行环境以后就可以开始安装Java运行环境:先安装jdk1_2_2-001-win.exe,然后安装jre1_2_2-001-win-i.exe,最后安装hostpot_1_0_1-win.exe
   
安装Nokia WAP Toolkit:在安装了Java运行环境之后,直接运行Setupex12.exe。如果还没有安装Java运行环境或者安装了不合适的Java版本,安装程序都会给出提示并自动退出程序。

l         启动

    开始菜单栏中找到“Nokia WAP Toolkit”,选择“Toolkit”Nokia WAP Toolkit就启动了。由于Nokia WAP Toolkit是使用Java编写的,因此启动的时候过程比较缓慢。在启动以后,就自动处于如图2-19所示状态:


2-19 Nokia WAP Toolkit

    左边是集成开发环境,右边是6150界面的模拟器。在启动Nokia WAP Toolkit之后,它自动调用一个工具包自带的初始化页面,并将其文本内容显示在“WML Deck”一栏中。

l         功能介绍

    总的来说Nokia WAP Toolkit的使用并不是很复杂。只要懂得WAP中的一些概念,学习起来还是很容易的。其他模拟器的基本功能都十分相似,在本书中不再做详细介绍。

    • File

    主要是关于文件操作的功能。“New”用来生成一个新文件。注意到Nokia WAP Toolkit支持WMLWMLScriptWBMP文件的编辑。“Open”是用来打开文件,“Save”用来保存当前文件,“Save As”是将文件另外保存。

    • Edit

    主要是和编辑相关的功能。这和一般的写字板的编辑功能没有太大的区别。需要注意的是:无法粘贴其他Windows窗口的文本或者拷贝当前的文本到其他Windows窗口。

    • Go

    “Back”返回到上一个页面。“Home”返回到初始页面。“Refresh Card”刷新装入卡片。“Reload Deck”重新装入页面。“Stop Loading Deck”停止装入页面。在装入的过程中,用户可以随时停止装入。“Load Location”给模拟器指定一个URL地址。如果联合Web服务器做测试,那么这将是一个经常使用的功能。“Load File”从文件装入页面。

    • Toolkit

    “Toolkit”中主要是关于开发环境的一些设置选项。“Show”中的选择项控制着在界面上需要显示哪些栏目。其中“Message”是关于浏览器的记录;“Variables”是关于当前页面中的变量记录;“History”是关于浏览器的历史记录;“Book Mark”主要记录了书签地址;“WML Deck”是当前WML页面的文本内容;“Session”显示了浏览器与服务器之间的交互状况。
    “Clear Bytecode Cache”
是一个非常有用的功能。如果不清除Cache,那么模拟器会自动调用上次的页面。特别是做调试的时候,如果对页面的错误已经做了修改,但是忘记了清除Cache,那么在使用模拟器浏览的时候,它仍然将使用上次保存在Cache中的错误页面。所以每次修改之后,不要忘了选择这个功能。实际上,WAP手机中也有这个功能。如果使用手机测试的时候忘记了这一点,那么将永远找不到正确答案。有关Cache的问题,在后面将有详细的讨论。
    “Add Bookmark”
增加一个书签。如果在输入URL地址的时候觉得比较麻烦,就可以使用这个功能。在Nokia 7110中,也有同样的书签功能。
“Preference”
是配置一些比较高级的选项。在“Communication”中,可以设置模拟器是通过HTTP直接访问WML内容服务器,还是通过网关访问。当通过网关的时候,需要对网关进行详细的配置。“Encoding”是选择编码方式。在使用快速编码的时候很容易出现编译错误,可以把这项取消。在“General”中是其他一些配置,一般不影响模拟过程。

    • Help

    主要是和版本、帮助文档相关的功能。Nokia WAP Toolkit有比较完整的文档,可以从中得到很多帮助。如果不嫌PDF和英文麻烦的话,阅读这些帮助对读者来说应该不是一件很难的事情。

 

五、初识WML

   通过一个简单的WML页面来了解WML的基本情况

 
请大家先看第一个例子:

  1. <?xml version="1.0"?>
  2. <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
  3. <wml>
  4. <template>
  5.     <do type="prev" label="back">
  6.         <prev/>
  7.         <!--provide a button you can clink to back a step-->
  8.     </do>
  9. </template>
  10. <card id="friends" title="Hot link">
  11.     <p>
  12.         <a href="http://wap.yestock.com">Yestock</a><br/>
  13.         <a href="#nextcard">Next Card</a>
  14.     </p>
  15. </card>
  16. <card id="nextcard">
  17.     <p>
  18.         this is the second card.
  19.     </p>
  20. </card>
  21. </wml>

    通过以上示例大家可以了解到以下内容:

  • 语法

WML的语法与HTML相似,仍然是一种标记语言,而且延续了XML语法规则。

  • 标签

XMLWML语言中,语言的基本元素称之为标签

    • 标签必须被“<” “>”括起来。

    • 大多数标签都包括”“两部分。例如:<p>…</p>

    • 某些特殊标签可以只有一个标签,但是必须有结束标记“/”。例如:<prev/>

  • 属性

XML语言的标签可以包含很多属性,给标签提供必要的附加信息。

    • 属性内容通常在起始标签内使用。

    • 属性只作为参数为标签提供必要的信息,不会被浏览器显示。

    • 属性的值需要被引号括起来(不能省略,这点与HTML不相同),可以是单引号或者双引号,引号可以成对嵌套使用。

例如:
<card id="friends" title="Hot link">
id
title就是属性,而“friends”“Hot link”就是属性值。

  • 注释

注释内容是方便制作者阅读源代码,不会被浏览器显示。WML不支持注释嵌套。当注释中需要使用关键字的时候,例如:“<”“>”。需要使用CDATA来躲避语法检查。CDATA内的数据内容都会被当作文本来处理,从而避开语法检查,直接作为文本显示。看下面的例子:

  1. <?xml version="1.0"?>
  2. <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
  3. <wml>
  4. <card id="main" title="CDATA Test">
  5.     <p>
  6.         Result:
  7.         <![CDATA[<b>this is a test !</b>]]>
  8.     </p>
  9. </card>
  10. </wml>

Nokia WAP Toolkit 1.2的测试结果如图2-20所示:


2-20 CDATA测试结果

  • 文档结构

    WML文档是由卡片(card)和页面(deck)构成的,一个页面是一个或多个卡片的集合。在这里做个比方:页面就好比是手中的一副扑克牌,而每一张牌就是卡片。那么在桥牌的每一局中,虽然手中的卡片会有所不同,但是将这些卡片统称为页面。
   
在得到客户终端的请求之后,WML服务器通过网络把页面发送到客户的浏览器(有时候被称作用户代理)。访问者可以浏览页面内包含的所有卡片,而不必从网上单独下载每一个卡片。程序中的第一个卡片是缺省可见的卡片。
   
如果WAP设备接收到上面例子的程序(需要提醒的是:手机接收到的内容是已经经过WAP网关编码的二进制WML,而不是文本形式。这点是与HTML不同的地方。),那么它将首先显示第一个卡片,也就是“friends”卡片。
UpPhone的模拟器上来看应该是图2-21和图2-22的样子:


2-21 第一张卡片


2-22 第二张卡片

Nokia WAP Toolkit 1.2模拟器上来看应该是图2-23和图2-24的样子:


2-23 第一张卡片


2-24 第二张卡片

  • 大小写敏感

无论是标签元素还是属性内容都是大小写敏感的,这一点继承了XML的严格特性,任何大小写错误都可能导致访问错误,这是WML设计开发者必须注意的问题。

  • 定义变量

    WML可以使用变量供浏览器和Script使用,通过在页面中的一个卡片上设置变量,其他卡片不必重新设置就可以直接调用。
   
变量的语法如下:
        $identifier
        $(identifier)
        $(identifier:conversion)
   
如果变量内容包含空格就需要用圆括号括起来。由于变量在语法中有最高的优先级,包含变量声明字符的字符串会被当作变量对待,所以如果要显示“$”,就一定要连续使用两个“$”。看下面显示美元符号的例子。

  1. <?xml version="1.0"?>
  2. <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" http://www.wapforum.org/DTD/wml_1.1.xml">
  3. <wml>
  4. <card id="main" title="Dollar Test">
  5.     <p>
  6.         Your account has $$15.00 in it.
  7.     </p>
  8. </card>
  9. </wml>

 

Nokia WAP Toolkit 1.2的测试结果如图2-25所示:


2-25 显示美元符号

XML是一种语法非常严格的语言,WML也继承了这种规则,任何不规范语法都会导致错误。

 

六、动态输出WML页面

使用多种服务器端脚本语言动态输出WML页面

   以下将要讲述的是使用各种服务器端脚本语言编写动态输出WML页面。这里主要以几种最常见服务器端编程方式为例子。其他的服务器端编程方式都可以依此类推。
   
无论使用哪种服务器端编程方式来动态输出WML页面,其关键就是输出Content-typetext/vnd.wap.wml。然后按照WML的语法格式输出WML内容就可以达到目的。当然其中针对不同的编程方式还有一些细节问题。

使用ASP动态输出WML页面

    • 使用记事本输入下面的ASP程序(图2-26):

  1. <%@ Language=VBScript %>
  2. <% Response.ContentType="text/vnd.wap.wml" %><?xml version="1.0"?>
  3. <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
  4. <wml>
  5.     <card id="main" title="ASP WML">
  6.         <p>
  7.             Hello
  8.         </p>
  9.     </card>
  10. </wml>

2-26 Hello.asp

    • 保存为hello.asp。并且是它和hello.wml在同一个子目录下。注意:记事本在保存的时候只识别txt文件扩展名,因此在保存之后应该手工修改文件扩展名。为了方便起见,可以使用其他的文本编辑工具,或者是使用专门的ASP编辑工具(例如:Viusal InterDev)。

    • 启动IIS服务器管理。在默认的Web站点下找到“wap”目录,用鼠标右键点击它,选择属性“Properties”,就会弹出一个对话框,里面有好多配置选项。 在虚拟目录标签页当中有一项应用程序设置。将许可一项设置为:执行(包括脚本)。确定后,退出IIS Console

    • 打开WinWAP,马上Stop它,在URL栏里输入:http://127.0.0.1/wap/hello.asp,并按下回车。等着欣赏第一个ASP页面(图2-27)。


2-25 WinWAP测试结果显示

    • 如果读者熟悉ASP,读者也许会注意到:为什么在<?xml version="1.0"?>并没有换行写。注意,这是必须的!否则,程序在WinWAP下可能没有任何问题,但是在其他模拟器上,可能就会导致编译错误!因为在<?xml version="1.0"?>前面不允许有多余的空格或者换行。

CGI编程时应该注意的问题

    使用CGI程序生成动态WML页面和使用CGI编程生成动态HTML的方式基本一样。只是在返回WML页面前,先要输出Content-Typetext/vnd.wap.wml,然后再输出WML内容。
   
输出的返回格式:"Content-type: text/vnd.wap.wml\n\n"

使用标准C动态输出WML页面

    这里有一个非常简单的标准C例子,该例子适合于WindowsUnix

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. #include <string.h>
  5. void GenerateCGIHeader();
  6. void GenerateCGIFooter();
  7. /*--------------------------------------------------------------*/
  8. int main(int argc, char* argv[])
  9. {
  10.     char tmpBuf[128];
  11.     char strGET[]="GET";
  12.     if (getenv("REQUEST_METHOD") == NULL)
  13.     {
  14.         printf("This script is not intended to run from shell.\n");
  15.         return -1;
  16.     }
  17.     // Check that the request method is GET (see CGI specs)
  18.     strcpy(tmpBuf, getenv("REQUEST_METHOD"));
  19.     if (strcmp(tmpBuf, strGET) != 0)
  20.     {
  21.         printf("This script requires use of GET-method.\n");
  22.         return -1;
  23.     }
  24.     /* Create a header for our reply */
  25.     GenerateCGIHeader();
  26.     /* Display operating system-style date and time. */
  27.     _strtime( tmpBuf );
  28.     printf( "OS time:%s\n", tmpBuf );
  29.     _strdate( tmpBuf );
  30.     printf( "OS date:%s\n", tmpBuf );
  31.     /* Create a footer for our reply */
  32.     GenerateCGIFooter();
  33.     return 0;
  34. }
  35. /*--------------------------------------------------------------*/
  36. void GenerateCGIHeader()
  37. {
  38.     /* Generate header for CGI response */
  39.     printf("Status: 200\n");
  40.     printf("Content-type: text/vnd.wap.wml\n\n");
  41.     printf("<?xml version=\"1.0\"?>\n");
  42.     printf("<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN");
  43.     printf("\" \"http://www.wapforum.org/DTD/wml_1.1.xml\">\n");
  44.     printf("<wml>\n");
  45.     printf(" <template>\n");
  46.     printf(" <do type=\"prev\" label=\"Back\">\n");
  47.     printf(" <prev/>\n");
  48.     printf(" </do>\n");
  49.     printf(" </template>\n");
  50.     printf(" <card id=\"datecard\" title=\"CGIDate\">\n");
  51.     printf(" <p>\n");
  52. }
  53. /*--------------------------------------------------------------*/
  54. void GenerateCGIFooter()
  55. {
  56.     /* Generate footer for CGI response */
  57.     printf(" </p>\n");
  58.     printf(" </card>\n");
  59.     printf("</wml>\n");
  60. }

    Windows环境下,使用Viusal C&C++ 6.0创建一个Windows Console Application——WapCCGI。然后将该文件加入到项目中,编译生成WapCCGI.exe。将该可执行文件拷贝到wap子目录下。启动Nokia WAP Toolkit,在“Go”下选择“Open Location”,并输入URL地址(图2-28):


2-26 输入URL地址

    在选择“OK”以后,模拟器上出现系统当前的日期和时间(图2-29):


2-27 测试结果显示

使用Perl动态输出WML页面

 对于Perl编程,其基本原理符合CGI编程原理。先看下面的程序片段:

# HTTP header with correct MIME type
print "Content-type: text/vnd.wap.wml\n\n";

# WML starts here with the correct XML doc type declaration
print "<?xml version=\"1.0\"?>";
print "<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\"";
print "\"http://www.wapforum.org/DTD/wml_1.1.xml\">";
……
更多的Perl 代码,输出WML页面内容。
……

    按照以上的程序开头便可以输出动态的WML页面。下面有一段更详细的教程,这个教程是为以下开发者准备的:

    • 知道如何编写WML页面和卡片,并且了解自己的工作。

    • 知道如何配置自己服务器,使之能够运行Perl

    • CGI的基本知识,并且了解Perl的语法。

    从表面上来说,使用CGI来产生WML页面和产生HTML页面没有太大的区别。一旦读者仔细地看一下,就会明白WML所包含的新内容的确还是与其他的页面动态生成有所不同。
   
假设,老板需要做一个可以通过电话来访问电话目录。公司现在只有两个职员,但是将要不断扩大。因此,肯定不会去做一个固化的目录。相反将把数据保存在一个文本文件中,通过动态生成WML页面的来显示信息。
   
首先,将要建立一个“people_data.txt”,如下所示:

# Format is:
# Name|phone num|fax num|Title
Linus Smith|123-4567|098-7654|Director of OS Development
Jane Doe|123-4568|098-7655|Senior VP of OS Research

    可以看出“|”符号是用来区别字段的。“#”是用来表示注释的。这些字符没有其他的意义,仅仅是一般的转换。
   
下面,需要决定最后的WML文件将是什么样子。在同老板商量之后,决定如下:

  1. <?xml version="1.0"?>
  2. <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
  3. <wml>
  4. <card title="Telephone Book" id="index">
  5.     <p>
  6.         Welcome to Widgets Inc. Telephone Book
  7.         <a href="#view">Click to view list</a>
  8.     </p>
  9. </card>
  10. <card title="List of Names" id="view">
  11.     <p>
  12.         <a href="#linus_info">Linus Smith - 123-4567</a>
  13.         <a href="#jane_info">Jane Doe - 123-4568</a>
  14.     </p>
  15. </card>
  16. <card title="Info for Linus Smith" id="linus_info">
  17.     <p>
  18.         Director of OS development
  19.         Phone number: 123-4567
  20.         Fax number: 098-7654
  21.     </p>
  22. </card>
  23. <card title="Info for Jane Doe" id="jane_info">
  24.     <p>
  25.         Senior VP of OS research
  26.         Phone number: 123-4568
  27.         Fax number: 098-7655
  28.     </p>
  29. </card>
  30. </wml>

    每当增加新成员的时候,都不得不重新修改文件。这不是一个好办法。但是如果向文本文件中增加内容,那将是一个不会编写WML页面的程序员也会的事情。为了实现这个目标,使用以下的脚本:

#!/usr/bin/perl –w
use strict;
use CGI qw/:standard/;

# Our CGI object
my $q = new CGI;

    以上的内容是设置Perl环境。使用CGI模块来进行HTTP数据显示和处理。然而在这个例子当中,将使用尽量少的CGI模块,读者将会在将来的教程当中明白处理服务是多么的复杂。

# data file to get info from
my $data = "people_data.txt";

# Template for the title card
my $title_card = qq (
<card title="Telephone Book" id="index">
<p>
Welcome to Widgets Inc. Telephone Book<br/>
<a href="#view">Click to view list</a>
</p>
</card>
);

# Initial id of the first record
my $id = "a";

    这里已经创建了一个字符串,它将作为标题卡片。当然还有需要创建的动态ID。(选择‘a’,是因为它是一个简单的字符,可以很容易地增加)既然每个记录都有唯一的ID(因此可以把它作为头一个卡片)。ID将从‘a’开始,然后每找到一个新记录就增加一个。要一次性将所有的名字都生成卡片。然后使用一个初始化的缓冲区来显示成员的数据内容。

# Now iterate over the
data file building data dynamically
open(FILE,"$data") || die "Can't open $data: $!\n";
while (<FILE>) {
chomp;
next if (/^\#/);
my ($name, $phone, $fax, $position) = split(/\|/);

    打开自己的文件(当然要检查打开权限,保证能够打开),现在就可以读取了(注意:要跳过以“#”开头的行)。其他的部分,只需要从已经定义和产生的文件中读取就可以了。显然在读取的过程当中,需要做一定的检查,保证每个部分的确有数据。

# Build up the view card
$view_card .= build_view_card($name, $phone, $fax, $position);
# Build up info cards
$all_info_card .= build_info_card($name, $phone, $fax, $position);
}

    这里,将从文件中提取的数据传给两个函数。他们将返回由这些参数所确定的卡片。注意到Perl“.= ”操作符,函数返回的字符串将不断地扩充。

# Finish off the card

$view_card .= "</p> </card>";

my $header = qq (
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml.xml">
);

# Template for the entire deck
my $deck = qq(
$header
<wml>
$title_card
$view_card
$all_info_card
</wml>
);
print $q->header('text/vnd.wap.wml');
print $deck;

    这里非常简单地将他们绑在一起。打印文件头,建立卡片,并且将他们动态地放在一起。当做好这一切之后,需要输出一个Content-type        text/vnd.wap.wml
   
现在来看一下建立卡片的两个子过程:

sub build_view_card {
$id++;
my ($name, $phone, $fax, $position) = @_;
my $info_item = qq (
<a href="#$id">$name - $phone</a><br/>
);
return $info_item;
}

    这个代码将接收信息,并把这些信息插入到一个简单的模板里面。一个非常重要的一点是每次都在增加“$id”变量的数值。这个保证每个记录都有唯一的id号。Perl允许直接使用“++”操作符,使得从‘a’增加到‘b’

sub build_info_card {
# Template for card showing info for a single person
my ($name, $phone, $fax, $position) = @_;
my $info_card = qq (
<card title="Info for $name" id="$id">
<p>
$position<br/>
Phone number: $phone <br/>
Fax number: $fax <br/>
<do type="prev"><prev/></do>
</p>
</card>
);
return $info_card;
}

    在这个子过程当中,做了和前面一样的事情,只不过用了不同的模板。注意到已经在前面的函数中增加了“$id”的数值,因此,在这个函数中只需要返回一个新的卡片就可以了。
   
这就是全部过程,现在已经有了一个动态的电话本。当然这个教程不是一个很好的解决办法,至少他需要一个人手工来修改txt文件。另外,WML页面有大小的限制。但是无论如何,以上就是全部的过程。

 

使用PHP动态输出WML页面

请先看一个简单的PHP例子:

  1. <?php
  2. header ("Content-Type: text/vnd.wap.wml");
  3. echo "<?xml version=\"1.0\"?>\n"
  4. ?>
  5. <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" http://www.wapforum.org/DTD/wml_1.1.xml">
  6. <!-- Source Generated by WML Deck Decoder -->
  7. <wml>
  8. <card id=”main” title=”PHP WML”>
  9.     <p>
  10.         <?php
  11.             echo "hello,world\n";
  12.         ?>
  13.     </p>
  14. </card>
  15. </wml>

    启动Nokia WAP Toolkit,在“Go”下选择“Open Location”,并输入URL地址(图2-30):


2-30 输入URL地址

    使用Nokia WAP Toolkit测试结果显示如下(图2-31):


2-31 测试结果显示

    下面演示的是如何使用WMLPHP实现动态交互。演示的内容是:用户在WML页面中输入用户名和密码,然后提交给服务器端;服务器端将动态生成WML显示用户的输入,并返还到客户端。整个演示包括登录页面logon.wml和服务端脚本程序logon.php

logon.wml

  1. <?xml version="1.0"?>
  2. <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
  3. <!-- Source Generated by WML Deck Decoder -->
  4. <wml>
  5. <card>
  6.     <p>
  7.         User:<input name="user" value="admin"/>
  8.         Password:<input name="password" value="whoami"/>
  9.     </p>
  10.     <do type="accept">
  11.         <go href="http://127.0.0.1/php4/logon.php">
  12.             <postfield name="user" value="$(user)"/>
  13.             <postfield name="password" value="$(password)"/>
  14.         </go>
  15.     </do>
  16. </card>
  17. </wml>

logon.php

  1. <?php
  2.     header ("Content-Type: text/vnd.wap.wml");
  3.     echo "<?xml version=\"1.0\"?>\n"
  4. ?>
  5. <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
  6. <!-- Source Generated by WML Deck Decoder -->
  7. <wml>
  8. <card>
  9.     <p>
  10.         <?php
  11.             echo "Hello ! $(user) <br/>\n";
  12.             echo "Password is $(password) <br/>\n";
  13.         ?>
  14.     </p>
  15. </card>
  16. </wml>

     启动Nokia WAP Toolkit,在“Go”下选择“Open Location”,并输入URL地址(图2-32):


2-32 输入URL地址

    在模拟器上出现(图2-33):


2-33 登录界面

    在确定输入后(图2-34):


2-34 从服务器端返回的处理结果

使用Servlet动态输出WML页面

    下面有两个简单的例子程序,先看第一个简单例子:

  1. import java.io.*;
  2. import javax.servlet.*;
  3. import javax.servlet.http.*;
  4. public class WapServlet extends HttpServlet
  5. {
  6.     protected void service(HttpServletRequest request, HttpServletResponse response)
  7.         throws ServletException, java.io.IOException
  8.     {
  9.         response.setContentType("text/vnd.wap.wml");
  10.         PrintWriter out = response.getWriter();
  11.         xmlHeader(out);
  12.         out.println("<wml>");
  13.         out.println("<card title = \"JavaTest\">");
  14.         out.println("<p>Hello! This comes from a servlet!</p>");
  15.         out.println("</card>");
  16.         out.println("</wml>");
  17.     }
  18.     public void xmlHeader(PrintWriter out)
  19.     {
  20.         out.println("<?xml version=\"1.0\"?>");
  21.         out.println("<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\" "+"\"http://www.wapforum.org/DTD/wml_1.1.xml\">");
  22.     }
  23. }

    下面是Nokia提供的一个例子程序:

  1. import java.io.*;
  2. import javax.servlet.*;
  3. import javax.servlet.http.*;
  4. /*
  5. * HelloWorldServlet - a very simple servlet
  6. */
  7. public class HelloWorld extends HttpServlet
  8. {
  9.     String m_text;
  10.     // the initialization parameter is read during
  11.     // the initialization of the servlet
  12.     public void init(ServletConfig config) throws ServletException
  13.     {
  14.         super.init(config);
  15.         m_text = config.getInitParameter("text");
  16.         if (m_text == null)
  17.         {
  18.             m_text = "This is a simple test servlet.";
  19.         }
  20.     }
  21.     public void doGet(HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException
  22.     {
  23.         PrintWriter out = response.getWriter();
  24.         out.println("<?xml version=\"1.0\"?>");
  25.         out.println("<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML1.1//EN\"\"http://www.wapforum.org/DTD/wml_1.1.xml\">");
  26.         out.println("<wml>");
  27.         out.println("<card id=\"card1\" title=\"Hello World\">");
  28.         out.println("<p>");
  29.         out.println(m_text);
  30.         out.println("</p>");
  31.         out.println("</card>");
  32.         out.println("</wml>");
  33.         // Remember to close the out object
  34.         out.close();
  35.     }
  36.     public String getServletInfo()
  37.     {
  38.         return "The simple HelloWorld servlet.";
  39.     }
  40. }

使用JSP动态输出WML页面

    先看一个简单的例子程序:

  1. <?xml version="1.0"?>
  2. <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
  3. <% response.setContentType("text/vnd.wap.wml"); %>
  4. <wml>
  5. <card id="start">
  6.     <do type="accept">
  7.         <go href="index.jsp#test"/>
  8.     </do>
  9.     <p>
  10.         JSP Test:<br/>
  11.         Press accept to continue!<br/>
  12.     </p>
  13. </card>
  14. <card id="test">
  15.     <do type="prev">
  16.         <prev/>
  17.     </do>
  18. <%
  19.     out.println("<p>");
  20.     out.println("Hello from script code!<br/>");
  21.     out.println("</p>");
  22. %>
  23. </card>
  24. </wml>

    下面将要演示使用WMLJava Server API如何简单快速地在WAP终端上生成动态的WML页面。这里将不再去讲每个WML标记的作用,而是给出一个简单的例子。这个例子体现出整个语言的特点。
   
这个JSP例子显示了为一个移动用户定时更新约会的例子,例如:做一个给家里打电话的应用程序。这个应用是在Windows NT下使用Java Server Web Development KitJSWDK1.01JSWDK配套的服务端工具,来建立的JSP页面。使用Nokia WAP Toolkit 1.2测试和浏览应用。
   
一个典型的路径还包括在移动电话和WAP内容服务之间的一个WAP网关。这样安排的主要原因是要对WML进行编码,这样来适应其窄带的特点。但是Nokia WAP Toolkit包含了这样一个编译器,因此它可以直接从HTTP服务器提取WML内容(在例子里是从服务端工具上来取的)。
   
当建立WAP应用的时候,必须记住一点。WAP设备的显示屏幕很小——典型的只有412个字符,不要使用过多的滚动。这样会使用户十分烦心。数据的输入功能也十分有限。而且设备的RAM有限,CPU的能力有限。并且现在无线网络的带宽有限而且高延时。因此WAP的应用程序应当短小,简单。
   
应用程序包括两个页面。第一个是“pick_appointment.jsp”。它有一个选择卡片。当用户选择了其中的一个约会时间的时候,就会进入到“show_appointment_data.jsp”页面(中间还传递了约会的ID号)。第二个页面有两个卡片。第一个显示会面的时间,第二个显示数据输入,允许工程师通过输入ID取消约会。
   
动态的约会数据是通过JavaBean的实例来取得的。在这里没有它的代码,总的来说是通过JDBC连接到数据库的过程。取消的过程是通过Servlet来操作的,在这里其程序也没有显示。既然用户可能随时取消某个约会,因此需要定时地刷新“pick_appointment.jsp”

pick_appointment.jsp

  1. <%@ page language="java" contentType="text/vnd.wap.wml" %>
  2. <jsp:useBean id="appointmentBean" class="mwebber.samples.AppointmentBean" scope="application" />
  3. <%!
  4.    // This convenience method builds our <option> elements, one for each appointment.
  5.     private String getOptions(mwebber.samples.AppointmentBean appointmentBean)
  6.     {
  7.         StringBuffer sb = new StringBuffer();
  8.         int[] appointmentIDs = appointmentBean.getAppointmentIDs();
  9.         for(int i=0; i<appointmentIDs.length; i++)
  10.         {
  11.             sb.append("<option onpick=\"show_appointment_data.jsp?id=");
  12.             sb.append(i);
  13.             sb.append("\">");
  14.             sb.append(appointmentBean.getAppointmentTime(i));
  15.             sb.append("</option>");
  16.         }
  17.         return sb.toString();
  18.     }
  19. %>
  20. <%! String strXMLPrologue = "<?xml version=\"1.0\"?>"; %>
  21. <%-- WML CONTENT BEGINS --%>
  22. <%= strXMLPrologue %>
  23. <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
  24. <wml>
  25. <card id="pick" title="Appointments">
  26.     <!-- Refresh the deck every minute -->
  27.     <onevent type="ontimer">
  28.         <go href="pick_appointment.jsp"/>
  29.     </onevent>
  30.     <timer value="600"/>
  31.     <!-- Display a widget to navigate back by one in the history stack -->
  32.     <do type="prev">
  33.         <prev/>
  34.     </do>
  35.     <!-- Display the "select" widget of appointments to pick -->
  36.     <p>
  37.         <select title="Appointments">
  38.         <%= getOptions(appointmentBean) %>
  39.         </select>
  40.     </p>
  41. </card>
  42. </wml>
  43. <%-- WML CONTENT ENDS --%>

show_appointment_data.jsp

  1. <%@ page language="java" contentType="text/vnd.wap.wml" %>
  2. <jsp:useBean id="appointmentBean" class="mwebber.samples.AppointmentBean" scope="application" />
  3. <%
  4.     // Grab the "id" parameter, using the in-built reference to the request object.
  5.     int intAppointmentID = Integer.parseInt(request.getParameter("id"));
  6. %>
  7. <%! String strXMLPrologue = "<?xml version=\"1.0\"?>"; %>
  8. <%-- WML CONTENT BEGINS --%>
  9. <%= strXMLPrologue %>
  10. <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
  11. <wml>
  12. <card id="main_data" title="Main Data">
  13.     <p align="center">
  14.         <b>
  15.             <%= appointmentBean.getAppointmentTime(intAppointmentID) %>
  16.         </b>
  17.     </p>
  18.     <p>
  19.         <br/>
  20.             <%= appointmentBean.getAppointmentDetails(intAppointmentID) %>
  21.         <br/>
  22.         <a href="#check_off">Check off this appointment</a><br/>
  23.         <a href="http://localhost:8080/pick_appointment.jsp">Back to appointments list</a>
  24.     </p>
  25. </card>
  26. <card id="check_off" title="Check Off">
  27.     <!-- Set up a widget to send the input field data to the server -->
  28.     <do type="accept">
  29.         <go href="/servlet/ProcessCheckOff" method="post">
  30.             <postfield name="check_off_code" value="$check_off_code"/>
  31.         </go>
  32.     </do>
  33.     <p>
  34.         <input name="check_off_code" emptyok="false" maxlength="6"/>
  35.     </p>
  36.     <p>
  37.         <a href="#main_data">Back to appointment data</a>
  38.         <br/>
  39.         <a href="http://localhost:8080/pick_appointment.jsp">Back to appointments list</a>
  40.     </p>
  41. </card>
  42. </wml>
  43. <%-- WML CONTENT ENDS --%>

    在每一文件的开头,都要设置正确的MIME文件类型。如果文件类型不对,那么这个页面将遭到模拟器的拒绝。在这个例子里,“text/vnd.wap.wml”表明是WML页面。其他类型例如“text/vnd.wap.wmlscript”WMLScript源代码,而“image/vnd.wap.wbmp” WBMP文件。
   
既然WML页面是一个XML文档,因此它包含了标准的XML文件头和一个DTD头,但是“<?”“?>”标记显然与JSP的标记有混合。因此将这一段隐藏到字符串变量当中。别忘记JSP将被编译成Java源文件,最后成为Servlet

 

七、解决中文显示问题的四种方法

    WMLXML的一种应用,而XML的缺省编码是UTF-8,也就是Unicode8位编码方式。如果不特殊说明,那么XML将认为采用的是UTF-8的编码方式。这就造成了一个问题,几乎所有的文档内容都采用了GB2312方式,数据库中也不例外。而UnicodeGB2312的编码有很大的不同,可以说根本不一样,这是造成乱码的主要原因。
   
任何编码方式,包括日文、韩文、希腊文、阿拉伯文等都能轻松转换成Unicode。如果使用Unicode,就可以在同一段文档中加入各种语言。虽然现有的应用软件很少采用Unicode,但Windows NT的内核却采用Unicode来处理字符。Unicode方式有两个吸引人的个性:独立且宽容。
如何解决这些问题,现在常用的有以下的四种方法。

    • 直接采用UTF-8编码

    这种方法无需多讲。如果内容可以轻易转换到UTF-8编码还需要什么呢?坏处是需要对内容要进行全面的转换,而且与现有的大多数应用不兼容。

    • 直接使用GB2132编码

    这种方法也很简单,在编码声明时,标注采用GB2312编码方式,具体做法如:<?xml version="1.0" encoding="GB2312"?>。但是并不是所有终端都支持GB2132编码,仍然会出现乱码。
   
笔者做开发的时候就是采用这种办法解决中文问题。当时公司采用的是Motorola公司的网关,使用Motorola L2000wwwNokia 7110Simens 3568i进行测试。结果都十分满意。
   
如果使用这样的页面在Nokia WAP Toolkit上直接进行测试,将会发现Nokia WAP Toolkit将对中文进行自动编码,只是按照半个字节进行处理。因此出来之后就变成乱码了。

    • 采用字符转换

    其思想是用ASCII字符表现更大字符集中的字符。比如要展现希腊文的小写的alphaalphaUnicode的编码中是94516进制就是3B1,于是写下“α”或者“α”显示的就是小写的“α”。只需要知道汉字的Unicode编码,将其转换成“XXXX;”的形式。用ASCII编码方式,任何平台都能处理,而且HTML也支持。但是这样就增加了文件长度。

    • 配置应用服务直接输出UTF-8编码(适用于IIS

    IISResponse Object有一个属性CharSet,按微软的说法只要这么做就行:
            <% Response.Charset("UTF-8") %>
   
这种方法,只适用于Windows NTIISASP编程。其他的平台和Web服务器就没有如此简单的方式。

GB2132Unicode

    Unicode的原理也很简单。使用“”加上ASCII码的数值文本再加结尾。当然要注意的是:这里所指的ASCII数值并不是简单的内存数值(或者说是GB2132编码)。例如:字,在内存里面的数值是0xb1a5,而在Unicode映射表内是0x9971。请先看下面的一段小程序。在WML页面中混有英文和中文。中文是采用Unicode书写的。

<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1 //EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
    <card id="main" title="Chinese">
        <p>
            T;⛳
        </p>
    </card>
</wml>

Nokia WAP Toolkit中的测试结果为(图2-35所示):


2-35 使用Unicode的中文WML页面

    可以看到“⛳”变成了字。如果用“b1a5;”或者直接使用GB2132编码(相当于直接书写中文)那么显示结果就可能是图2-36所示的样子。肯定要问自己,怎么会这样!原因就是在这个位置上的Unicode编码字符是个怪字符。


2-36 没有使用Unicode的中文WML页面

    对于英文字符可以使用Unicode方式,也可以不使用。那么Unicode编码如何获得呢?
   
如果使用手工方式,那么可以在附件中找到“Unicode映射表。打开映射表,选择宋体“CJK Unified Ideograph”,在里面可以找到很多中文字符的Unicode编码。随便拷贝一个到WML文件中,百试百灵!就是使用的时候不是很方便。
   
如果使用Visual Basic脚本语言编写服务端程序(例如:ASP),那么问题就简单多了。可以使用AscW这个函数来解决编码的问题。以下的几个编码函数就是这样做的。
   
如果使用Visual C&C++编写服务端程序,那么必须使用到Windows的一个API函数:MultiButeToWideChar。这个函数可以实现Unicode的转换。
对于其他服务端脚本语言也应该有类似的函数。如果没有,那么就必须自己做一个转换工具,能把WML中的中文转换为Unicode。在MSDN提供的例子中有一个小的转换工具:Uconvert。但是好像不是很好用,不过可以研究里面的程序。在网络上有不少这样的实用小工具。

适用于ASP的转换程序

<%=replace(Server.HTMLEncode(request.form("text")),chr(13)+chr(10),"<BR>")%> >
以下是您的UNICODE:
<textarea name="text" cols="40" rows="10">
<%=Server.HTMLEncode(unicode(request.form("text")))%>

第一个Basic程序

function unicode(str)

    for i = 1 to Len(str)
        c = Mid(str, i, 1)
        unicode = unicode & "" & Hex(AscW(c)) & ";"
    next

end function

第二个Basic程序

function unicode(str)

    dim i,j,c,i1,i2,u,fs,f,p

    unicode=""
    p=""

    for i=1 to len(str)
        c=mid(str,i,1)
        j=ascw(c)
        if j<0 then
            j=j+65536
        end if
        if j>=0 and j<=128 then
            if p="c" then
                unicode=" "&unicode
                p="e"
            end if
            unicode=unicode&c
       else
            if p="e" then
                unicode=unicode&" "
                p="c"
            end if
            unicode=unicode&""&j&";"
        end if
    next

end function

 

全部转换程序

<%
    function unicode(str)

        dim i,j,c,i1,i2,u,fs,f,p

        unicode=""
        p=""
        for i=1 to len(str)
            c=mid(str,i,1)
            j=ascw(c)
            if j<0 then
                j=j+65536
           end if
            if j>=0 and j<=128 then
                if p="c" then
                    unicode=" "&unicode
                    p="e"
                end if
                    unicode=unicode&c
            else
                if p="e" then
                    unicode=unicode&" "
                    p="c"
                end if
                unicode=unicode&""&j&";"
            end if
    next

end function

function cutline(str,linelen)

    dim i,j,c,k

    cutline=""
    j=0
    for i=1 to len(str)
        c=mid(str,i,1)
        if asc(c)<0 or asc(c)>127 then
            k=2
        else
            if asc(c)<32 then
                k=0
                if asc(c)=13 then
                    j=0
                    cutline=cutline+"<br/>"+c
                    c=""
               end if
            else
                k=1
            end if
        end if
        j=j+k
        if j>linelen*2 then
            cutline=cutline+"<br/>"+vbCrlf+c
            j=k
        else
            cutline=cutline+c
        end if
    next

end function

function convertsymbol(sStr)

    dim i,c

    convertsymbol=""
    for i=1 to len(sStr)
        c=mid(sStr,i,1)
        if c=">" then
            convertsymbol=convertsymbol & ">"
        elseif c="<" then
            convertsymbol=convertsymbol & "<"
        elseif c="'" then
            convertsymbol=convertsymbol & "&apos;"
        elseif c="""" then
            convertsymbol=convertsymbol & """
        elseif c="&" then
            convertsymbol=convertsymbol & "&"
        elseif c="$" then
            convertsymbol=convertsymbol & "$$"
        else
            convertsymbol=convertsymbol & c
        end if
    next

end function

function convertstring(sStr)

    dim strtemp,asctemp,c

    strtemp=""
    for i=1 to len(sStr)
        c=mid(sStr,i,1)
        asctemp=ascw(c)
        if (asctemp>47 and asctemp<58) or (asctemp>64 and asctemp<91) or (asctemp>96 and asctemp<123) then
            strtemp=strtemp & c
        end if
    next

    convertstring=Lcase(strtemp)

end function
%>

八、动态输出WBMP

WBMP的格式

    对于一个宽为16个像素,高15个像素的白色画面,WBMP文件的数据如下:
                0x00
0x000x100x0f0xff0xff.……0xff
   
头两个字节为0。第一个字节是文件类型,0表示非压缩的黑白图片。第二个是文件扩展,是保留字段。第三个字节为图片的宽。第四个字节图片的高。后面的都是图像数据。
   
对于非压缩的黑白图片,数据格式有以下特点:每个位对应着一个像素。0表示黑色,1表示白色。高位开始到底位结束对应着从左到右的像素颜色。数据从低地址到高地址对应着图片从上到下。一个字节可以表示8个像素,不足的部分可以用0补齐。
   
下面是一张太阳的图片:

0x00 //file type=0
0x00 //file extension
没有使用
0x14 //

0x14 //

//Pixel data
0xfa,0xf5,0xf0,0xfb,0x6d,0xf0,0xfb,0xad,0xf0,0xfb,
0xdd,0x80,0x1b,0xfc,0x70,0xe7,0x1f,0xe0,0x7c,0xe7,
0xd0,0xbb,0xfb,0xd0,0xdb,0xfb,0xb0,0xd7,0xfd,0x70,
0xb7,0xfd,0xb0,0x77,0xf5,0xd0,0xfb,0xeb,0xe0,0xfb,
0xdb,0xf0,0x1c,0xe7,0x80,0xc7,0x1c,0x70,0xf7,0xfd,
0xf0,0xf7,0xbf,0xf0,0xf6,0x5d,0xf0,0xf5,0xed,0xf00

    以下的两个程序都是要显示一个长为5高为2WBMP图片。该图片第一行为白色,第二行为黑色。为此使用Nokia WAP Toolkit做了一个5*2WBMP图片,上行是黑的,下行是白的。整个WBMP文件总共六个字节,所有的数据如下:

0x00 //file type=0表示黑白的非压缩方式
0x00 //file extension
保留字段
0x05 //

0x02 //

0x00 //
第一行像素
0xf8 //
第二行像素

    由于宽为5不是8的倍数,因此右边的位使用0补齐。因此出现了第二行的0xf8,换成二进制就是:11111000。只有左边的五位是1,剩余的三位是0
需要注意的是:在做测试之前需要在MIME文件类型映射表中加入相应的WBMP文件映射。

使用ASP动态输出WBMP图片

wbmp.asp

<%@ Language=VBScript %>
<%
    Function StoB(varstr)
        str2bin = ""
        For i = 1 To Len(varstr)
            varchar = Mid(varstr, i, 1)
            str2bin = str2bin & ChrB(AscB(varchar))
        Next
        StoB = str2bin
    End Function

    img=chr(0) & chr(0) & chr(5) & chr(2) & chr(0) & chr(&Hf8)

    Response.ContentType="image/vnd.wap.wbmp"
    Response.BinaryWrite stob(img)
%>

    WML调用部分的程序如下:

<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card id="main">
    <p>
        <img src="http://127.0.0.1/wap/wbmp.asp" alt=":)" />
    </p>
</card>
</wml>

    启动Nokia WAP Toolkit,在“Go”下选择“Open Location”,并输入URL地址(图2-37):


2-37 输入URL地址

在确认后,模拟器上出现图2-38:(短短的一条直线正是想要的WBMP!)


2-38 动态输出的WBMP图像

使用ISAPI动态输出WBMP图片

    先使用Visual C&C++ 6.0ISAPI Wizard生成一个工程文件——WBMP。在WBMP.cpp中找到Default(CHttpServerContext* pCtxt)函数。按照下面的形式进行修改:

void CWBMPExtension::Default(CHttpServerContext* pCtxt)
{
/*
StartContent(pCtxt);
WriteTitle(pCtxt);

*pCtxt << _T("This default message was produced by the Internet");
*pCtxt << _T(" Server DLL Wizard. Edit your CWBMPExtension::Default()");
*pCtxt << _T(" implementation to change it.\r\n");

EndContent(pCtxt);
*/
AddHeader(pCtxt, "Content-type: image/vnd.wap.wbmp\r\n");

BYTE img[6]={0x00,0x00,0x05,0x02,0x00,0xf8};

CHtmlStream htmlStream;
htmlStream.Write(img,6);

*pCtxt<<htmlStream;
}

    WML调用部分的程序如下:

<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card id="main">
    <p>
        <img src="http://127.0.0.1/wap/wbmp.dll" alt=":)" />
    </p>
</card>
</wml>

    启动Nokia WAP Toolkit,在“Go”下选择“Open Location”,并输入URL地址(图2-39):


2-39 输入URL地址


   
在确认后,模拟器上出现图2-40所示的内容:(短短的一条直线正是想要的WBMP!)


2-40 动态输出的WBMP图像

 

九、定时器

介绍了如何使用定时器来实现动态刷新

    WML中的ontimer事件有着很重要的意义。在实现自动刷新和动画方面很有用处。下面就要讲述如何使用ontimer事件来实现一个自动刷新的时钟程序。
   
在实现自动刷新的过程中一个十分重要的问题就是CacheCacheWAP中有很重要的作用,但是同时它也给程序编制人员带来了很多不方便。例如:当我反复地查询同一支股票信息的时候,如果不采取防止Cache的方法,那么结果只能是一个。这显然是不正确的。那么如何防止这种情况发生呢?在WAP问答中有详细的解答。但是这里将采用一种最简单的办法,那就是使得用户所产生的URL请求没有一次是相同的。即使出现相同,那么手机的内存无法保留那么多的页面内容。
   
时钟的例子程序如下:(需要注意到WML页面和动态生成程序)

Main.wml

<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card id="main" ontimer="http://127.0.0.1/wap/wapd.dll?GetTime?ID=100" title="Main">
    <timer name="wait1" value="60"/>
    <p>
        Wait for a moment...
    </p>
</card>
</wml>

    动态时钟程序的核心:

void CWAPExtension::GetTime(CHttpServerContext* pCtxt,int nID)
{
    AddHeader(pCtxt,"Content-type: text/vnd.wap.wml\n\n");

    *pCtxt<<_T("<?xml version=\"1.0\"?>\n");
    *pCtxt<<_T("<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN");
    *pCtxt<<_T("\" \"http://www.wapforum.org/DTD/wml_1.1.xml\">\n");
    *pCtxt<<_T("<wml>\n");

    CString strCard;
    strCard.Format(_T("<card id=\"time\" ontimer=\"http://127.0.0.1/wap/wapd.dll?GetTime?ID=%d\" title=\"Time\">\n"),++nID);
    *pCtxt<<(LPCTSTR)strCard;

    CString strWait;
    strWait.Format(_T("<timer name=\"wait%d\" value=\"60\"/>"),nID);
    *pCtxt<<(LPCTSTR)strWait;

    *pCtxt<<_T(" <p>\n");

    CTime tmCurrent=CTime::GetCurrentTime();
    *pCtxt<<(LPCTSTR)tmCurrent.Format(_T("%Y-%m-%d\n"));
    *pCtxt<<(LPCTSTR)tmCurrent.Format(_T("%H:%m:%S\n"));

    *pCtxt<<_T(" </p>\n");
    *pCtxt<<_T(" </card>\n");
    *pCtxt<<_T("</wml>");
}

    我们注意到nID在不断增加,那么就意味着nID就不可能相同。那么每次的URL就不会相同,手机每次必须去服务器取回内容。大家同时也注意到了ontimername怎么也在不断地改变呢?这个是一个很有意思的事情:在UpPhoneNokia WAP Toolkit 1.2中,如果不改变这个ID,那么就只出现一次刷新,这是为什么呢?很简单,他们将timer这个变量做为全局变量保存了下来,再下一次进来的时候,忘记将timervalue重新赋值了。但是这也不是一个最好的解决办法。你会发现UpPhone在运行128次后就死掉了。为什么?因为每次不同,它都作为全局的timer,因此最多只能有128timer。怎么办呢?最好的程序在下面:

void CWAPExtension::GetNext(CHttpServerContext* pCtxt,int nID)
{
    nID=nID%67+1;

    AddHeader(pCtxt,"Content-type: text/vnd.wap.wml\n\n");

    *pCtxt<<_T("<?xml version=\"1.0\"?>\n");
    *pCtxt<<_T("<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN");
    *pCtxt<<_T("\" \"http://www.wapforum.org/DTD/wml_1.1.xml\">\n");
    *pCtxt<<_T("<wml>\n");
    *pCtxt<<_T("<card id=\"humor\" title=\"Picture\">\n");
    *pCtxt<<_T(" <onevent type=\"ontimer\">\n");
    CString strGoTo;
    strGoTo.Format(_T(" <go href=\"http://127.0.0.1/wap/wap.dll?GetNext?ID=%d\">\n"),nID);
    *pCtxt<<(LPCTSTR)strGoTo;
    *pCtxt<<_T(" <setvar name=\"wait\" value=\"150\"/>\n");
    *pCtxt<<_T(" </go>\n");
    *pCtxt<<_T(" </onevent>");
    *pCtxt<<_T(" <timer name=\"wait\" value=\"150\"/>");
    *pCtxt<<_T(" <p>\n");
    CString strImage;
    strImage.Format(_T(" <img alt=\"wbmp%d\" src=\"http://127.0.0.1/wap/%d.wbmp\"/>\n"),nID,nID);
    *pCtxt<<(LPCTSTR)strImage;
    *pCtxt<<_T(" </p>\n");
    *pCtxt<<_T(" </card>\n");
    *pCtxt<<_T("</wml>");
}

    这个程序相比之下就好多了。但是在UpPhone上运行还是有问题。已经有同志使用这种方法实现了WAP电子地图的自动更新。

 

十、用户代理检


介绍了如何检测用户浏览器的类型。

    User-AgentURL申请头中有着重要的意义。可以用来判断用户代理的类型。通过检测用户代理,就可以针对不同的用户代理发送最适合的信息。在这里将要介绍一个Servlet小工具用来检测User-Agent,以及几种有特点的用户代理。当然该工具也适合于检测其他类型的HTTP头,源程序如下:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class Snoop extends HttpServlet
{
    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
    {
        response.setContentType("text/vnd.wap.wml");
        PrintWriter out = response.getWriter();
        out.println("<?xml version=\"1.0\"?>");
        out.println("<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\" \"http://www.wapforum.org/DTD/wml_1.1.xml\">");
        String check=request.getParameter("check");
        out.println("<wml>");
        out.println(" <card id=\"check\" title=\"" + check + "\">");
        out.println(" <p>");
        if(check.equals("Method"))
        {
            out.println(request.getMethod());
        }
        else if(check.equals("URL"))
        {
            out.println(request.getRequestURL());
        }
        else if(check.equals("Protocol"))
        {
            out.println(request.getProtocol());
        }
        else if(check.equals("ServletPath"))
        {
            out.println(request.getServletPath());
        }
        else if(check.equals("PathInfo"))
        {
            out.println(request.getPathInfo());
        }
        else if(check.equals("PathTranslated"))
        {
            out.println(request.getPathTranslated());
        }
        else if(check.equals("QueryString"))
        {
            out.println(request.getQueryString());
        }
        else if(check.equals("ContentLength"))
        {
            out.println(request.getContentLength());
        }
        else if(check.equals("ServerName"))
        {
            out.println(request.getServerName());
        }
        else if(check.equals("ServerPort"))
        {
            out.println(request.getRemoteUser());
        }
        else if(check.equals("RemoteUser"))
        {
            out.println(request.getRemoteAddr());
        }
        else if(check.equals("RemoteHost"))
        {
            out.println(request.getRemoteHost());
        }
        else if(check.equals("AuthType"))
        {
            out.println(request.getAuthType());
        }
        else
        {
            out.println(request.getHeader(check));
        }
        out.println(" </p>");
        out.println(" </card>");
        out.println("</wml>");
    }
}

    我在Catalina Server下调试通过。程序根据你选择的不同check选择项返回不同的信息。这个小工具对于在程序调试的时候是十分有用的。你可以在你的form中间加入一个check选择项,将action指向snoop就可以知道用户代理发送给服务器的所有内容,方便吧!
   
当你输入check=User-Agent的时候,那么返回的就是User-Agent的值。m3Gate只能在使用IE的时候才可以启动。UpPhone传回的数据最长。Nokia WAP Toolkit 1.2相比之下就比较简单。ccWAP居然就只有:WML Browser。其他类型的User-Agent值你可以在浏览器列表中找到!

转载于:https://www.cnblogs.com/ableid/archive/2009/03/21/1418560.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值