posts - 129, comments - 17, trackbacks - 0, articles - 0
  IT博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

 
Windows下Apache+Tomcat+MySQL+jsp+php的服务器整合配置经验总结

1.作者:moonsbird  
  题目:Windows下Apache+Tomcat+MySQL+jsp+php的服务器整合配置经验总结
  时间:2004.8.19
  最初发表于西南交通大学锦城驿站   
2.本文是作者学习几年来学习jsp/配置服务器环境的经验总结。可以转载,但请注明出处.
    
   开始学习jsp很久了,网上有许多关于jsp整合的例子,但存在着许多问题。
对于Apache+Tomcat+MySQL+jsp+php的整合,针对不同的Tomcat和apache的版本,稍微有些区别。
即使你E文很好,去看Tomcat和Apache的手册,也不一定能完全整合成功。暑假在家,网速不像
在交大那样---老黄牛拉破车样的慢,所以基于此作者查阅大量网上资料,汲其精华,写这篇文
章希望能为初学者提供方便,也可以作为高手的参考手册。
    jsp是优秀的,它基于Java技术,利用JavaBean/Servlet制作动态网站,Java强大的面向对
象编程使Jsp相对于php和asp有着得天独厚的优势。但学习jsp相对于php和asp来说是有难度的。
我想原因之一就是jsp服务器环境的配置要棘手一些。对于动态网页制作,在交大asp曾很长时
间占据上风,不太多的“高手”(也许很多人认为使用jsp的是高手吧)使用jsp,交大比较成功的
jsp网站如世纪星网(Tomcat)、网络教育学院网站(Resin)、交大招生就业网(Tomcat),服务器是
Linux 9,运行相对来说比较稳定可靠。Linux下的Apache和Tomcat整合我曾经发过一篇文章,需
要的大家在锦城搜搜。linux和windows下整合思想是相同的,利用apache来解析*.html/*.htm
静态网页,当遇到*.jsp和servlet时,通过一个apache中的module把jsp页面抛给Tomcat(或者
resin)来处理,在linux下通过编译来制作这个module,要麻烦一些,本文通过现成的win下的一
个module来完成整合。
废话不说啦,下面开始整合:

所需软件
  (1) j2sdk-1_4_2-windows-i586.exe 
   jdk的版本关系倒不是很大,不影响Apache和Tomcat的整合,配置环境变量时注意一些就行了.
  (2) apache_2.0.49-win32-x86-no_ssl.exe
   apache版本对整合影响不是太大,但最好利用此版本。
  (3) jakarta-tomcat-4.1.30.exe
   版本最好用这个版本,其他的我没有试过,有可能会导致整合不成功。
  (3) php-4.3.3-Win32.zip
  是一个压缩包,下载下来直接解压就行了。
  (4) mod_jk.dll
  这个module是放到apache中的module中去的,利用它来使Tomcat和Apache之间通讯。
  (5) mysql-max-3.23.46a-win.zip  
  数据库mysql,其版本对整合影响不大.
  (6) jdbc驱动
   mysql的驱动程序,和数据库连接时用
   以上软件我就不把down的地址贴出来啦,去网上搜baidu、北大天网,只要用心找,应该问题
不大,如果真的找不到,发信找我要吧。

一.安装j2sdk
   在Windows下,直接运行下载的j2sdk-1_4_1_01-windows-i586.exe文件,根据安装向导安装
到一个目录,例如安装到d:\server\j2sdk
   1.配置环境变量:
   添加CLASSPATH变量
变量值为
./;%CLASSPATH%;D:\server\j2sdk1.4.0\lib\dt.jar;d:\server\j2sdk1.4.0\lib\tools.jar;
             d:\server\j2sdk1.4.0\bin
   这个配置保证java运行时可以找到class类。
   2.添加JAVA_HOME变量,变量值为d:\server\j2sdk1.4.0
   3.在原来的path变量中,添加d:\server\j2sdk1.4.0\bin,这样无论在何处运行java名令都行。
重新启动

cmd进入dos,运行java和javac看有没有反应,如果出现命令的帮助,那么java就安装成功了
也可以运行一个java小程序看看.

public class test{
  public static void main(String args[]){
    System.out.println("看到这个说明java安装成功!");
}
}
把上以程序存放在test.java文件中,一定要是test.java才行,然后利用命令编译
javac test.java
如果没有错误,则说明编译成功,如果有错,在程序书写正确的基础上,刚说明java环境变量没
有配置好,刚重新配置。如果编译成功,则运行:
java test
注意,不是java test.java!如果无误,刚会打印出:

   看到这个说明java安装成功!

gxgx,jdk安装成功啦!可以进行下一步.

二.apache安装
    1.运行apache_2.0.49-win32-x86-no_ssl.exe,一路点"确定"和"接受"就行,路径选
d:\server\apache group\apache2。
    此时apache已经安装成功,且已运行,在IE中输入http://localhost/可以看到
apache的主页。安装成功!
    2.修改httpd.conf
     apache的配置文件是httpd.conf,位于apache根目录的下的conf文件夹下
    (1).修改默认网站根目录:
       在d:\server下建立文件夹www,以此作为网站的根目录
       DocumentRoot "D:/server/Apache Group/Apache2/htdocs"
       改为DocumentRoot "D:/server/www
    (2).修改字符设置
      apache解析中文网页时会产生乱码,
      修改AddDefaultCharset ISO-8859-1为AddDefaultCharset GB2312
    (3).修改默认主页
       当访问目录时,apache会自动导入的主页,优先级以先后顺序为准
       把DirectoryIndex index.html index.html.var改为
       DirectoryIndex index.html index.jsp index.php default.jsp default.php index.html.var
    (4).设置错误页面
      这样对于你的网站会更安全些,如果没设置,别人在你的网址后随便输入一个路径,会显示404错
    误,并且会显示你的服务器版本号,服务器配置一目了然,为了避免这种情况,可以设置错误页面。
      当出现404错误,即找不到网页时,把访问者导入到一个错误页面
  找到:
    #
    # Customizable error responses come in three flavors:
    # 1) plain text 2) local redirects 3) external redirects
    #
    # Some examples:
    #ErrorDocument 500 "The server made a boo boo."
    #ErrorDocument 404 /missing.html
    #ErrorDocument 404 "/cgi-bin/missing_handler.pl"
    #ErrorDocument 402 http://www.example.com/subscription_info.html
    # 
       httpd.conf中的这一部分,#ErrorDocument 404 /missing.html
      一行修改为ErrorDocument 404 /error/noFile.htm,其中noFile.htm为d:\server\www\error\下一个错误文件,需
   要你自己建立。当发生404错误时,进入noFile.htm页面,可以提示网页没有找到。这样就不可能看到
你的服务器软件信息了。也可以设置其它的错误导向的页面,具体http响应错误编号请查阅相关资料。
重新启动apache,如果没意外,此时已经安装成功,把静态页面放到d:\server\www\error\目录下,看能不能成功解析。
随便输入一个:http://localhost/sdagasgdasasg.htm,看是不是导向你设置的404错误,即noFile.htm错误页面.
成功了吧!
    关于apache中其他参数的意义,我就不说啦,你可以去网上查,其实最好的方法是看httpd.conf中的注释部
分,已经说得很清楚啦。
好啦,到此apache安装成功,你可以休息一下,听首音乐,开始下一步的安装:)

三.php安装
   解压php-4.3.3-Win32.zip,到d:\server\php目录下.下配置PHP和Apache,使之能解析php程序。
   将d:\server\php\目录中的“php.ini-dist”改名“php.ini”,并复制到C:\windows\system32
(Win2K目录为:C:\winnt\system32)下,在apache的配置文件httpd.conf中下入以下几行,使之
支持php.
    ScriptAlias /php/ "d:/server/php/" 
    AddType application/x-httpd-php .php3
    AddType application/x-httpd-php .php
    AddType application/x-httpd-php .phtml 
    Action application/x-httpd-php "/php/php.exe"
    在d:\server\www下建立网页:index.php,内容为:
    <html>
     <head><title>php测试</title></head>
     <?php
       phpinfo();
     ?>
    </html>
  用文本编辑器编辑好为存为index.php,重新启动apache,在IE中输入http://localhost/index.php,如果出现php的
环境变量,说明php,安装成功,如果不能解析<?php phpinfo(); ?>中的部分,则说明没有成功安装。

四.Tomcat的安装
   我用的是jakarta-tomcat-4.1.30.exe安装文件,在windows下一路点"确定"就行了,比较简单,安装目录为
d:\server\tomcat
   下面设置环境变量:
   同jdk的环境变量,添加变量TOMCAT_HOME,值为d:\server\tomcat;增加变量CATALINA_HOME,值为
d:\tomcat\server,在path变量中加入d:\server\tomcat\bin
   下面启动tomcat,运行tomcat目录下的,bin/startup.bat文件就行啦,出现tomcat运行控制台,tomcat成功
启动.
   在IE中输入http://localhost:8080/,看到大猫的吧,则tomcat安装成功.

五.安装MySQL

   第一步:解压 mysql-max-3.23.46a-win.zip,运行mysql安装文件,把mysql安装到d:\server\mysql
   第二步:在环境变量path中加入 d:\server\mysql\bin
   第三步:进入d:\server\mysql\bin目录,运行winmysqladmin,刚会出现对话框,设置mysql用户密码,假设
帐号为:root 密码为:123
   第四步:进入dos,进入mysql/bin下,运行以下命令打开mysql
   mysqld-nt --standalone
   则mysql开启
   然后运行:
   mysql
   如果开启,则出现以下文字:
   Welcome to the MySQL monitor. Commonds end with ; or \g.
   Your MySQL connection id is 1 to server version: 3.23.46-nt 
   Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
   mysql>
   注意:此时mysql没有设置密码,为了不让别人进入你的数据库,请为mysql设置密码,具体操作请参阅mysql
的中文说明文档。
   至此,mysql安装成功!

六.安装MySQL的驱动类jdbc
   解开jdbc驱动包,把里面的org文件夹复制到jsp可以找到的位置就行啦。可以是tomcat的根目录common/classes
文件夹下,也可以是jsp网站class类WEB-INF/class/下。当加载驱动程序时,以便能找到相关的类。或者把jdbc包
的路径加入到classpath变量中。

七.Tomcat和Apache的整合配置
   Tomcat只是一个用来解析*.jsp文件的容器,它不是服务器,其解析静态htm/html文件的能力远远不如apache,
整合后在同一个端口使apache来解析静态网页,而遇到*.jsp网页时,交由tomcat来解析.
  第一步:把 mod_jk.dll复制到apache根目录module文件夹下。
  第二步:配置APACHE

  打开apache根目录下conf\httpd.conf

添加以下语句:

     #------------------------------------------

    #connecting to tomcat using ajp13 
    LoadModule jk_module modules/mod_jk.dll
    JkWorkersFile "d:/server/tomcat/conf/workers.properties"
    JkMount /servlet/* ajp13 
    JkMount /*.jsp ajp13 


    #-----------------------------------------------------

   注意:以上语句的基本意思是:
   loadModule一行:加载mod_jk.dll动态链接库模块,使之与tomcat通讯
   jkWorkersFile:指明工作时tomcat相关文件workers.properties位置
   jkMount:两行指明当遇到*.jsp和servlet时,交由ajp13协议,继而转发给tomcat处理。
  第三步:建立workers.properties文件,在tomcat根目录conf下建立workers.properties文件,内容为:
workers.tomcat_home=d:\server\tomcat
workers.java_home=d:\server\j2sdk1.4.0
ps=\
# worker.list=ajp13 
worker.list=ajp12,ajp13  

worker.ajp12.port=8007
worker.ajp12.host=localhost
worker.ajp12.type=ajp12 
worker.ajp12.lbfactor=1
             
worker.ajp13.port=8009         
worker.ajp13.host=localhost      
worker.ajp13.type=ajp13
worker.ajp13.lbfactor=1

worker.loadbalancer.type=lb 

worker.loadbalancer.balanced_workers=ajp12, ajp13
worker.inprocess.type=jni 
worker.inprocess.class_path=$(workers.tomcat_home)$(ps)classes 
worker.inprocess.class_path=$(workers.tomcat_home)$(ps)lib$(ps)jaxp.jar 

worker.inprocess.class_path=$(workers.tomcat_home)$(ps)lib$(ps)parser.jar 

worker.inprocess.class_path=$(workers.tomcat_home)$(ps)common$(ps)lib$(ps)jasper.jar 

worker.inprocess.class_path=$(workers.tomcat_home)$(ps)common$(ps)lib$(ps)servlet.jar 

worker.inprocess.class_path=$(workers.tomcat_home)$(ps)common$(ps)lib$(ps)webserver.jar

worker.inprocess.class_path=$(workers.java_home)$(ps)lib$(ps)tools.jar 

worker.inprocess.cmd_line=-config 

worker.inprocess.cmd_line=$(workers.tomcat_home)/conf/jni_server.xml 

worker.inprocess.cmd_line=-home 

worker.inprocess.cmd_line=$(workers.tomcat_home) 

worker.inprocess.jvm_lib=$(workers.java_home)$(ps)jre$(ps)bin$(ps)classic$(ps)jvm.dll 


worker.inprocess.stdout=$(workers.tomcat_home)$(ps)inprocess.stdout 

worker.inprocess.stderr=$(workers.tomcat_home)$(ps)inprocess.stderr 

worker.inprocess.sysprops=tomcat.home=$(workers.tomcat_home)

注意以上文件第一、二行填上tomcat和jdk的相关目录.
 
  第四步:配置tomca/conf下的server.xml文件,这个是tomcat的主要配置文件,重要配置如下:
    1.虚拟目录设置:
     找到下面内容:
     <Host name="localhost" debug="0" appBase="/webapps" 
       unpackWARs="true" autoDeploy="true">
    修改为:
    <Host name="localhost" debug="0" appBase="d:/server/www" 
       unpackWARs="true" autoDeploy="true">
    使tomcat主目录为d:\server\www.
    找到下面内容:
    <Context path="examples" docBase="/examples" debug="0"
                 reloadable="true" crossContext="true">
    修改为
    <Context path="" docBase="/" debug="0"
                 reloadable="true" crossContext="true">
    这个我的理解是应该是使tomcat根目录下的jsp作为一个虚拟主机吧.
   2.设置tomcat监听端口,找到下面的内容:
   <Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
         port="8080"     minProcessors="5" maxProcessors="75"
               enableLookups="true" redirectPort="8443"
               acceptCount="100" debug="0" connectionTimeout="0"
               useURIValidationHack="false" disableUploadTimeout="true" />
    默认的端口是8080,但这个大家都知道,最好修改一下,假设修改为:8123,则如下:
   <Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
         port="8123"     minProcessors="5" maxProcessors="75"
               enableLookups="true" redirectPort="8443"
               acceptCount="100" debug="0" connectionTimeout="0"
               useURIValidationHack="false" disableUploadTimeout="true" />
   3.找到以下8009端口的地方,这个很重要,通过8009,tomcat与apache互相通讯,如果下面的部分
被<!---->注释掉,一定要把<!---->去掉.
    <!-- Define a Coyote/JK2 AJP 1.3 Connector on port 8009 -->
    <Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
               port="8009" minProcessors="5" maxProcessors="75"
               enableLookups="true" redirectPort="8443"
               acceptCount="10" debug="0" connectionTimeout="0"
               useURIValidationHack="false"
               protocolHandlerClassName="org.apache.jk.server.JkCoyoteHandler"/>
    第五步:配置tomcat的conf/web.xml文件:
    类似于apache下404错误页面的配置,在最后</web-app>一行之前加入以下内容:
      
       <error-page> 
        <error-code>404</error-code>
        <location>/error/noFile.htm</location> 
      </error-page>
      <error-page>
       <exception-type>java.lang.NullPointerException</exception-type> 
       <location>/error/error.jsp</location> 
      </error-page>
      第一个<error-page></error-page>之间的是404未找到jsp网页的错误导向页面,使之发生404错误时,
网页导向至/error/noFile.htm,也可以用类似方法添加其多的错误代码导向页面。
      第二个<error-page></error-page>之间的是当jsp网页出现java.lang.NullPointerException导常时
导向至/error/error.jsp错误页面。那么需要在第个jsp网页中加入以下内容:
      <%@ page  errorPage="/error/error.jsp" %>
      典型的error.jsp错误页面的程序写法如下:
   
    <%@ page contentType="text/html;charset=GB2312"%> 
    <%@ page isErrorPage="true"%>
    <html>
    <head><title>错误页面</title></head>

    <body>
    出错了:</p> 错误信息: <%= exception.getMessage() %><br>

    Stack Trace is : <pre><font color="red"><%
    java.io.CharArrayWriter cw = new java.io.CharArrayWriter();
    java.io.PrintWriter pw = new java.io.PrintWriter(cw,true);
    exception.printStackTrace(pw);
    out.println(cw.toString());
    %></font></pre>
    </body>
    </html>
        当出现NullPointerException异常时tomcat会把网页导入到error.jsp,且会打印出出错信息。
        第五步主要是安全考虑,在正常情况下,如果找不到网页即出现404错误或者jsp程序出错,在客户端
    会列举出类似于如下的信息(以tomcat为例,resin类似):
      HTTP Status 404 - /sdags.jsp

      --------------------------------------------------------------------------------

      type Status report

      message /sdags.jsp

      description The requested resource (/sdags.jsp) is not available.


       --------------------------------------------------------------------------------

      Apache Tomcat/5.0.19
      
      这样服务器版本会被别人看到,如果完成第五步的配置,就不会出现这种情况.
    第六步:配置httpd.conf文件
    这种方法把tomcat和apache的根目录设置为同一目录,但是必须限制客户对目录下一些文件的访问,比如存入class
的web-inf,在httpd.conf下添加以下代码:
        #deny the access to WEB-INF
    <Directory ~ "/WEB-INF/"> 
          Order allow,deny 
          Deny from all 
    </Directory> 
     这样,当试图记问http://localhost/web-inf/时,apache将会拒绝.
      
     到此为至,tomcat和apache的整合就完成啦.重新启动tomcat和apache,
 把jsp网页放到d:\server\www\下,例如:
     <html>
    <h1>JSP test<hr>

    <%out.println("Hello World!");%>

    </html>
    存为index.jsp,在IE中输入http://localhost:8123/index.jsp
    如果出现了
    Hello World!
    刚说明tomcat运行正常,在IE中输入http://localhost/index.jsp,如果也出现
    Hello World!
    恭喜你!整合成功啦,你在jsp漫长艰辛的道路上迈出了一大步~!

    以上整合文章我是在仓促的的时间完成的,可能有些错误。或考虑不周的地方,但是总体应该没
 太大问题,我是反复使用这种方法整合成功的。如果有疑问,可以直接给我联系.
   moonsbird:    mail:pengw97@163.com

posted @ 2005-08-04 17:58 woow 阅读(1613) | 评论 (1)编辑 收藏

http://www.chedong.com/tech/cvs_card.html

关键词:CVS CVSWeb CVSTrac WinCVS CVSROOT

内容摘要:

CVS是一个C/S系统,多个开发人员通过一个中心版本控制系统来记录文件版本,从而达到保证文件同步的目的。工作模式如下:

       CVS服务器(文件版本库)     /     |       \     (版 本 同 步)   /       |         \开发者1  开发者2   开发者3
作为一般开发人员挑选2,6看就可以了,CVS的管理员则更需要懂的更多一些,最后还简单介绍了一些Windows下的cvs客户端使用,CVS远程用户认证的选择及与BUG跟踪系统等开发环境的集成问题。

CVS环境初始化:CVS环境的搭建 管理员
CVS的日常使用:日常开发中最常用的CVS命令, 开发人员 管理员
CVS的分支开发:项目按照不同进度和目标并发进行 管理员
CVS的用户认证:通过SSH的远程用户认证,安全,简单 管理员
CVSWEB:CVS的WEB访问界面大大提高代码版本比较的效率 管理员
CVS TAG:将$Id$ 加入代码注释中,方便开发过程的跟踪开发人员
CVS vs VSS: CVS和Virsual SourceSafe的比较 开发人员 管理员
WinCVS: 通过SSH认证的WinCVS认证设置
基于CVSTrac的小组开发环境搭建:通过CVSTrac实现web界面的CVS用户管理,集成的BUG跟踪和WIKI交流
CVS中的用户权限管理:基于系统用户的CVS权限管理和基于CVSROOT/passwd的虚拟用户管理

一个系统20%的功能往往能够满足80%的需求,CVS也不例外,以下是CVS最常用的功能,可能还不到它全部命令选项的20%,作为一般开发人员平时会用cvs update和cvs commit就够了,更多的需求在实际应用过程中自然会出现,不时回头看看相关文档经常有意外的收获。

 


CVS环境初始化
环境设置:指定CVS库的路径CVSROOT
tcsh
setenv CVSROOT /path/to/cvsroot
bash
CVSROOT=/path/to/cvsroot ; export CVSROOT

后面还提到远程CVS服务器的设置:
CVSROOT=:ext:$USER@test.server.address#port:/path/to/cvsroot CVS_RSH=ssh; export CVSROOT CVS_RSH

初始化:CVS版本库的初始化。
cvs init

一个项目的首次导入
cvs import -m "write some comments here" project_name vendor_tag release_tag
执行后:会将所有源文件及目录导入到/path/to/cvsroot/project_name目录下
vender_tag: 开发商标记
release_tag: 版本发布标记

项目导出:将代码从CVS库里导出
cvs checkout project_name
cvs 将创建project_name目录,并将最新版本的源代码导出到相应目录中。这个checkout和Virvual SourceSafe中的check out不是一个概念,相对于Virvual SourceSafe的check out是cvs update, check in是cvs commit。

 

CVS的日常使用

注意:第一次导出以后,就不是通过cvs checkout来同步文件了,而是要进入刚才cvs checkout project_name导出的project_name目录下进行具体文件的版本同步(添加,修改,删除)操作。

将文件同步到最新的版本
cvs update
不制定文件名,cvs将同步所有子目录下的文件,也可以制定某个文件名/目录进行同步
cvs update file_name
最好每天开始工作前或将自己的工作导入到CVS库里前都要做一次,并养成“先同步 后修改”的习惯,和Virvual SourceSafe不同,CVS里没有文件锁定的概念,所有的冲突是在commit之前解决,如果你修改过程中,有其他人修改并commit到了CVS 库中,CVS会通知你文件冲突,并自动将冲突部分用
>>>>>>
content on cvs server
<<<<<<
content in your file
>>>>>>
标记出来,由你确认冲突内容的取舍。
版本冲突一般是在多个人修改一个文件造成的,但这种项目管理上的问题不应该指望由CVS来解决。

确认修改写入到CVS库里
cvs commit -m "write some comments here" file_name

注意:CVS的很多动作都是通过cvs commit进行最后确认并修改的,最好每次只修改一个文件。在确认的前,还需要用户填写修改注释,以帮助其他开发人员了解修改的原因。如果不用写-m "comments"而直接确认`cvs commit file_name` 的话,cvs会自动调用系统缺省的文字编辑器(一般是vi)要求你写入注释。
注释的质量很重要:所以不仅必须要写,而且必须写一些比较有意义的内容:以方便其他开发人员能够很好的理解
不好的注释,很难让其他的开发人员快速的理解:比如: -m "bug fixed" 甚至 -m ""
好的注释,甚至可以用中文: -m "在用户注册过程中加入了Email地址校验"

修改某个版本注释:每次只确认一个文件到CVS库里是一个很好的习惯,但难免有时候忘了指定文件名,把多个文件以同样注释commit到CVS库里了,以下命令可以允许你修改某个文件某个版本的注释:
cvs admin -m 1.3:"write some comments here" file_name

添加文件
创建好新文件后,比如:touch new_file
cvs add new_file
注意:对于图片,Word文档等非纯文本的项目,需要使用cvs add -kb选项按2进制文件方式导入(k表示扩展选项,b表示binary),否则有可能出现文件被破坏的情况
比如:
cvs add -kb new_file.gif
cvs add -kb readme.doc

如果关键词替换属性在首次导入时设置错了怎么办?
cvs admin -kkv new_file.css

然后确认修改并注释
cvs ci -m "write some comments here"

删除文件
将某个源文件物理删除后,比如:rm file_name
cvs rm file_name
然后确认修改并注释
cvs ci -m "write some comments here"
以上面前2步合并的方法为:
cvs rm -f file_name
cvs ci -m "why delete file"
注意:很多cvs命令都有缩写形式:commit=>ci; update=>up; checkout=>co/get; remove=>rm;

添加目录
cvs add dir_name

查看修改历史
cvs log file_name
cvs history file_name

查看当前文件不同版本的区别
cvs diff -r1.3 -r1.5 file_name
查看当前文件(可能已经修改了)和库中相应文件的区别
cvs diff file_name
cvs的web界面提供了更方便的定位文件修改和比较版本区别的方法,具体安装设置请看后面的cvsweb使用

正确的通过CVS恢复旧版本的方法:
如果用cvs update -r1.2 file.name
这个命令是给file.name加一个STICK TAG: "1.2" ,虽然你的本意只是想将它恢复到1.2版本
正确的恢复版本的方法是:cvs update -p -r1.2 file_name >file_name
如果不小心已经加成STICK TAG的话:用cvs update -A 解决

移动文件/文件重命名
cvs里没有cvs move或cvs rename,因为这两个操作是可以由先cvs remove old_file_name,然后cvs add new_file_name实现的。

删除/移动目录
最方便的方法是让管理员直接移动,删除CVSROOT里相应目录(因为CVS一个项目下的子目录都是独立的,移动到$CVSROOT目录下都可以作为新的独立项目:好比一颗树,其实砍下任意一枝都能独立存活),对目录进行了修改后,要求其开发人员重新导出项目cvs checkout project_name 或者用cvs update -dP同步。

项目发布导出不带CVS目录的源文件
做开发的时候你可能注意到了,每个开发目录下,CVS都创建了一个CVS/目录。里面有文件用于记录当前目录和CVS库之间的对应信息。但项目发布的时候你一般不希望把文件目录还带着含有CVS信息的CVS目录吧,这个一次性的导出过程使用cvs export命令,不过export只能针对一个TAG或者日期导出,比如:
cvs export -r release1 project_name
cvs export -D 20021023 project_name
cvs export -D now project_name

CVS Branch:项目多分支同步开发
确认版本里程碑:多个文件各自版本号不一样,项目到一定阶段,可以给所有文件统一指定一个阶段里程碑版本号,方便以后按照这个阶段里程碑版本号导出项目,同时也是项目的多个分支开发的基础。

cvs tag release_1_0

开始一个新的里程碑:
cvs commit -r 2 标记所有文件开始进入2.x的开发

注意:CVS里的revsion和软件包的发布版本可以没有直接的关系。但所有文件使用和发布版本一致的版本号比较有助于维护。

版本分支的建立
在开发项目的2.x版本的时候发现1.x有问题,但2.x又不敢用,则从先前标记的里程碑:release_1_0导出一个分支 release_1_0_patch
cvs rtag -b -r release_1_0 release_1_0_patch proj_dir

一些人先在另外一个目录下导出release_1_0_patch这个分支:解决1.0中的紧急问题,
cvs checkout -r release_1_0_patch
而其他人员仍旧在项目的主干分支2.x上开发

在release_1_0_patch上修正错误后,标记一个1.0的错误修正版本号
cvs tag release_1_0_patch_1

如果2.0认为这些错误修改在2.0里也需要,也可以在2.0的开发目录下合并release_1_0_patch_1中的修改到当前代码中:
cvs update -j release_1_0_patch_1

CVS的远程认证通过SSH远程访问CVS
使用cvs本身基于pserver的远程认证很麻烦,需要定义服务器和用户组,用户名,设置密码等,

常见的登陆格式如下:
cvs -d :pserver:cvs_user_name@cvs.server.address:/path/to/cvsroot login
例子:
cvs -d :pserver:cvs@samba.org:/cvsroot login

不是很安全,因此一般是作为匿名只读CVS访问的方式。从安全考虑,通过系统本地帐号认证并通过SSH传输是比较好的办法,通过在客户机的 /etc/profile里设置一下内容:
CVSROOT=:ext:$USER@cvs.server.address#port:/path/to/cvsroot CVS_RSH=ssh; export CVSROOT CVS_RSH
所有客户机所有本地用户都可以映射到CVS服务器相应同名帐号了。

比如:

CVS服务器是192.168.0.3,上面CVSROOT路径是/home/cvsroot,另外一台开发客户机是192.168.0.4,如果 tom在2台机器上都有同名的帐号,那么从192.168.0.4上设置了:
export CVSROOT=:ext:tom@192.168.0.3:/home/cvsroot
export CVS_RSH=ssh
tom就可以直接在192.168.0.4上对192.168.0.3的cvsroot进行访问了(如果有权限的话)
cvs checkout project_name
cd project_name
cvs update
...
cvs commit


如果CVS所在服务器的SSH端口不在缺省的22,或者和客户端与CVS服务器端SSH缺省端口不一致,有时候设置了:
:ext:$USER@test.server.address#port:/path/to/cvsroot

仍然不行,比如有以下错误信息:
ssh: test.server.address#port: Name or service not known
cvs [checkout aborted]: end of file from server (consult above messages if any)

解决的方法是做一个脚本指定端口转向(不能使用alias,会出找不到文件错误):
创建一个/usr/bin/ssh_cvs文件,假设远程服务器的SSH端口是非缺省端口:34567
#!/bin/sh
/usr/bin/ssh -p 34567 "$@"
然后:chmod +x /usr/bin/ssh_cvs
并CVS_RSH=ssh_cvs; export CVS_RSH

注意:port是指相应服务器SSH的端口,不是指cvs专用的pserver的端口

 

CVSWEB:提高文件浏览效率
CVSWEB就是CVS的WEB界面,可以大大提高程序员定位修改的效率:

使用的样例可以看:http://www.freebsd.org/cgi/cvsweb.cgi

CVSWEB的下载:CVSWEB从最初的版本已经演化出很多功能界面更丰富的版本,这个是我个人感觉安装设置比较方便的:
原先在:
http://www.spaghetti-code.de/software/linux/cvsweb/,但目前已经删除,目前仍可以在本站下载CVSWEB,其实最近2年FreeBSD的CVSWeb项目已经有了更好的发展吧,而当初没有用FreeBSD那个版本主要就是因为没有彩色的文件Diff功能。
下载解包:
tar zxf cvsweb.tgz
把配置文件cvsweb.conf放到安全的地方(比如和apache的配置放在同一个目录下),
修改:cvsweb.cgi让CGI找到配置文件:
$config = $ENV{'CVSWEB_CONFIG'} || '/path/to/apache/conf/cvsweb.conf';

转到/path/to/apache/conf下并修改cvsweb.conf:

修改CVSROOT路径设置:
%CVSROOT = (
'Development' => '/path/to/cvsroot', #<==修改指向本地的CVSROOT
);
缺省不显示已经删除的文档:
"hideattic" => "1",#<==缺省不显示已经删除的文档
在配置文件cvsweb.conf中还可以定制页头的描述信息,你可以修改$long_intro成你需要的文字
CVSWEB可不能随便开放给所有用户,因此需要使用WEB用户认证:
先生成 passwd:
/path/to/apache/bin/htpasswd -c cvsweb.passwd user

修改httpd.conf: 增加
<Directory "/path/to/apache/cgi-bin/cvsweb/">
AuthName "CVS Authorization"
AuthType Basic
AuthUserFile /path/to/cvsweb.passwd
require valid-user
</Directory>

 

CVS TAGS: $Id: cvs_card.html,v 1.5 2003/03/09 08:41:46 chedong Exp $
将$Id: cvs_card.html,v 1.9 2003/11/09 07:57:11 chedong Exp $ 加在程序文件开头的注释里是一个很好的习惯,cvs能够自动解释更新其中的内容成:file_name version time user_name 的格式,比如:cvs_card.txt,v 1.1 2002/04/05 04:24:12 chedong Exp,可以这些信息了解文件的最后修改人和修改时间

 


几个常用的缺省文件:default.php<?php/* * Copyright (c) 2002 Company Name. * $Header: /home/cvsroot/tech/cvs_card.html,v 1.9 2003/11/09 07:57:11 chedong Exp $ */?>====================================Default.java: 注意文件头一般注释用 /* 开始 JAVADOC注释用 /** 开始的区别/* * Copyright (c) 2002 MyCompany Name. * $Header: /home/cvsroot/tech/cvs_card.html,v 1.9 2003/11/09 07:57:11 chedong Exp $ */package com.mycompany;import java.;/** * comments here */public class Default {    /**     * Comments here     * @param     * @return     */    public toString() {    }}====================================default.pl:#!/usr/bin/perl -w# Copyright (c) 2002 Company Name.# $Header: /home/cvsroot/tech/cvs_card.html,v 1.9 2003/11/09 07:57:11 chedong Exp $# file comments hereuse strict;
CVS vs VSS
CVS没有文件锁定模式,VSS在check out同时,同时记录了文件被导出者锁定。

CVS的update和commit, VSS是get_lastest_version和check in

对应VSS的check out/undo check out的CVS里是edit和unedit

在CVS中,标记自动更新功能缺省是打开的,这样也带来一个潜在的问题,就是不用-kb方式添加binary文件的话在cvs自动更新时可能会导致文件失效。

$Header: /home/cvsroot/tech/cvs_card.html,v 1.5 2003/03/09 08:41:46 chedong Exp $ $Date: 2003/11/09 07:57:11 $这样的标记在Virsual SourceSafe中称之为Keyword Explaination,缺省是关闭的,需要通过OPITION打开,并指定需要进行源文件关键词扫描的文件类型:*.txt,*.java, *.html...

对于Virsual SourceSafe和CVS都通用的TAG有:
$Header: /home/cvsroot/tech/cvs_card.html,v 1.5 2003/03/09 08:41:46 chedong Exp $
$Author: chedong $
$Date: 2003/11/09 07:57:11 $
$Revision: 1.9 $

我建议尽量使用通用的关键词保证代码在CVS和VSS都能方便的跟踪。

WinCVS
下载:

cvs Windows客户端:目前稳定版本为1.2
http://cvsgui.sourceforge.net
ssh Windows客户端
http://www.networksimplicity.com/openssh/

安装好以上2个软件以后:
WinCVS客户端的admin==>preference设置
1 在general选单里
设置CVSROOT:
username@192.168.0.123:/home/cvsroot
设置Authorization: 选择SSH server

2 Port选单里
钩上:check for alternate rsh name
并设置ssh.exe的路径,缺省是装在 C:\Program Files\NetworkSimplicity\ssh\ssh.exe

然后就可以使用WinCVS进行cvs操作了,所有操作都会跳出命令行窗口要求你输入服务器端的认证密码。

当然,如果你觉得这样很烦的话,还有一个办法就是生成一个没有密码的公钥/私钥对,并设置CVS使用基于公钥/私钥的SSH认证(在general 选单里)。

可以选择的diff工具:examdiff
下载:
http://www.prestosoft.com/examdiff/examdiff.htm
还是在WinCVS菜单admin==>preference的WinCVS选单里
选上:Externel diff program
并设置diff工具的路径,比如:C:\Program Files\ed16i\ExamDiff.exe
在对文件进行版本diff时,第一次需要将窗口右下角的use externel diff选上。

基于CVSTrac的小组开发环境搭建
作为一个小组级的开发环境,版本控制系统和BUG跟踪系统等都涉及到用户认证部分。如何方便的将这些系统集成起来是一个非常困难的事情,毕竟我们不能指望 Linux下有像Source Offsite那样集成度很高的版本控制/BUG跟踪集成系统。

我个人是很反对使用pserver模式的远程用户认证的,但如果大部分组员使用WINDOWS客户端进行开发的话,总体来说使用 CVSROOT/passwd认证还是很难避免的,但CVS本身用户的管理比较麻烦。本来我打算自己用perl写一个管理界面的,直到我发现了 CVSTrac:一个基于WEB界面的BUG跟踪系统,它外挂在CVS系统上的BUG跟踪系统,其中就包括了WEB界面的CVSROOT/passwd文件的管理,甚至还集成了WIKIWIKI讨论组功能。
这里首先说一下CVS的pserver模式下的用户认证,CVS的用户认证服务是基于inetd中的:
cvspserver stream tcp nowait apache /usr/bin/cvs cvs --allow-root=/home/cvsroot pserver
一般在2401端口(这个端口号很好记:49的平方)

CVS用户数据库是基于CVSROOT/passwd文件,文件格式:
[username]:[crypt_password]:[mapping_system_user]
由于密码都用的是UNIX标准的CRYPT加密,这个passwd文件的格式基本上是apache的htpasswd格式的扩展(比APACHE的 PASSWD文件多一个系统用户映射字段),所以这个文件最简单的方法可以用
apache/bin/htpasswd -b myname mypassword
创建。注意:通过htpasswd创建出来的文件会没有映射系统用户的字段
例如:
new:geBvosup/zKl2
setup:aISQuNAAoY3qw
test:hwEpz/BX.rEDU

映射系统用户的目的在于:你可以创建一个专门的CVS服务帐号,比如用apache的运行用户apache,并将/home/cvsroot目录下的所有权限赋予这个用户,然后在passwd文件里创建不同的开发用户帐号,但开发用户帐号最后的文件读写权限都映射为apache用户,在SSH模式下多个系统开发用户需要在同一个组中才可以相互读写CVS库中的文件。

进一步的,你可以将用户分别映射到apache这个系统用户上。
new:geBvosup/zKl2:apache
setup:aISQuNAAoY3qw:apache
test:hwEpz/BX.rEDU:apache

CVSTrac很好的解决了CVSROOT/passwd的管理问题,而且包含了BUG跟踪报告系统和集成WIKIWIKI交流功能等,使用的 CGI方式的安装,并且基于GNU Public License:


在inetd里加入cvspserver服务:
cvspserver stream tcp nowait apache /usr/bin/cvs cvs --allow-root=/home/cvsroot pserver

xietd的配置文件:%cat cvspserver
service cvspserver
{
disable = no
socket_type = stream
wait = no
user = apache
server = /usr/bin/cvs
server_args = -f --allow-root=/home/cvsroot pserver
log_on_failure += USERID
}

注意:这里的用户设置成apache目的是和/home/cvsroot的所有用户一致,并且必须让这个这个用户对/home/cvsroot/下的 CVSROOT/passwd和cvstrac初始化生成的myproj.db有读取权限。


安装过程

下载:可以从http://www.cvstrac.org 下载
我用的是已经在Linux上编译好的应用程序包:cvstrac-1.1.2.bin.gz,
%gzip -d cvstrac-1.1.2.bin.gz
%chmod +x cvstrac-1.1.2.bin
#mv cvstarc-1.1.1.bin /usr/bin/cvstrac
如果是从源代码编译:
http://www.sqlite.org/download.html 下载SQLITE的rpm包:
rpm -i sqlite-devel-2.8.6-1.i386.rpm
ftp://ftp.cvstrac.org/cvstrac/ 下载软件包
解包,假设解包到/home/chedong/cvstrac-1.1.2下,并规划将cvstrac安装到/usr/local/bin目录下, cd /home/chedong/cvstrac-1.1.2 编辑linux-gcc.mk:
修改:
SRCDIR = /home/chedong/cvstrac-1.1.2
INSTALLDIR = /usr/local/bin
然后
mv linux-gcc.mk Makefile
make
#make install


初始化cvstrac数据库:假设数据库名是 myproj
在已经装好的CVS服务器上(CVS库这时候应该已经是初始化好了,比如:cvs init初始化在/home/cvsroot里),运行一下
%cvstrac init /home/cvsroot myproj
运行后,/home/cvsroot里会有一个的myproj.db库,使用CVSTRAC服务,/home/cvsroot/myproj.db /home/cvsroot/CVSROOT/readers /home/cvsroot/CVSROOT/writers /home/cvsroot/CVSROOT/passwd这几个文件对于web服务的运行用户应该是可写的,在RedHat8上,缺省就有一个叫 apache用户和一个apache组,所以在httpd.conf文件中设置了用apache用户运行web服务:
User apache
Group apache,
然后设置属于apache用户和apache组
#chown -R apache:apache /home/cvsroot
-rw-r--r-- 1 apache apache 55296 Jan 5 19:40 myproj.db
drwxrwxr-x 3 apache apache 4096 Oct 24 13:04 CVSROOT/
drwxrwxr-x 2 apache apache 4096 Aug 30 19:47 some_proj/
此外还在/home/cvsroot/CVSROOT中设置了:
chmod 664 readers writers passwd

在apche/cgi-bin目录中创建脚本cvstrac:
#!/bin/sh
/usr/bin/cvstrac cgi /home/cvsroot
设置脚本可执行:
chmod +x /home/apache/cgi-bin/cvstrac

http://cvs.server.address/cgi-bin/cvstrac/myproj 进入管理界面
缺省登录名:setup 密码 setup
对于一般用户可以从:
http://cvs.server.address/cgi-bin/cvstrac/myproj
在setup中重新设置了CVSROOT的路径后,/home/cvsroot
如果是初次使用需要在/home/cvsroot/CVSROOT下创建passwd, readers, writers文件
touch passwd readers writers
然后设置属于apache用户,
chown apache.apache passwd readers writers
这样使用setup用户创建新用户后会同步更新CVSROOT/passwd下的帐号

修改登录密码,进行BUG报告等,
更多使用细节可以在使用中慢慢了解。


对于前面提到的WinCVS在perference里设置:
CVSROOT栏输入:
username@ip.address.of.cvs:/home/cvsroot
Authenitication选择:use passwd file on server side
就可以了从服务器上进行CVS操作了。


CVS的用户权限管理
CVS的权限管理分2种策略:


基于系统文件权限的系统用户管理:适合多个在Linux上使用系统帐号的开发人员进行开发。
基于CVSROOT/passwd的虚拟用户管理:适合多个在Windows平台上的开发人员将帐号映射成系统帐号使用。
为什么使用apache/apache用户?首先RedHat8中缺省就有了,而且使用这个用户可以方便通过cvstrac进行WEB管理。
chown -R apache.apache /home/cvsroot
chmod 775 /home/cvsroot

Linux上通过ssh连接CVS服务器的多个开发人员:通过都属于apache组实现文件的共享读写
开发人员有开发服务器上的系统帐号:sysuser1 sysuser2,设置让他们都属于apache组,因为通过cvs新导入的项目都是对组开放的:664权限的,这样无论那个系统用户导入的项目文件,只要文件的组宿主是apache,所有其他同组系统开发用户就都可以读写;基于ssh远程认证的也是一样。


   apache(system group)
/            |           \
sysuser1   sysuser2     sysuser3


Windows上通过cvspserver连接CVS服务器的多个开发人员:通过在passwd文件种映射成 apache用户实现文件的共享读写
他们的帐号通过CVSROOT/passwd和readers writers这几个文件管理;通过cvstrac设置所有虚拟用户都映射到apache用户上即可。


   apache(system user)
/            |            \
windev1     windev2      windev3            

 

利用cvs + (WinCVS/cvsweb/cvstrac),构成了一个相对完善的跨平台工作组开发版本控制环境。

相关资源:

CVS HOME:
http://www.cvshome.org

CVS FAQ:
http://www.loria.fr/~molli/cvs-index.html

相关网站:
http://directory.google.com/Top/Computers/Software/Configuration_Management/Tools/Concurrent_Versions_System/


CVS--并行版本系统
http://www.soforge.com/cvsdoc/zh_CN/book1.html

CVS 免费书:
http://cvsbook.red-bean.com/

CVS 命令的速查卡片:
http://www.refcards.com/about/cvs.html

WinCVS:
http://cvsgui.sourceforge.net/

CVSTrac: A Web-Based Bug And Patch-Set Tracking System For CVS
http://www.cvstrac.org

StatCVS:基于CVS的代码统计工具:按代码量,按开发者的统计表等
http://sourceforge.net/projects/statcvs


如何在WEB开发中规划CVS上:在Google上查 "cvs web development"
http://ccm.redhat.com/bboard-archive/cvs_for_web_development/index.html

一些集成了CVS的IDE环境:
Eclipse
Magic C++

posted @ 2005-08-04 17:54 woow 阅读(193) | 评论 (0)编辑 收藏

1. 开发WAP软件需要哪些工具?

为了开发WAP应用程序,需要一个WAP网关(注意:这里的网关可能是指支持WML的服务器。可以通过配置WWW服务器达到这个目的)和WAP工具包。工具包应当包括模拟器和能让开发者浏览WML网页。WML页
面的开发和HTML页面的开发一样,可以使用Notepad或者其他文本编辑器来进行编辑。

2. 有哪些公司现在提供这样的开发环境?

Nokia、Ericsson、UpPhone和Motorola都提供免费的WAP网关和工具包。
Nokia:Nokia Toolkit和Nokia WAP Server。
Ericsson:Ericsson R320和WapIDE。
UpPhone:UpPhone SDK。
Motorola:Motorola ADK。

3. 开发WAP应用一定要有WAP手机吗?

不是,当开发WAP应用的时候,不一定需要WAP手机。模拟器可以帮助开发者解决大部分的问题。但是如果是开发商业网站,特别是想知道各种移动电话在显示WML页面上的差别的时候,最好是配备一个。目前各种手机
对WML标记的支持和中文的支持状况大不相同,因此WAP手机还是必要的。

4. 开发者需要一个WAP网关吗?

不是很必要。如果只想进行简单的WAP内容服务,可以使用现有的Web服务器(只需要修改MIME类型)。移动电话会通过坐落在本地的网关连接到你的服务器上。
但是在网关上驻留开发者的程序有很多好处。既然开发者的程序是网关的一个部分,开发者就可以知道呼叫号码、身份、位置等等。

5. 可以看到WML的源代码么?

如果开发者使用SDK浏览的时候将能够看到WML的代码。如果只有一个HTML浏览器,可以访问“Fetch Page”服务(http://www.webcab.de)来取得代码。这个可以显示在Inter
net上的任何WML页面中。

6. 可能在WML中加入applets吗?

不能。
7. 可以使用HTML开发工具来开发WAP应用吗?

在大多数情况下开发工具是使用基于PC的浏览器。HTML、JavaScript和Java对于WAP开发来说都没有用处。但是,越来越多的开发工具在加入对WML的支持。
Allair的Cold Fusion 4.5 和 HomeSite已经有WML支持,虽然Allair也许需要清除一些BUG。另外PHP和ASP在Coldfusion/HomeSite也能支持。
可以到 Marjolei Katsma的 HomeSite Help site 上得到更多的信息。

8. 可以通过WML页面来操作数据库吗?

可以,与创建HTML页面相同。任何相关的服务器端的技术都可以用来生成WML页面。

9. 可以使用CGI生成WML页面吗?

当然。可以用创建HTML同样的方法来创建WML。如果想书写一个CGI来创建WML,只要记住在页面的开头正确设置MIME类型。具体的形式根据所使用的语言不同而不同。例如在Perl中:

print ("Content-type:application/vnd.wap.wml \n\n\n"); 

注意至少要使用2个换行。

10. 如何使用Cold Fusion来生成页面?

使用Cold Fusion只需要加上:

<CFCONTENT type="text/vnd.wap.wml">

11. 如何使用PHP来书写动态的WML页面?

PHP(和大多数其他服务端脚本语言一样)可以被用来书写动态的WML内容。只需要将输出的标记限制在WML微型浏览器可接受的范围内。
注意PHP有很多内建的HTML功能,特别是错误功能,这些功能WML微型浏览器可能无法识别。
PHP同样可以在一个HTML文件中编写出既适合于HTML,也适合于WML的内容。PHP的源代码对于客户端来说是不可见的。因此可以针对HTML浏览器输出HTML页面,针对WML浏览器输出WML页面。
可以在开发PHP编写的WML页面的时候把以下代码加在开头:

<?

// header("Content-type: text/vnd.wap.wml");
  echo("<?xml version=\"1.0\"?>\n");
echo("<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\"
//"http://www.wapforum.org/DTD/wml_1.1.xml\">\n\n");
?>

基于PC的浏览器将忽略这些无法理解的WML标记。但是如果想在WAP设备或者模拟器上测试的时候,只需要将"//"去掉,页面自动变成WML页面。

12. 使用PHP动态输出WML

这些例子生成一个非常有用的应用叫做:PizzaCalc。它将输入所有的pizza的帐单和人的数目,可以算出每个人的花费。
应用生成一个动态的页面叫做“calc”或者“input”。注意到所有的转义字符例如双引号。该页显示了一个简单的变量处理,和如何传递参数到另外的卡片:
使用WML浏览器就可以测试应用程序:
http://wap.colorline.no/wap-faq/apps/pizzacalc.html。
或者输入:
http://wap.colorline.no/demos.html选择应用。

<?
header("Content-type: text/vnd.wap.wml");
echo("<?xml version=\"1.0\"?>\n");
echo("<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\"
\"http://www.wapforum.org/DTD/wml_1.1.xml\">\n\n");
echo("<!--The application PizzaCalc was originally made by The Crusaders
www.crusaders.no on the Commodore Amiga -->\n");
echo("<!-- It was unfortunately not possible to emulate the crap interger handling of the
original program -->\n");
?>

<wml>
<?
  if($action == "calc") {
    echo("<card id=\"result\" title=\"PizzaCalc\">\n");
    echo("<do type=\"prev\" label=\"Back\">\n");
    echo("<go href=\"pizzacalc.html#input\"/>\n");
    echo("</do>\n");
    echo("<p>\n");
    echo("The cost per eater will be ".$total / $eaters."<br/>\n");
  }
  else {
    echo("<card id=\"input\" title=\"PizzaCalc\">\n");
    echo("<p>\n");
echo("<anchor>Split Pizza bill
<go href=\"pizzacalc.html?total=\$(total)&eaters=\$(eaters)&action=calc\"/>
</anchor>\n");
    echo("<br/>\n");
    echo("Total cost: <input type=\"text\" name=\"total\" format=\"*N\"/>\n");
    echo("Eaters: <input type=\"text\" name=\"eaters\" format=\"*N\"/>\n");
  }
?>
</p>
</card>
</wml>

13. 可以使用Java Servlet来生成WML页面吗?

当然。可以使用创建HTML同样的方法来创建WML。如果想书写一个CGI来创建WML,只要记住在页面的开头正确设置MIME类型:
response.setContentType("text/vnd.wap.wml");

14. 可以使用ASP、Perl等生成动态的应用吗?

是的。可以使用任何服务器端的脚本语言来生成WAP应用。
15. 如何使用ASP书写WML内容?

ASP(Active Server Pages)可以做到和PHP一样,也可以用来书写动态的WML。如果需要一些好的例子请参考Luca Passani's WAP and ASP articles。或
者查看Jean-Luc Praz's (jeanluc@corobori.com)。更多的ASP例子在:http://www.corobori.com/wap/。

16. 在使用ASP动态输出WML页面的时候,已经设置了Content-type,但是浏览器返回的仍然是text/html,有什么问题吗?

如果在ASP脚本中有一个错误,那么诊断程序会发还一个HTML页面,请检查脚本。

17. 在使用ASP生成WML页面的时候出现了错误: <MIME type "text/html" is not supported>,会是什么问题?

这个问题是Web浏览器不知道WML的正确类型,修改ASP的第一行,加入:

<Response.ContentType = "text/vnd.wap.wml"> 

后就可以工作了。

18. 下面的代码有什么问题吗?

<%Response.ContentType = "text/vnd.WAP.WML"%>
<?xml version="1.0"?>

去掉<?xml version="1.0"?>之前的空格。XML解释器希望在这行中没有其他字符,甚至是空行。

19. ASP代码可以在模拟器上工作,在真正的浏览器上怎么不行?

在很多模拟器上没有像真正的WML浏览器那么严格。这些对于那些没有使用网关的模拟器(Nokia SDK/Toolkit)来说更是这样,有些就根本没有使用网关(WinWAP、WapMAN)。
一个真正的WML浏览器应该只读取二进制的数据(从WML编码得来的)WMLC,对于网关应该将文本WML转换/编译成WMLC。语法是非常严格的。ASP是为HTML浏览器设置的,但是HTML没有WML那么
严格。
这里在ASP生成动态页面的时候有一个微小的“bug”。它在WML浏览器上不允许有任何地方输出白行(例如:空格,回车,换行)。注意到有些网关可能会修正这些问题,但有的则不管(例如:CMG网关)。
下面是一个常见的ASP代码用来输出WML页面开头的MIME类型:

<%Response.ContentType = "text/vnd.wap.wml"%>
<?xml version="1.0"?>

问题就在ASP将会在 .wml"%> 和 <?xml vers 之间输出换行和回车。这两行就被分割了。这将打乱WML代码的内容。WML必须以“<”开头,而且第一行是<?xml version="1.0"?>。
就上面的WML页面回车/换行将会出现问题。
最简单的解决办法是:

<%Response.ContentType = "text/vnd.wap.wml"%><?xml version="1.0"?>

在XML定义正确的格式化以后,后面的部分WML对空格就没有那么严格的要求。

要注意的是有些网关在输出ASP的时候会有问题,因此在WML代码中最好使用 Response.Write 而不是<%=MyVar%>。
20. 如何使用Perl来生成WML内容?

和其他Server端程序一样。Perl也可以用来书写漂亮的WAP应用程序。
最常见的就是如何使用Perl输出正确的MIME类型,下面的例子说明了这一点:

print "Content-type: text/vnd.wap.wml\n\n";
print "<?xml version=\"1.0\"?>\n";
print "<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\"
\"http://www.wapforum.org/DTD/wml_1.1.xml\">\n";
print "<wml>\n";
……

21. 应当如何下手书写WAP应用程序?

其实需要的只是Text编辑器。但是使用一个开发工具可以节约很多时间。
在这之前应该浏览一下WAP的权威站点:www.wapforum.com。
在Nokia WAP 开发论坛中进行注册,并且下载Nokia WAP Developer Toolkit 。Toolkit中的PDF文件可以给出一定的WML和WMLScript指导。Nokia To
olkit需要JRE (Java Runtime Environment) v.1.2.2 或者更高版本。
虽然工具可以用来为WAP设备设计应用,但是不是为专门的移动电话。在WAP开发工具上所看到的并不代表用户在手机上所看到的。为了确定想看到的事情,最好需要一个WAP设备,例如移动电话,或者模拟器。
Nokia WAP SDK 2有一个7110的模拟器。模拟器是一个有效的检测方式,能检测程序中的bug。 Nokia SDK 同样还包括一个小的WAP server让开发者可以从本地或者HTTP服务
器上下载WML页面。
到 Phone.com 开发站点注册后,Phone.com 提供UP.browser。这是最流行的浏览器,特别是在美国,Phone.com 提供UP.SDK。 在注册之后就可以下载。
对于Ericsson R320 和 R380是最近的事情。应该注册并查看Ericsson's Developer's Zone 来得到开发工具。R380是一个非常好的模拟器,在 Symbian 不需
要注册就可以下载。Ericsson 没有公开的为R320的模拟器。
Motorola 有一个平台叫做 Mobile Internet eXchange 或者 MIX 。Mobile Application Development Kit 已经开发出一个开发平台,即为
WAP也为Motorola的 VoxML。在注册后,可以在下面的网址找到数据包。

http://www.motorola.com/MIMS/MSPG/cgi-bin/spn_madk.cgi. 

WAPmine 是一个独立的应用,叫做 WAPPage 是一个所见即所得的编辑工具。而且有一个XML树型控件来编辑WML标签。
如果在开发公共应用程序时,想在很多设备上测试你的程序,就像在不同的浏览器上测试HTML页面一样。注意在不同的WML浏览器上的差别,可能比在不同的HTML浏览器上的差别要大。

22. 如何编写和测试WML页面?

现在有很多SDK。AnywhereYouGo.com有WAP SDK和IDE列表,可以下载一个来用。任何文本编辑器都可以书写一个简单的WML页面,当然HTML编辑器也可以(特别是那些支持个人定义标签
的),例如:Allaire Homesite (http://www.allaire.com )。可以使用SDK来做简单的测试,但是对于大的项目可能要困难些。AnywhereYouGo.com已经建立
一套基于Web的工具来帮助WAP测试。
23. 哪儿可以在找到WML的测试工具?

首先确定WML代码是正确的,然后再使用WML测试工具。
有一个非常好的测试工具在Zygo Communications(http://wap.z-y-g-o.com/tools/),测试工具是用Perl写的。里面还有其他的工具可供下载。

24. 如何操作WML页面?

操作WML页面或者卡片,最简单的办法是通过现有的网关。大多数移动电话提供者将功能都放在主页上,在上面可以通过WAP设备操作。网关的链接一般叫做“Go to URL”。当选择以后,WAP设备将通过网关
操作指定的普通IP或者URL。在这种情况下,网关读取从WAP设备发送给网关的WML内容,就像PC浏览器读取内容的过程一样。
有些营运商选择不让他们的用户操作其他的站点。这个就像Internet Service Provider只允许用户操作ISP自己的站点。像这样的做法是不明智的,这样会发现自己的用户去其他地方了。
如果要坚持这种方法,可以通过ISP拨号或者使用一个公共的网关来取得其他的WAP资源。

25. 有没有一个友好的方式来管理WML内容?

还没有。虽然Oracale正在开发数据库驱动的文档服务,被称为Panama,可以支持WAP分发。

26. 如何防止用户代理cache页面?

如果用户使用ASP,应该加入一行<%Response.expires=-1%> ,这个将阻止Cache。

27. 怎样防止从Cache中读取WML页面?

当WML页面下载到WAP设备后,它将保存在WAP设备内存中一段时间,直到这个时间过期。在这之后,页面将从服务器下载,而不是从WAP设备的缓存读取。这个过程被称做Cache。
但是有些时候不想让页面从缓存中读取,而是从服务器端读取。一个典型的例子就是当服务器的内容不断在更新的时候,通过在HTTP头中加入一定的cache信息,来告诉WAP设备该页面将不存储在缓存中。
可以在服务器端生成HTTP头,或者使用PHP、ASP、Perl或者其他服务端开发语言。这一行不能被包括在页面里,既然是HTTP的信息头,就不是WML元素。
对于静态页面,或许没有使用服务器端脚本语言,许多浏览器支持META标签来控制浏览器的Cache。看本部分的最后的例子。
将下面代码加入到HTTP头中,页面将马上过期:

Expires: Mon, 26 Jul 1997 05:00:00 GMT
Last-Modified: DD. month YYYY HH:MM:SS GMT
Cache-Control: no-cache, must-revalidate
Pragma: no-cache
第一行告诉微型浏览器,页面已经过期一段时间了。第二行告诉浏览器页面最后一次修改的时间。DD应该换成当天的日期,month YY HH MM SS等等类推。第三行和第四行有同样的效果。告诉浏览器页面不被
Cache(第三行适用于 HTTP 1.1,第四行适用于HTTP 1.0)。
下面的是PHP的一个例子:

<?
// set the correct MIME type
     header("Content-type: text/vnd.wap.wml");
// expires in the past
     header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
// Last modified, right now
     header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); 
// Prevent caching, HTTP/1.1
     header("Cache-Control: no-cache, must-revalidate");
// Prevent caching, HTTP/1.0
     header("Pragma: no-cache");
   ?>

下面是使用WebClasses(VB)的例子。使用"Response.Expires=-1",防止Cache。

 Private Sub WebClass_Start()
      'Set correct MIME type
      Response.ContentType = "text/vnd.wap.wml"
     
      'Make sure no caching
      Response.Expires = -1
      Response.AddHeader "Pragma", "no-cache"
      Response.AddHeader "Cache-Control", "no-cache, must-revalidate"
   
      'Use basicwml(my own) as template
      Set NextItem = basicwml
  End Sub 

这里有一个ASP的例子,同样使用“Response.Expires=-1”防止Cache。

<%
    Response.ContentType = "text/vnd.wap.wml"
    Response.Expires = -1
    Response.AddHeader "Pragma", "no-cache"
    Response.AddHeader "Cache-Control", "no-cache, must-revalidate"
%> 

最后是使用META的例子:

<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
  <wml>
    <head>
      <meta forua="true" http-equiv="Cache-Control" content="max-age=0"/>
    </head>
    <card id="alwaysexpire">
      <p>This deck will never be stored in the cache</p>
    </card>
  </wml>

下面的页面是在经过86400秒(24 hours)后过期。

<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
  <wml>
    <head>
      <meta forua="true" http-equiv="Cache-Control" content="max-age=86400"/>
    </head>
    <card id="expire1day">
      <p>This card will live in the cache for a day</p>
    </card>
  </wml>

有些浏览器例如:UP.Simulator如果可以通过“返回”达到另外一个卡片,那么它将不会重新装载卡片。为了强制这个更新动作,用户必须在META标签中使用must-revalidate 参数。

<meta forua="true" http-equiv="Cache-Control" content="must-revalidate"/>

28. 如何防止变量被保存在Cache中?

变量保存在Cache中,这样变量还可以再利用。例如当用户返回到上一个输入卡片,他不需要重新输入,只需要改变需要改变的地方。但是在某些情况下这会造成一些问题。例如以WAP聊天系统,有些变量用了一遍又一
遍,但是需要不同的内容。有些浏览器,例如:Nokia 7110,就会存在类似的在该清除的时候无法清除的问题。
在WML中,<card>标签有一个参数叫做newcontext。
当newcontext="true" 时清除所有的变量。但是这样也清除了所有导航的历史记录,这意味着back按钮不再工作。
为了清除变量,可以告诉浏览器将变量设为空:

<setvar name="one_variable" value=""/>
<setvar name="another_variable" value=""/>

但是,不是每个时候都有效果。在某些情况下必须使用一个难以想象的方法来清空变量。就是使用 onenterforward 事件。

<onevent type="onenterforward">
   <refresh>
     <setvar name="one_variable" value=""/>
     <setvar name="another_variable" value=""/>
   </refresh>
</onevent>
29. 怎么能够知道请求是从WML浏览器来的还是HTML浏览器来的?

既然要利用已经存在的为HTML浏览器编写的代码,就需要知道请求是从HTML浏览器还是从WML浏览器过来的。同样地,如果想重新引导的HTML浏览器直接到相应的HTML文档上,WML浏览器到WML页面上
,以下的PHP代码就可以做到这些。

<?
// Because this script sends out HTTP header information,
// the first characters in the file must be the <? PHP tag.

// relative URL to your HTML file
   $htmlredirect = "/html/my_htmlpage.html";
// ABSOLUTE URL to your WML file 
   $wmlredirect = "http://wap.mysite.com/wml/my_wmldeck.wml";

   if(strpos(strtoupper($HTTP_ACCEPT),"VND.WAP.WML") > 0)
{// Check whether the browser/gateway says it accepts WML.
     $br = "WML";
   }
   else {
     $browser=substr(trim($HTTP_USER_AGENT),0,4);
     if($browser=="Noki" || // Nokia phones and emulators
        $browser=="Eric" || // Ericsson WAP phones and emulators
        $browser=="WapI" || // Ericsson WapIDE 2.0
        $browser=="MC21" || // Ericsson MC218
        $browser=="AUR " || // Ericsson R320
        $browser=="R380" || // Ericsson R380
        $browser=="UP.B" || // UP.Browser
        $browser=="WinW" || // WinWAP browser
        $browser=="UPG1" || // UP.SDK 4.0
        $browser=="upsi" || // another kind of UP.Browser ??
        $browser=="QWAP" || // unknown QWAPPER browser
        $browser=="Jigs" || // unknown JigSaw browser
        $browser=="Java" || // unknown Java based browser
        $browser=="Alca" || // unknown Alcatel-BE3 browser (UP based?)
        $browser=="MITS" || // unknown Mitsubishi browser
        $browser=="MOT-" || // unknown browser (UP based?)
        $browser=="My S" || // unknown Ericsson devkit browser ?
$browser=="WAPJ" || // Virtual WAPJAG www.wapjag.de
$browser=="fetc" || // fetchpage.cgi Perl script from www.wapcab.de
$browser=="ALAV" || // yet another unknown UP based browser ?
        $browser=="Wapa") // another unknown browser (Web based "Wapalyzer"?)
        {
        $br = "WML";
     }
     else {
       $br = "HTML";
     }
   }

   if($br == "WML") {
// Force the browser to load the WML file instead
    header("302 Moved Temporarily");
    header("Location: ".$wmlredirect);
    exit;
   }
   else {
// Force the browser to load the HTML file instead
    header("302 Moved Temporarily");
    header("Location: ".$htmlredirect);
    exit;
   }
  ?> 

这个判断是在服务端完成的, PHP代码将首先查看网关是否接收text/vnd.wap.vml MIME类型。如果不是,将检测前面的字符,查看是否为WML浏览器。如果不符合,那么就假设为HTML浏览器
。如果有新的WML浏览器,那么ID字符串也要增加。
这个代码基于Robert Whitinger(robert@wapsight.com)的代码,使用了Don Amaro(donamaro.concepcion@nl.unisys.com)提供的列表

注意:由于只需要四个字符串就可以辨别,因此例如:"WapIDE-SDK/2.0;(R320s(Arial))" 可以使用“WapI”来代替是可行的做法,也是足够的。
同样的功能也可以通过ASP来解决。先判断请求的是“/index.wml” 或者 “/index.html” 和所需要的MIME类型。另外以下的脚本辨别的方式和上面不一样。另外还需要网关告诉服务器它能
接收 的text/vnd.wap.wml MIME类型。该例子如下所示:

<%
Response.Buffer = TRUE
  Dim IsWap
  httpAccept = LCase(Request.ServerVariables("HTTP_ACCEPT"))
  if Instr(httpAccept,"wap") then
  IsWap=1
  Else Response.Redirect "/index.html" : Response.Flush : Response.End
End if
%>
<%Response.ContentType = "text/vnd.wap.wml"%><?xml version="1.0"?>
<%Response.Flush%>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
  <wml>
  <card id="redirect">
  <onevent type="onenterforward">
  <go href="/index.wml"/>
  </onevent>
  <p>
  <a href="/index.wml">enter</a>
  </p>
  </card>
  </wml>
  <%Response.Flush:Response.End%>

30. 如何判断访问者是来自哪个浏览器或者移动电话?

可以通过检查HTTP_USER_AGENT标签来判断。例如试着使用Microsoft Internet Explorer访问一个站点的时候,HTTP_USER_AGENT将返回:Mozilla/4.
0 (compatible;MSIE 5.0; Windows 98);在同样的情况下使用Nokia 7110访问这个站点,HTTP_USER_AGENT就会是:Nokia7110/1.0(04.73
)。据此可以判断用户代理是什么类型的。

31. 可以得到用户代理的电话号码吗?

不可以,除非网关支持这个特点,WAP没有办法知道用户的电话号码。

32. 可以通过WML使得可以用WAP设备进行拨号吗?

WAP的电话功能可以使用Wireless Telephony Application Interface(WTAI)。

例如:

WMLScript: WTAPublic.MakeCall("9287787"); 

但是第一代的WAP设备不支持这个功能。

33. 能够从WAP设备中读取数据吗,例如:电话号码?

这里有一些通过HTTP的信息,但是十分有限。既然只有网关发送过来少量的信息,像WAP设备的号码可能无法读取。同时,在某些国家这还涉及到个人隐私的问题。
基本上丢弃的内容就是WAP网关传送给HTTP服务器的内容。这不同于WAP网关到网关。Phone.com的UP.Link网关是一个最好的例子。因为它在HTTP头中返回一个字符串叫做 UP_X_SUBN
O,里面包含了电话号码。Ericsson网关将传送一个辨别设备用的字符串,但是在明文中没有电话号码。
每次WAP设备向HTTP服务器请求一个URL,WAP网关就会将信息传送给HTTP服务器。
以下的PHP脚本显示了从网关过来的所有HTTP头的信息。可以使用WML浏览器进行测试。(http://wap.colorline.no/clientinfo.html)。其他的例子也可以在下面的UT
L中找到:http://wap.colorline.no/demos.html
第一个部分是取得所有的标准HTTP头信息。第二个部分是提取一个内容。

<?
  header("Content-type: text/vnd.wap.wml");
  echo("<?xml version=\"1.0\"?>\n");
echo("<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\"
\"http://www.wapforum.org/DTD/wml_1.1.xml\">\n\n");
  echo("<!—Code written in Microsoft NOTEPAD.EXE à \n");
?>

<wml>
  <card id="init" title="Client Info">
    <p>
      <?
        // First part – standard HTTP stuff
        $headers = getallheaders();
        while (list($header, $value) = each($headers)) {
          echo strtoupper($header). ": ". $value. "<br/>\n";       
        }
        // Second part

// IP address of the client side
        echo("REMOTE_ADDR: ".$REMOTE_ADDR. "<br/>\n");
// Port at the client side
        echo("REMOTE_PORT: ".$REMOTE_PORT. "<br/>\n");
// Name of authenticated user
        echo("REMOTE_USER: ".$REMOTE_USER. "<br/>\n");
// Gateway Interface type
        echo("GATEWAY_INTERFACE: ".$GATEWAY_INTERFACE. "<br/>\n");
// Protocol used by the server
        echo("SERVER_PROTOCOL: ".$SERVER_PROTOCOL. "<br/>\n");
// Request Method
        echo("REQUEST_METHOD: ".$REQUEST_METHOD. "<br/>\n");
// Connection type
        echo("HTTP_CONNECTION: ".$HTTP_CONNECTION. "<br/>\n");
// Host it connected via (proxy)
        echo("HTTP_VIA: ".$HTTP_VIA. "<br/>\n");
      ?>
    </p>
  </card>
</wml> 
Henrik Gemal (gemal@dk.net)也有一个在线的基于WML的工具BrowserSpy,来显示更多关于HTTP头的信息、服务器环境和用户的浏览器等等。有关这个工具的详细情况可以浏览h
ttp://wap.gemal.dk/
Werner Forkel 提交了一个Perl的脚本,可以显示电话号码(如果有)。可以在以下位置测试:http://wap.colorline.no/wap-faq/apps/subnotest.w
ml,同样也收集在:http://wap.colorline.no/demos.html.
这些程序只适合某个网关。如果要测试其他的网关,可能就显示不出电话号码。因此电话号码不能作为ID号来处理。至少因为不是一个全球的标准。 34. 有没有办法连接到电话号码?

在某些情况下,当在显示了一连串的号码之后,需要中断功能连接到一个电话号码上并拨号。例如:执行一个 dial:12345678 就非常像 mailto: 标签。
越来越多的浏览器都支持这个功能,但还不是所有。Phone.com, Mitsubishi 和 Ericsson 已经在浏览器中集成了这个基于Wireless Telephony Interface
specifications (WTAI)的功能。 WTAI将允许以下的URL将关闭连接并且拨号:

  <go href="wtai://wp/mc;+4712345678">Make a call to +47-12345678</go> 

Nokia 7110 已经有个功能叫做“Use Number”。它可以通过WML卡片查找一个类似于电话号码的列表,然后用户可以选择进行呼叫。注意用户必须分离这些数字以便它能正常工作。

35. 使用GET或者POST方式能传送多少字符?

使用GET或者POST方式所能传送的字符数目,不同的设备有不同的限制。一个GET通过UTL传送变量,能传送的数据总量比使用POST方式所能传送的数据要小。例如,Nokia 7110似乎对每个GET 
限制在512个字节左右,但是POST最大可以达到一个编译后卡片的大小(约1300字节)。UP.SDK 4.0将GET请求限制在970左右,最大可以达到一个编译后卡片的大小。
显然,卡片有时候保存了要发送给服务器的参数的内容,既然编译后的卡片大小有限制,那么肯定要影响到整个所能传输的数据。
在POST和GET之间没有太多的区别。比如这个没有很好地使用GET的例子。

<input type="text" name="var1" format="*N"/>
   <p>
     <anchor>Send it
     <go href="somescript.cgi?variable=$(var1)" method="get"/>
     </anchor>
   </p>

下面仍然是一个使用GET的请求,但是使用了<postfield>来传送参数。这个代码就漂亮多了。既然可以定义为GET,同样也很容易转成POST。

<input type="text" name="var1" format="*N"/>
   <p>
     <anchor>Send it
     <go href="somescript.cgi" method="get">
       <postfield name="variable" value="$(var1)"/>
     </go>
     </anchor>
   </p>

直接改为POST:

<input type="text" name="var1" format="*N"/>
   <p>
     <anchor>Send it
     <go href="somescript.cgi" method="post">
       <postfield name="variable" value="$(var1)"/>
     </go>
     </anchor>
   </p>

最好是做测试找到到底能传输多少数据。这里有个测试程序: http://wap.colorline.no/wap-faq/apps/putsize.php3。

这个程序也可以在下面的URL中找到:http://wap.colorline.no/demos.html。
该程序将产生一个卡片包含一个变量,里面包含了一定数量的字符X。用户可以选择传输是使用GET还是POST。在传输之后,脚本将要显示接收到的字符个数。
脚本生成一个页面来测试使用GET或者POST方式到底能发送多少个字符:

<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
  <head>
  <meta forua="true" http-equiv="Cache-Control" content="max-age=0"/>
  <meta forua="true" http-equiv="Cache-Control" content="must-revalidate"/>
  </head>
  <card>
  <do type="prev" label="Back">
  <go href="putsize.php3"/>
  </do>
  <p>
  <anchor>GET data
  <go method="get" href="putsize.php3">
  <postfield name=\"a\"
value="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"/>
  </go>
  </anchor>
  </p>
  </card>
</wml>

36. 如何同HTML站点一样POST/CGI,返回表单数据到服务器?

如果使用:

<go method="post" href="mycgi.cgi"> 

并且使用:

<postfield name="fieldname" value="$NameOfInputField"/> 

就可以POST数据给CGI程序了。

37. POST无法工作是怎么回事?

有很多说POST参数将会丢失的流言,特别是在Nokia 7110。就笔者所知,还没有哪个Nokia 7110有这样的问题。这个问题主要是存在于网关或接收方。
测试显示Nokia SDK 2.18,当使用内建网关的时候,使用POST会出现问题。甚至当method 设置成“POST”的时候,服务器那边还是将POST请求作为 GET。
当使用POST的URL时 ,Nokia SDK 将会崩溃。在某些情况下URL的最后的字符将被删除。
POST Test页面将简单的POST的两个变量叫做“var1”和“var2”来显示整个变量的内容和HTTP头的内容。如果不能看到正确的变量内容,肯定有问题。检查HTTP头中的application
/x-www-form-urlencoded(注意!需要在变量中输入一些内容)。
这个方法解决了Nokia SDK 2.18的问题,可以把它配置到任意的公共网关来测试。笔者推荐使用 wapHQ 网关。
在其他的情况下,POST确实不工作,问题可能是HTTP头在服务器端解释的时候有问题。脚本语言,例如:ASP、Java或是CGI等等都是通过查看在HTTP头中的 application/x-www-f
orm-urlencoded 完全匹配的字符串。在某些情况下字符串可能有附加的数据,例如:charset="utf8" 。既然服务器端不是精确的匹配,它就不会查看HTTP头,因此POST就变量丢失了。

注意这不是浏览器的问题,在HTTP头加入字符集描述,将造成脚本语言方面的错误。
为了检测有关网关或浏览器的问题,仍使用上面的POST Test页面来测试。同样查看application/x-www-form-urlencoded 的输出,检查有没有附加的字符在结尾部分,如果有,
那么这就是服务器端的问题。
解决这个问题的方案很复杂,它随用户使用的脚本描述语言不同而不同,而且需要操作原代码。简单地说,解决方案就是需要人工读取HTTP头,不要使用脚本语言已经写好的读取方式。
这里有一个用ASP编写的解决方法。它显示了如何在POST中抓取数据。用户需要从二进制数据中发现需要的变量。

Dim lngToalByteCount
Dim vntRequestData

  lngTotalByteCount = Request.TotalBytes
  vntRequestData = Request.BinaryRead(lngTotalByteCount)

全部的代码,就应该像下面的代码:

<%@ Language=VBScript %>
<%
  Dim Temp, i, sPost, sWMLDeck

  'Converts the binary data to a string.
  For i = 1 To Request.TotalBytes
    Temp = Request.BinaryRead(1)
    sPost = sPost & Chr(AscB(Temp))
  Next

  'Parses out the values of the POSTED variables (in this
  'example myvar1 and myvar2).
  Dim sVar1, sVar2
  sVar1 = getVar("myvar1", sPost)
  sVar2 = getVar("myvar2", sPost)

  'Writes the WML Deck displaying the POSTED Variables
  sWMLDeck = "<?xml version=""1.0""?>" & vbCrLf
  sWMLDeck = sWMLDeck & "<!DOCTYPE wml PUBLIC ""-//WAPFORUM//DTD WML 1.1//EN"" "
  sWMLDeck = sWMLDeck & """http://www.wapforum.org/DTD/wml_1.1.xml"">" & vbCrLf
  sWMLDeck = sWMLDeck & vbCrLf & "<wml>" & vbCrLf & vbTab
  sWMLDeck = sWMLDeck & "<card id=""main"" title=""POST TEST"">" & vbCrLf
  sWMLDeck = sWMLDeck & vbTab & vbTab & "<p>" & vbCrLf
  sWMLDeck = sWMLDeck & vbTab & vbTab & vbTab & "myVar1: " & sVar1 & "<br/>" & vbCrLf
  sWMLDeck = sWMLDeck & vbTab & vbTab & vbTab & "myVar2: " & sVar2 & vbCrLf
  sWMLDeck = sWMLDeck & vbTab & vbTab & "</p>" & vbCrLf & vbTab
  sWMLDeck = sWMLDeck & "</card>" & vbCrLf & ">/wml>"

  Response.ContentType = "text/vnd.wap.wml"
  Response.Write(sWMLDeck)

  'Quick function for picking out the values of the POSTed variables.
  'sKey is the variable name, sRaw is the POST string.
  Private Function getVar(sKey, sRaw)
  Dim sRetVal
If InStr(sRaw, sKey) Then
sRetVal = Mid(sRaw, InStr(sRaw, sKey) + Len(sKey) + 1)
If InStr(sRetVal, "&") Then
sRetVal = Mid(sRetVal, 1, InStr(sRetVal, "&") - 1)
End If
End If
getVar = sRetVal
  End Function
%>

38. 为什么META标签不工作?

浏览器不支持默认的meta标签,例如:

<meta http-equiv="refresh" content="1;http://somewhere.com/"> 

虽然有少量网关支持非常有限的META标记。但是测试显示,如果使用了它们,网关就会出问题。例如某网关不支持普通的HTTP Cache控制,如果要实现Cache控制只好使用特殊的META标记。显然从其他
网关来的用户就可能不支持这个META。注意:不要使用META tags。肯定有其他的方式来完成你的想法。

最常使用的META是:

<meta http-equiv="refresh" content="1;http://somewhere.com/"> 

这个告诉浏览器重新装入指定的WML页面。WML中已经包含了一个<ontimer>。

39. 为什么服务器接收不到用户发送的参数?

用户输入的参数或者其他参数可以像在HTML中一样通过提交方式发送到服务器。在HTML中这个是<FORM>,POST或者GET。
首先知道要知道POST和GET的区别。对于POST浏览器将生成一个数据包将变量名和它们的内容捆绑在一起,并发送到服务器。对于GET,它其实是一个URL请求,变量名和内容都包含在URL中。
对于WAP环境,要求是非常严格的,必须要根据协议来操作。虽然以下的URL

"/cgi-bin/somescript?username=john&telephone=123-123-1234&occupation=banana+bender"

可以在HTML环境中工作,但是在WAP环境中无法工作。以上的部分编码将使得保护的变量内容被误解。特殊的空格(在 banana 和 bender )被转成 “+”。 URL就根本没有空格。
以上的URL在WAP中无法工作的主要原因是用来分割每个变量和变量内容的 & (与号)没有转义。正确的格式应该是:

"/cgi-bin/somescript?username=john&amp;telephone=123-123-1234&amp;occupation=banana+bender"

在这里 & 被名字实体所替换。为了解释更清楚些,请看下面的代码:

<card id="input" title="Gimme some data">
  <p><input type="text" name="username" format="M*m"/></p>
  <p><input type="text" name="occupation" format="M*m"/></p>
<p>
<anchor>Send this
<go href="/cgi-bin/somescript?username=$(username)
&amp;occupation=$(occupation)"/>
</anchor>
</p>

注意这不是真正的WAP协议,专门的字符应该转义,否则将得到不可预料的结果。

40. 为什么在HTTP中的Referer看不见?

当HTML浏览器从一个URL到另外一个URL的时候,它默认地会发送一个叫做 Referer的 HTTP头给服务器,告诉它在浏览这个页面之前的那个页面。这是一个非常有用的特点,在WAP环境中同样也有。
但是既然这个信息来自用户代理(浏览器)、WAP设备,通常为了节约带宽和时间,就被省略了。
为了使用 Referer ,应该使用新的URL标签例如: <a>,<go>等等,并且加入参数:sendreferer。

<go href="/somedir/somedeck.wml" sendreferer="true"/> 

这样就会把参考的URL发送到服务器。

41. 如果没有找到URL,有可能重新将用户引导到另外一个WML卡片或者页面吗?

是的。但这是服务器端的特点,与客户端没有关系。

42. 为什么普通的HTTP 302重新导向不好使?

这的确是一个事实。核心的问题是在服务端的脚本语言,而不是在服务端语言和服务器之间。
所谓的302 Found HTTP反应是指服务器告诉用户代理,它所需要的资源在另外的地方可以找到。302反应可能包括一个人们可理解的信息,如果在这种情况下“ Content-type: ”就被设置了
。笔者所测试过的服务器,即使没有内容也都加了“Content-type:”。默认的 “Content-type:” 是text/html。当然有些网关不喜欢这个类型。
以下的例子已经经过测试,在Apache和Microsoft Internet Information Server都可以工作。如果使用其他的Web Server,或者其他的脚本语言,需要能转换这些简
单的脚本。关键的工作是十分简单的,除非需要,不用告诉服务器整个HTTP头。大多数Web Server将自动完成这个HTTP头,使得用户代理可以理解。
所有的代码例子可以在线测试。如果它们能够工作,用户将被重新引导到http://wap.colorline.no/clientinfo.html ,在那儿将产生一个WML页面来显示所有的HTTP头。
PHP 代码测试可以在 http://wap.colorline.no/wap-faq/apps/302test.php3中找到。

<?
    header("Location: http://wap.colorline.no/clientinfo.html");
    header("Content-type: text/vnd.wap.wml";
?> 

Perl测试代码可以在http://wap.colorline.no/cgi-bin/302test.pl中找到。 

print "Location: http://wap.colorline.no/clientinfo.html\n";
print "Content-type: text/vnd.wap.wml\n"; 

ASP测试代码可以在 http://www.colorline.no/302test.asp中找到。 (注意不同的URL): 

<%
    Response.Redirect = "http://wap.colorline.no/clientinfo.html";
    Response.ContentType = "text/vnd.wap.wml";
    Response.Flush
    Response.End
%>

43. 可能在WML中实现ASP Session吗?

不可能。可以把信息存储在临时变量中模拟Session。Session是保存在用户PC上的“cookies”中。目前的WAP设备不支持“cookies”。不过下一代的设备和WML可能支持“cookie
s”。

44. WAP支持Session吗?

在HTML中,一个十分普遍的“处理”用户的方法就是为每个用户分配一个“session”。这个有时候是通过指定一个独一无二的cookies来实现的。然而WAP的资源非常有限,因此session的处理必
须以不同的方式来处理。
Alex Kriegel 提供了一个安装在 WAPlinks的Custom Session Object包。这个zip文件中包含了VB类的文件和编译过的Dll文件,还有相关的文档。这些可以在http
://www.waplinks.com/customsessionobject.zip下载。
另外一种方法是使用 PHPlib ,它是使用 PHP 编写的。
Tarique (tarique@nagpur.dot.net.in) 提供了如何使用PHPlib来验证和处理每个WAP用户。有相关的文件和注释可以到下面地址下载:

http://wap.colorline.no/wap-faq/archive/phplib_wml.zip
45. 可以在WAP中使用Cookies吗?

在理论上是可以的,但不是所有的WAP设备都支持。另一个方法来管理会话是使用隐藏的fields(包含会话标识,无论是POST或者GET方式)。

46. WAP支持Cookies吗?

普通的HTTP Cookies是作为WAP的扩展来实现的。无论你以前听到什么,Cookies的支持将越来越好。实际上Phone.com的 UP.Link网关已经支持这个功能有一段时间了。
可以使用以下的脚本语言检测Cookie-support,:
http://wap.colorline.no/wap-faq/apps/cookietest.php3
脚本在http://wap.colorline.no/demos.html也可以得到。
当第一次看见卡片的时候,记数器应该为0。所有的Cache都被关闭。卡片通过在URL中随机地加入变量来强制自己加载(笔者不推荐这种强制加载办法)。当点击增加计数连接,页面将重新加载,卡片就再次出现,并
且记数器变成1。
在脚本中,Cookie的名字被称做 TestCookie,它有很长的生命期,因此可以隔好几天再来查看记数器,它将是上一次的数值。这要求你能使用与上一次访问所使用的WAP环境一样,否则将把你的数值清0

如果记数装置一直都是0,那么cookie 就没有能传送到你的Web Server。这个脚本也能表示Cookie是否被传送。
另外,这个脚本同样还显示HTTP头中的HTTP_VIA 和 HTTP_USER_AGENT 。这些能够得到所使用的网关和模式。一些网关使用HTTP_VIS标识自己,而另外一些使用HTTP_USER_
AGENT,还有一些则让程序无法知道。
下面是它的PHP代码。一个标准的 PHP setcookie() 函数只有在这种脚本语言中才会出现。函数只是简单地设置cookie,并且PHP变量 $HTTP_COOKIE_VARS 用来读取coo
kie。

<?
if(isset($HTTP_COOKIE_VARS["TestCookie"]))
{// Check if TestCookie is set
      $cookieset = "set";

// Read the Cookie
      $cookieid = $HTTP_COOKIE_VARS["TestCookie"];
  // and increase its value
      $cookieid++;
     }
    else {
  // cookie was not set
      $cookieset = "not set";
  // start counter at zero
      $cookieid = 0;
    }
  // apply the Cookie to the HTTP header
 setcookie("TestCookie",$cookieid);
  // set the content type for WML
    header("Content-type: text/vnd.wap.wml");

// disable ALL caching
    header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
    header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
    header("Cache-Control: no-cache, must-revalidate");          
    header("Pragma: no-cache");                                  
    echo("<?xml version=\"1.0\"?>\n");
 echo("<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\"
 \"http://www.wapforum.org/DTD/wml_1.1.xml\">\n\n");
 echo("<!-- This application attempts to test the capabilities of a WAP gateway to support     cookie
s -->\n");
    echo("<!-- App by Espen.Lyngaas@colorline.no (c) 2000 -->\n");
  // Generate random value for reload forcing
    $random = mt_rand(100000,999999);
 ?>
  <wml>
  <head>

// Even more cache disabling
<meta forua="true" http-equiv="Cache-Control" content="must-revalidate"/> 
  </head>
  <card id="init" title="CookieTest">
  <p>
   Cookie "TestCookie" was <?echo($cookieset)?>.
   Value is currently "<?echo($cookieid)?>"
  </p>
  <p>

<anchor>
Increase value
<go method="get" href="<?echo($PHP_SELF)?>?random=<?echo($random)?>"/>
</anchor>
</p>
<p>Gateway: 
  <?
    if(isset($HTTP_VIA))
{ // Is there something in the HTTP_VIA variable?
      echo($HTTP_VIA);
    }
    else {
      if(isset($HTTP_USER_AGENT))
{ // Is there something in the HTTP_USER_AGENT variable?
        echo($HTTP_USER_AGENT);
      }
      else {
// Absolutely no identifier was found
        echo("Unknown");
      }
    }
  ?>
  </p>
  </card>
  </wml>
47. 如何使用WAP设备发送E-Mail?

在HTML中有一个默认的E-Mail机制:“ mailto:” 。但在WML中不好使,因此E-Mails必须通过WML表单来解决。例如:

<wml>
     <card id="edit" title="Email Editor">
      <p>From: <input type="text" name="from" format="*M"/></p>
      <p>To: <input type="text" name="to" format="*M"/></p>
      <p>Subject: <input type="text" name="subject" format="*M"/></p>
      <p>Message body: <input type="text" name="body" format="*M"/></p>
      <p>
        <anchor>Send this mail
          <go method="post" href="http://some.host/mailhandler"?action=send/">
            <postfield name="from" value="$(from)"/>
            <postfield name="to" value="$(to)"/>
            <postfield name="subject" value="$(subject)"/>
            <postfield name="body" value="$(body)"/>
          </go>
       </anchor>
      </p>
   </card>
</wml> 

在代码中的http://some.host/mailhandler是一个CGI程序,它是服务端的脚本程序,将提交的表单转换成E-Mail格式并发送出去。
如果想使用一个类似于发信的过程,就需要编辑变量名。另外发送的数据是有限的,长信息可能需要打断。
为了演示它是如何工作的,下面的 PHP 脚本可以用来处理Mail。它将格式化WML页面,告诉用户是否发出信件。在真实的应用中,应该加入检测,例如:E-Mail的合法格式。

<?
// Tell the client that this is a WML deck
    header("Content-type: text/vnd.wap.wml");
    echo("<?xml version=\"1.0\"?>\n");
    echo("<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\"
\"http://www.wapforum.org/DTD/wml_1.1.xml\">\n");
// The name of your mail server
    $mailer = "wap.colorline.no";
// Format the from field
    $from = $from." (WAP user at ".$mailer.")";

// Add the from field and some character handling to the extra headers
  $extraheaders = $from."\nContent-Type: text/plain;
charset=iso-8859-1\nContent-Transfer-Encoding: 8bit";

// Start sending out the WML deck
    echo("<wml>\n");
    if(mail($to,$subject,$body,$extraheaders))
{// Use PHP's internal mail functionality
// Mail was successfully sent
      echo("<card id=\"sent\" title=\"Mail sent\">\n");
      echo("<p>Mail was sent successfully</p>\n");
      echo("</card>\n");
    }
    else {
// The mail could not be sent
      echo("<card id=\"notsent\" title=\"Mail failed\">\n"); 
      echo("<p>Unable to send mail</p>\n");
      echo("</card>\n");
    }
    echo("</wml>\n");
?>

因为安全性的原因,以上的代码没有演示程序。

48. 可以在模拟器上操作本地的页面,却没有办法访问Internet上的,有什么问题吗?

大多数模拟器和工具都可以浏览Internet、Intranet和本机的页面。但是在访问一些大公司页面的时候,必须通过代理服务器来取得进入Internet的权限。如果必须通过这个代理服务器来取得HTM
L页面,那么你的WAP模拟器也会取得权限来访问Internet。
注意到有些模拟器不支持代理服务器,但是大多数是支持的。在模拟器里面设置这些是很简单的。用户所做的只需要将代理主机的名字、IP地址和端口号输入就可以了。如果没有找到,你可以在 systems/netw
ork 管理器里面设置这些,也可以检查 Netscape/IE的设置。
在某些情况下,代理服务器使用 userid 和 password 来取得进入Internet的权限。有些模拟器支持用户代理服务器,用户应该能告诉模拟器相关的代理配置。
在极少的情况下,使用代理服务器(如Microsoft Proxy Server,)的用户会遇到更多的问题。这个代理服务器只接受一种验证方式(userid/password)。这种验证被称做 NTLM
,并且是某种 MS 的验证方式。几乎很少有模拟器支持这种方式。因此最好是避免使用它,或者让管理员使用“Basic Authentication”方式以避免更多的麻烦。

49. 什么是PUSHing,它是如何工作的?

PUSH被加入到WAP 1.2,而且只在WAP 1.2中才存在。简单地来说,PUSH提供了另外一种从服务器向用户发送数据的方式。PULLing是从客户端请求信息,然后接收它;PUSH意味着服务器可以
向用户发送数据,而不需要用户来请求。
内容或者应用服务器无法向用户代理直接发送数据,必须使用一种叫做Push Proxy 的网关。PPG 是基于Internet的Push Initiator (内容或者应用服务器) 与移动用户之间的。在
Internet一边,使用Push Access Protocol,在移动网络一边使用Push Over-the-Air Protocol。
当前只有 WAP 1.2 开发平台支持 PUSH, 例如 Nokia Toolkit 2.0。 Nokia Toolkit 2.0 only 内部支持PUSHing,意味着用户可以从工具包的界面将消
息推送到模拟器。如果想试着到一个外部的Push Proxy Gateway, 工具包就崩溃了。从readme文件中知道,PUSHing 还没有经过完整的测试。

50. WAP模拟器说text/html不支持,但是用户的MIME设置是正确的,为什么?

当使用服务端的脚本语言,例如ASP、PHP或者Perl,来生成WML输出,或者从HTTP服务器提供WML页面的时候。记住HTTP一般默认的显示是HTML,其MIME类型是text/html。
如果HTTP服务器或者服务器脚本有错误,错误的信息将使用HTML显示,因此微型浏览器是不能显示错误信息的。
一个开发工具/模拟器可以让用户看到从HTTP服务器过来的代码。例如,在Nokia SDK中,这个功能被称做View Source。通过看代码可以知道HTTP服务器到底发送了些什么内容。也可以使用普通
的浏览器来查看任何HTML格式的错误信息。

51. 在哪儿有Visio移动电话的模板库?

目前唯一知道的就是它包含在 Nokia 7110 中。

52. 有没有其他有用的WML内容服务列表?

这里至少有一个。
对于Unix用户, http://pwot.co.uk/wml/中有Thomas Neill (ponder@pwot.co.uk)提供的WML工具,包括WML二进制编译和反编译。
Angus 和 Zygo WAP(angus@z-y-g-o.com)已经开发出了一个Perl工具包。它还在为管道式的WML编译器工作。

53. XML到XSL的转换可以应用到WML和WAP吗?

既然WML实际上是XML,并且XSL将WML转换成其他不同的XML文档,那么问题的答案是显然的:XSL也可以应用到WML。可以参考Luca Passani的文章《WebTechniques》。这个文
章在网络上的地址是:
http://www.webtechniques.com/archives/2000/03/passani/
它推荐看一下叫做《在 Apache下Cocoon计划的实现》这篇文章。“Cocoon 是一个依赖于新的W3C技术(例如DOM,XML,和XSL)框架。Cocoon计划在于改变Wen信息创建,生成和提
供的方式。文档内容、风格和逻辑经常因为个人或者工作组的不同而不同。 Cocoon目标在于将这三层分离,允许三层次之间进行独立的设计,创建和管理,减少相互之间的影响,增加工作的可复用性以及缩短上市的时间
。Web内容的产生大多数是基于HTML的,但是HTML并不能将三者分离开来,混合着各种格式标签,程序逻辑等等。而Cocoon计划将要改变这种情况,允许内容,逻辑和风格相互分离。使用XML来保存,但是使
用XSL来将它们混合。”
基本上来说,Cocoon将解读HTTP头,判断使用的是什么浏览器,然后使用不同的风格来选择正确的页面,使用XSL进行混合。

54. 想让用户只要简单地按下一个按钮就能够转跳到其他卡片而不是通过选择URL,这个可能吗?

不,不可能。

55. 如何避免一个行的中断以便可以在一行中输入多个链接?

在Nokia 7110中,不可能做到这一点,每个链接都占据自己的一行。

posted @ 2005-08-04 17:52 woow 阅读(325) | 评论 (0)编辑 收藏

Windows Vista BT Download BT下载

Microsoft Windows Vista Beta 1

版本号5112。大小约为2.46G。
Build 5112. Size about 2.46G.

原RAR包的文件名:microsoft.windows.vista.codename.longhorn.beta.1.32bit.dvd.read.nfo-winbeta.rar
Filename of the original RAR Package: microsoft.windows.vista.codename.longhorn.beta.1.32bit.dvd.read.nfo-winbeta.rar

Torrent 1
http://static.thepiratebay.org/downloadtorrent/3362103.torrent/Windows_Vista_Beta_1.3362103.TPB.torrent

Torrent 2
http://static.thepiratebay.org/downloadtorrent/3362112.torrent/Microsoft.Windows.Vista.Codename.Longhorn.Beta.1.32Bit.DVD.READ..3362112.TPB.torrent

VS.net 2005 Beta 下载地址(Express Edition)

.NET Framework 2.0 SDK Beta 1 x86(234532 KB):
地址:

http://download.microsoft.com/download/a/a/1/aa1bb223-d4fa-4e1e-80c4-2dc31fbb09dc/setup.exe

DotNET Framework Version 2.0 Redistributable Package Beta 1 (x86) - 24M
http://download.microsoft.com/download/2/d/9/2d96a8eb-a612-46ad-b4ed-6497a8e1f245/dotnetfx.exe

Visual Web Developer 2005 Express Edition Beta - 43.4M
http://download.microsoft.com/download/6/e/1/6e1e9b98-928c-45f0-b44b-39f205090446/vwdsetup.exe

SQL Server 2005 Express Edition Beta - 36M
http://download.microsoft.com/download/0/d/8/0d8bbf1c-6fd8-49dd-81bf-b8d149634bca/SQLEXPR.EXE

Visual C# 2005 Express Edition Beta - 28.9M
http://download.microsoft.com/download/8/c/7/8c784f26-8e95-43b9-90b9-56b511220dfb/vcssetup.exe

Visual Basic 2005 Express Edition Beta - 34.1M
http://download.microsoft.com/download/3/f/5/3f52b37c-92a4-4ce0-9de0-0376165e5461/vbsetup.exe

Visual C++ 2005 Express Edition Beta - 65.3M
http://download.microsoft.com/download/1/f/9/1f9c364a-7835-426f-a12e-238bdf8e3ebf/vcsetup.exe

Visual J# 2005 Express Edition Beta - 32.3M
http://download.microsoft.com/download/f/4/c/f4c0f69b-ced6-46b3-8d5a-855882a1a0a8/vjssetup.exe

posted @ 2005-08-01 19:06 woow 阅读(1249) | 评论 (0)编辑 收藏

如何做好网页色彩的搭配?

 
网页的色彩是树立网站形象的关键之一,色彩搭配却是令人头疼的问题。
网页的背景,文字,图标,边框,超链接...,应该采用什么样的色彩,应该搭配
什么色彩才能最好的表达出预想的内涵呢?阿捷这里谈一些心得,希望对你有所启发。

首先我们先来了解一些色彩的基本知识:

1.颜色是因为光的折射而产生的。
2.红,黄,蓝是三原色,其它的色彩都可以用这三种色彩调和而成。
网页html语言中的色彩表达即是用这三种颜色的数值表示
例如:红色是color(255,0,0)十六进制的表示方法为(FF0000)
白色为(FFFFFF), 我们经常看到的"bgColor=#FFFFFF"就是指背景色为白色。
3.颜色分非彩色和彩色两类。
非彩色是指黑,白,灰系统色。
彩色是指除了非彩色以外的所有色彩。
4.任何色彩都有饱和度和透明度的属性,属性的变化产生不同的色相,所以至少可以制作
几百万种色彩。

网页制作用彩色还是非彩色好呢?根据专业的研究机构研究表明:彩色的记忆效果
是黑白的3.5倍。也就是说,在一般情况下,彩色页面较完全黑白页面更加吸引人。
我们通常的做法是:主要内容文字用非彩色(黑色),边框,背景,图片用彩色。这样
页面整体不单调,看主要内容也不会眼花。

●非彩色的搭配

黑白是最基本和最简单的搭配,白字黑底,黑底白字都非常清晰明了。
灰色是万能色,可以和任何彩色搭配,也可以帮助两种对立的色彩和谐过渡。如果你
实在找不出合适的色彩,那么用灰色试试,效果绝对不会太差。

●彩色的搭配

色彩千变万化,彩色的搭配是我们研究的重点。我们依然需要进一步学习一些色彩
的知识。

一.色环。我们将色彩按"红->黄->绿->蓝->红"依次过度渐变,就可以得到一个色彩环。
色环的两端是暖色和寒色,当中是中型色。(如下图)

红.橙.橙黄.黄.黄绿.绿.青绿.蓝绿.蓝.蓝紫.紫.紫红.红
|___________| |____| |_________| |_________|
|                |        |            |
暖色系          中性系  寒色系       中性系 

二.色彩的心理感觉。不同的颜色会给浏览者不同的心理感受。

红色---是一种激奋的色彩。刺激效果,能使人产生冲动,愤怒,热情,活力的感觉。
绿色---介于冷暖两中色彩的中间,显得和睦,宁静,健康,安全的感觉。
它和金黄,淡白搭配,可以产生优雅,舒适的气氛。
橙色---也是一种激奋的色彩,具有轻快,欢欣,热烈,温馨,时尚的效果。
黄色---具有快乐,希望,智慧和轻快的个性,它的明度最高。
蓝色---是最具凉爽,清新,专业的色彩。
它和白色混合,能体现柔顺,淡雅,浪漫的气氛(象天空的色彩:)
白色---具有洁白,明快,纯真,清洁的感受。
黑色---具有深沉,神秘,寂静,悲哀,压抑的感受。
灰色---具有中庸,平凡,温和,谦让,中立和高雅的感觉。

每种色彩在饱和度,透明度上略微变化就会产生不同的感觉。以绿色为例,
黄绿色有青春,旺盛的视觉意境,而蓝绿色则显得幽宁,阴深。

○网页色彩搭配的原理

1.色彩的鲜明性。网页的色彩要鲜艳,容易引人注目。
2.色彩的独特性。要有与众不同的色彩,使得大家对你的印象强烈。(参考设计思考第二篇
网站CI的标准色彩一节)
3.色彩的合适性。就是说色彩和你表达的内容气氛相适合。如用粉色体现女性站点的柔性。
4.色彩的联想性。不同色彩会产生不同的联想,蓝色想到天空,黑色想到黑夜,红色想到
喜事等,选择色彩要和你网页的内涵相关联。

○网页色彩掌握的过程

随着网页制作经验的积累,我们用色有这样的一个趋势:单色->五彩缤纷->标准色->单色。
一开始因为技术和知识缺乏,只能制作出简单的网页,色彩单一;在有一定基础和材料后,
希望制作一个漂亮的网页,将自己收集的最好的图片,最满意色彩堆砌在页面上;但是时
间一长,却发现色彩杂乱,没有个性和风格;第三次重新定位自己的网站,选择好切合自己
的色彩,推出的站点往往比较成功;当最后设计理念和技术达到顶峰时,则又返朴归真,用
单一色彩甚至非彩色就可以设计出简洁精美的站点。

○网页色彩搭配的技巧
文章写到这里,有心急的网友要问了:“到底用什么色彩搭配好看呢?你能不能推荐几
种配色方案?”别急,这里有一点技巧,可以帮助你迅速成为调色大师:)

1.用一种色彩。这里是指先选定一种色彩,然后调整透明度或者饱和度,(说得通俗些就是将
色彩变淡或则加深),产生新的色彩,用于网页。这样的页面看起来色彩统一,有层次感。

2.用两种色彩。先选定一种色彩,然后选择它的对比色(在photoshop里按ctrl+shift+I)。我的
主页用蓝色和黄色就是这样确定的。整个页面色彩丰富但不花稍。

3.用一个色系。简单的说就是用一个感觉的色彩,例如淡蓝,淡黄,淡绿;或者土黄,土灰,
土蓝。确定色彩的方法各人不同,我是在photoshop里按前景色方框,在跳出的拾色器窗中
选择"自定义",然后在"色库"中选就可以了:)

4.用黑色和一种彩色。比如大红的字体配黑色的边框感觉很"跳"。

在网页配色中,忌讳的是:
1.不要将所有颜色都用到,尽量控制在三种色彩以内。
2.背景和前文的对比尽量要大,(绝对不要用花纹繁复的图案作背景),以便突出主要文字内容。

怎样设计网站首页?

 
设计首页的第一步是设计版面布局。

    就象传统的报刊杂志编辑一样,我们将网页看作一张报纸,一本杂志来进行排版布局。虽然动态网页技术的发展使得我们开始趋向于学习场景编剧,但是固定的网页版面设计基础依然是必须学习和掌握的。它们的基本原理是共通的,你可以领会要点,举一反三。版面指的是浏览器看到的完整的一个页面(可以包含框架和层)。因为每个人的显示器分辨率不同,所以同一个页面的大小可能出现640*480像素,800*600像素,1024*768像素等不同尺寸。

    布局,就是以最适合浏览的方式将图片和文字排放在页面的不同位置。你可能注意到:“最适合”是一个不确定的形容词,什么才是最适合的呢?抱歉的是阿捷不能也不可能给您一个完整的正确的答案。就好比有人希望知道成功的秘诀是什么,成功者只能建议您用什么方法,什么途径才能最容易获得成功,而不可能有一步成功的"秘诀"告诉您。

    我们在设计思考系列文章第四篇讲过站点整体的创意,版面布局也是一个创意的问题,但要比站点整体的创意容易,有规律的多。让我们先来了解一下版面布局的步骤:

    一.草案
    新建页面就象一张白纸,没有任何表格,框架和约定俗成的东西,你可以尽可能的发挥你的想象力,将你想到的"景象"画上去(我们建议您用一张白纸和一支铅笔,当然用作图软件photoshop等也可以)。这属于创造阶段,不讲究细腻工整,不必考虑细节功能,只以粗陋的线条勾画出创意的轮廓即可。尽你的可能多画几张,最后选定一个满意的作为继续创作的脚本。

    二.粗略布局
    在草案的基础上,将你确定需要放置的功能模块安排到页面上。(注:功能模块我们在"首页设计-引子"中提过,主要包含网站标志,主菜单,新闻,搜索,友情链接,广告条,邮件列表,计数器,版权信息等)。注意,这里我们必须遵循突出重点、平衡谐调的原则,将网站标志,主菜单等最重要的模块放在最显眼,最突出的位置,然后在考虑次要模块的排放。

    三.定案
    将粗略布局精细化,具体化。(靠你的智慧和经验,旁敲侧击多方联想,才能作出具有创意的布局。)

    在布局过程中,我们可以遵循的原则有:

    1、正常平衡---亦称"匀称"。多指左右、上下对照形式,主要强调秩序,能达到安定诚实、信赖的效果。
    2、异常平衡---即非对照形式,但也要平衡和韵律,当然都是不均整的,此种布局能达到强调性、不安性、高注目性的效果。
    3、对比---所谓对比,不仅利用色彩、色调等技巧来作表现,在内容上也可涉及古与今、新与旧、贫与富等对比。
    4、凝视---所谓凝视是利用页面中人物视线,使浏览者仿照跟随的心理,以达到注视页面的效果,一般多用明星凝视状。
    5、空白---空白有两种作用,一方面对其他网站表示突出卓越,另一方面也表示网页品位的优越感,这种表现方法对体显网页的格调十分有效。
    6、尽量用图片解说---此法对不能用语言说服、或用语言无法表达的情感,特别有效。图片解说的内容,可以传达给浏览者的更多的心理因素。

    以上的设计原则,虽然枯燥,但是我们如果能领会并活用到页面布局里,效果就大不一样了。比如,

    ·网页的白色背景太虚,则可以加些色快; 
    ·版面零散,可以用线条和符号串联; 
    ·左面文字过多,右面则可以插一张图片保持平衡; 
    ·表格太规矩,可以改用导角试试。

    经过不断的尝试和推敲,你的网页一定会亮丽起来的哦:)

    看看我们经常用到的版面布局形式:

    1."T"结构布局。所谓"T"结构。就是指页面顶部为横条网站标志+广告条,下方左面为主菜单,右面显示内容的布局,因为菜单条背景教深,整体效果类似英文字母"T",所以我们称之为"T"形布局。这是网页设计中用的最广返的一种布局方式。这种布局的优点是页面结构清晰,主次分明。是初学者最容易上手的布局方法。缺点是规矩呆板,如果细节色彩上不注意,很容易让人"看之无味"。

    2."口"型布局。这是一个象形的说法,就是页面一般上下各有一个广告条,左面是主菜单,右面放友情连接等,中间是主要内容。这种布局的优点是充分利用版面,信息量大。缺点是页面拥挤,不够灵活。也有将四边空出,只用中间的窗口型设计,例如网易壁纸站。

    3."三"型布局。这种布局多用于国外站点,国内用的不多。特点是页面上横向两条色块,将页面整体分割为四部分,色块中大多放广告条。

    4.对称对比布局。顾名思义,采取左右或者上下对称的布局,一半深色,一半浅色,一般用于设计型站点。优点是视觉冲击力强,缺点是将两部分有机的结合比较困难。

    5.POP布局。POP引自广告术语,就是指页面布局象一张宣传海报,以一张精美图片作为页面的设计中心。常用于时尚类站点,比如ELLE.com。优点显而易见:漂亮吸引人。缺点就是速度慢。作为版面布局还是值得借鉴的。

    以上总结了目前网络上常见的布局,其实还有许许多多别具一格的布局,关键在于你的创意和设计了。对于版面布局的技巧,这里提供四个建议,您可以自己推敲:

    1.加强视觉效果
    2.加强文案的可视度和可读性
    3.统一感的视觉
    4.新鲜和个性是布局的最高境界

posted @ 2005-07-31 17:43 woow 阅读(1144) | 评论 (1)编辑 收藏

制作网页时网页背景与前景的字体配色比较麻烦的。如果背景颜色与字体颜色的搭配不合理,就会使网页效果大打折扣。如果背景色与字体色的对比度太大,就会显得太刺眼;如果对比度太小,就会使网页风格变得过于沉闷。要达最佳配色实在是麻烦之至,自定义-〉观看效果-〉不合适-〉从头来……,

  到最后发现我们大部分宝贵的Time都花在了上面。不过别急,有了我的"网页配色之黄金分割法"之后,就可以大大节省你的时间了。别打!别打!我马上就进入正题了(呜……呜……)。

  黄金分割法的基本原理是这样的:把颜色的对比度(背景与前景)调节在0.618比例附近的位置上。下面我以FrontPage 2000为例来讲解一下具体操作。

  1.选择背景色(记住颜色的亮度值)。打开FrontPage 2000以后单击鼠标右键,选择"网页属性",弹出"网页属性"对话框后选择"背景"单元(或"格式"菜单下的"背景"选项),然后在背景色选项中选取你想要的任意背景色,然后确定(本例中选取橄榄绿RGB:0,128,0 亮度:60)。

  2.选择前景色。选择格式-〉字体,在字体颜色中选取"其他颜色"。弹出颜色对话框后选择"自定义",弹出Windows的标准颜色对话框(如图):

    

  3.计算亮度值。若背景色的亮度值为0-92时,将上述对话框中的亮度确定为X+148;若在148-240之间则定为X-148。然后选择任何你认为搭配合理的颜色(亮度值不变)作为前景色。选择原理如下:亮度的最大值是240,黄金分割值为240×0.618≈148,即亮度差为148为最佳对比度,因此选取的前景色与背景色亮度值至少有一位于0-92或148-240之间,其他亮度值不一选取(强烈建议)。

  Ok,确定,输入几个字符看看效果,是不是要好一点呢?作者原创,千万不可随意外传呦^_^。

posted @ 2005-07-31 17:35 woow 阅读(267) | 评论 (0)编辑 收藏

C#学习杂记
.NET技术学习笔记:
因为项目需要开始突击.NET,今天主要了解了一下C#语法和.NET框架基础,因为有JAVA开发经验所以看起来倒也不是很费劲,就是不知道实际应用时会怎么样。
今天的笔记分两大部分,C#语法杂记(一)和.NET框架基础(一),比较凌乱的说:

C#语法杂记(一):

◎ 在C#中,值类型(Value)中的char类型声明的竟然是一个Unicode字符类型,长度是16位的,这 和JAVA,C等语言等语言差异真大,所以C#不会默认将char类型转换为整数类型,必须显式造型;

◎ 在C#值类型中还包括传统C语言的struct类型和enum类型;

◎ C#中Boxing和UnBoxing的基本语法:
Boxing: int j = 1; object o = j;
UnBoxing: Object o = 1; int j = (int) o;

◎ Delegate:C#中充当补偿失去指针的东东;

◎ C#中初始化数组的比较特别的方式
int[] a1 = new int[]{1, 2, 3};
int[,] a2 = new int[,]{{1, 2, 3}, {4, 5, 6}};
int[][] a3 = new int[2][];
a3[0] = new int[]{1, 2, 3};
a3[1] = new int[]{4, 5, 6};

◎ C#中类的修饰符有两种:abstract 和 sealed;
sealed类式不能被继承的,一个类不能同时为sealed和abstract;

◎ C#的成员修饰符:用于修饰方法和属性;
abstract,const,
event:指定一个能被“事件”出发的Delegate,
extern:用于修饰方法,表示此方法将会在外部执行,
override,
readonly:用来修饰域,如果一个域使用了readonly,那此域只能在初始化时或构造函数中修 改,
virtural:表示其修饰的方法和属性可被派生类重载

◎ 访问修饰符
public,protected,private,
internal:其修饰成员只能载本工程内被访问
protected internal
访问修饰符不能修饰NameSpace

◎ enum的默认访问修饰符:public,且此类型不允许其它访问修饰符,
class的默认为private,可使用其它,
interface默认为public,不允许其它,
struct默认为private,此类型还可使用public、private、internal来修饰,
属性和方法的默认为private

◎ 在C#中不能使用if(1){...}来做永真判断,应使用if(true){...};

◎ 在C#的SWITCH结构中每个case后面的语句必须永break或其它跳转语句,否则编译会报错,这点 和JAVA、C等有差异:
1、goto case label;
2、goto default;

◎ C#提供foreach()程序结构;

posted @ 2005-07-31 11:04 woow 阅读(1451) | 评论 (0)编辑 收藏

转载-少走弯路的十条忠告

少走弯路的十条忠告

  如何在涉世之初少走弯路,有一个好的开端,开始一番成功的事业?以下是一些先行者积累的10条有益的涉世忠告。好好地遵循、把握这些忠告和建议吧,比起所学的课堂课程来,它毫不逊色!

1. 买个闹钟,以便按时叫醒你。

贪睡和不守时,都将成为你工作和事业上的绊脚石,任何时候都一样。不仅要学会准时,更要学会提前。就如你坐车去某地,沿途的风景很美,你忍不住下车看一看,后来虽然你还是赶到了某地,却不是准时到达。“闹钟”只是一种简单的标志和提示,真正灵活、实用的时间,掌握在每个人的心中。


2. 如果你不喜欢现在的工作,要么辞职不干,要么就闭嘴不言。

初出茅庐,往往眼高手低,心高气傲,大事做不了,小事不愿做。不要养成挑三拣四的习惯。不要雨天烦打伞,不带伞又怕淋雨,处处表现出不满的情绪。记住,不做则已,要做就要做好。


3. 每个人都有孤独的时候。

要学会忍受孤独,这样才会成熟起来。年轻人嘻嘻哈哈、打打闹闹惯了,到了一个陌生的环境,面对形形色色的人和事,一下子不知所措起来,有时连一个可以倾心说话的地方也没有。这时,千万别浮躁,学会静心,学会忍受孤独。在孤独中思考,在思考中成熟,在成熟中升华。不要因为寂寞而乱了方寸,而去做无聊无益的事情,白白浪费了宝贵的时间。


4. 走运时要做好倒霉的准备。

有一天,一只狐狸走到一个葡萄园外,看见里面水灵灵的葡萄垂涎欲滴。可是外面有栅栏挡着,无法进去。于是它一狠心绝食三日,减肥之后,终于钻进葡萄园内饱餐一顿。当它心满意足地想离开葡萄园时,发觉自己吃得太饱,怎么也钻不出栅栏了。相信任何人都不愿做这样的狐狸。退路同样重要。饱带干粮,晴带雨伞,点滴积累,水到渠成。有的东西今天似乎一文不值,但有朝一日也许就会身价百倍。


5. 不要像玻璃那样脆弱。

有的人眼睛总盯着自己,所以长不高看不远;总是喜欢怨天尤人,也使别人无比厌烦。没有苦中苦,哪来甜中甜?不要像玻璃那样脆弱,而应像水晶一样透明,太阳一样辉煌,腊梅一样坚强。既然睁开眼睛享受风的清凉,就不要埋怨风中细小的沙粒。


6. 管住自己的嘴巴。

不要谈论自己,更不要议论别人。谈论自己往往会自大虚伪,在名不副实中失去自己。议论别人往往陷入鸡毛蒜皮的是非口舌中纠缠不清。每天下班后和你的那些同事朋友喝酒聊天可不是件好事,因为,这中间往往会把议论同事、朋友当做话题。背后议论人总是不好的,尤其是议论别人的短处,这些会降低你的人格。


7. 机会从不会“失掉”,你失掉了,自有别人会得到。

不要凡事在天,守株待兔,更不要寄希望于“机会”。机会只不过是相对于充分准备而又善于创造机会的人而言的。也许,你正为失去一个机会而懊悔、埋怨的时候,机会正被你对面那个同样的“倒霉鬼”给抓住了。没有机会,就要创造机会,有了机会,就要巧妙地抓住。


8. 若电话老是不响,你该打出去。

很多时候,电话会给你带来意想不到的收获,它不是花瓶,仅仅成为一种摆设。交了新朋友,别忘了老朋友,朋友多了路好走。交际的一大诀窍就是主动。好的人缘好的口碑,往往助你的事业更上一个台阶。


9. 千万不要因为自己已经到了结婚年龄而草率结婚。

想结婚,就要找一个能和你心心相印、相辅相携的伴侣。不要因为放纵和游戏而恋爱,不要因为恋爱而影响工作和事业,更不要因一桩草率而失败的婚姻而使人生受阻。感情用事往往会因小失大。


10. 写出你一生要做的事情,把单子放在皮夹里,经常拿出来看。

人生要有目标,要有计划,要有提醒,要有紧迫感。一个又一个小目标串起来,就成了你一生的大目标。生活富足了,环境改善了,不要忘了皮夹里那张看似薄薄的单子。
http://bbs.csai.cn/blog/Blog/floodking/2005729112056.html

posted @ 2005-07-29 19:44 woow 阅读(203) | 评论 (0)编辑 收藏

 正确识别java语言的关键字(keyword)和保留字(reserved word)是十分重要的。Java的关键字对java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等。保留字是为java预留的关键字,他们虽然现在没有作为关键字,但在以后的升级版本中有可能作为关键字。

  关键字列表

  abstract boolean break byte case
  catch char class continue default
  do double else extends false
  final finally float for if
  implements import instanceof int interface
  long native new null package
  private protected public return short
  static super switch synchronized this
  throw throws transient true try
  void volatile while
  
  保留字
  const,goto

  注意点

  识别java语言的关键字,不要和其他语言如c/c++的关键字混淆。

posted @ 2005-07-28 06:23 woow 阅读(313) | 评论 (0)编辑 收藏

 
Susan Warren
Microsoft Corporation
2001 年 11 月 27 日

与刚接触 ASP.NET 页面的开发人员交谈时,他们通常向我提出的第一个问题就是:“那个 ViewState 到底是什么?”他们的语气中流露出的那种感觉,就象我来到一家异国情调的餐馆,侍者端上一道我从未见过的菜肴时的那种感觉 - 既疑惑不解,又充满好奇。但肯定有人认为它不错,否则就不会提供了。所以,我会先尝一尝,或许会喜欢上它,尽管它看上去的确很古怪!

对于 ViewState 也是如此,但是如果适应了它的风格,您会发现在许多情况下,您将乐于在自己的 ASP.NET 应用程序中使用 ViewState,因为它可以帮助您使用更少的代码完成更多的工作。但是,有时也会对 ViewState 完全弃之不用。下面我们就这两种情况分别进行阐述,不过,让我们先回答什么是 ViewState 这个问题。

答案:ViewState 用于维护页面的 UI 状态

Web 是没有状态的,ASP.NET 页面也没有状态,它们在到服务器的每个往返过程中被实例化、执行、呈现和处理。作为 Web 开发人员,您可以使用众所周知的技术(如以会话状态将状态存储在服务器上,或将页面回传到自身)来添加状态。下面我们以图 1 中的注册窗体为例进行论述。

图 1:恢复回传的窗体值

从上图中可以看出,我为便餐选择了一个无效的值。此窗体与 Web 上的多数窗体一样友好,它在出现错误的字段旁边显示一条有用的错误消息和一个星号。而且,窗体中还显示了我在其他文本框和下拉列表中输入的所有有效值。这在某种程度上是可能的,因为 HTML 窗体元素会在 HTTP 标头中将其当前值从浏览器发送到服务器。您可以使用 ASP.NET 跟踪来查看回传的窗体值,如图 2 所示。

图 2:HTTP 窗体中回传的值(通过 ASP.NET 跟踪显示)

在 ASP.NET 之前,通过多次回传将值恢复到窗体字段中完全是页面开发人员的责任,他们将不得不从 HTTP 窗体中逐个拾取回传值,然后再将其推回字段中。幸运的是,现在 ASP.NET 可以自动完成这项任务,从而为开发人员免除了一项令人厌烦的工作,同时也无需再为窗体编写大量的代码。但这并不是 ViewState。

ViewState(英文)是一种机制,ASP.NET 使用这种机制来跟踪服务器控件状态值,否则这些值将不作为 HTTP 窗体的一部分而回传。例如,由 Label 控件显示的文本默认情况下就保存在 ViewState 中。作为开发人员,您可以绑定数据,或在首次加载该页面时仅对 Label 编程设置一次,在后续的回传中,该标签文本将自动从 ViewState 中重新填充。因此,除了可以减少繁琐的工作和代码外,ViewState 通常还可以减少数据库的往返次数。

ViewState 的工作原理

ViewState 确实没有什么神秘之处,它是由 ASP.NET 页面框架管理的一个隐藏的窗体字段。当 ASP.NET 执行某个页面时,该页面上的 ViewState 值和所有控件将被收集并格式化成一个编码字符串,然后被分配给隐藏窗体字段的值属性(即 <input type=hidden>)。由于隐藏窗体字段是发送到客户端的页面的一部分,所以 ViewState 值被临时存储在客户端的浏览器中。如果客户端选择将该页面回传给服务器,则 ViewState 字符串也将被回传。在上面的图 2 中可以看到 ViewState 窗体字段及其回传的值。

回传后,ASP.NET 页面框架将解析 ViewState 字符串,并为该页面和各个控件填充 ViewState 属性。然后,控件再使用 ViewState 数据将自己重新恢复为以前的状态。

关于 ViewState 还有三个值得注意的小问题。

  1. 如果要使用 ViewState,则在 ASPX 页面中必须有一个服务器端窗体标记 (<form runat=server>)。窗体字段是必需的,这样包含 ViewState 信息的隐藏字段才能回传给服务器。而且,该窗体还必须是服务器端的窗体,这样在服务器上执行该页面时,ASP.NET 页面框架才能添加隐藏的字段。
  2. 页面本身将 20 字节左右的信息保存在 ViewState 中,用于在回传时将 PostBack 数据和 ViewState 值分发给正确的控件。因此,即使该页面或应用程序禁用了 ViewState,仍可以在 ViewState 中看到少量的剩余字节。
  3. 在页面不回传的情况下,可以通过省略服务器端的 <form> 标记来去除页面中的 ViewState。

充分利用 ViewState

ViewState 为跨回传跟踪控件的状态提供了一条神奇的途径,因为它不使用服务器资源、不会超时,并且适用于任何浏览器。如果您要编写控件,那么肯定需要了解如何在控件中维护状态(英文)。

开发人员在编写页面时同样可以按照几乎相同的方式来利用 ViewState,只是有时页面会包含不由控件存储的 UI 状态值。您可以跟踪 ViewState 中的值,使用的编程语法与会话和高速缓存的语法类似:

[Visual Basic]

' 保存在 ViewState 中
ViewState("SortOrder") = "DESC"

' 从 ViewState 中读取
Dim SortOrder As String = CStr(ViewState("SortOrder"))

[C#]

// 保存在 ViewState 中
ViewState["SortOrder"] = "DESC";

// 从 ViewState 中读取
string sortOrder = (string)ViewState["SortOrder"];

请看下面的示例:要在 Web 页上显示一个项目列表,而每个用户需要不同的列表排序。项目列表是静态的,因此可以将这些页面绑定到相同的缓存数据集,而排序顺序只是用户特定的 UI 状态的一小部分。ViewState 非常适合于存储这种类型的值。代码如下:

[Visual Basic]

<%@ Import Namespace="System.Data" %>
<HTML>
    <HEAD>
        <title>用于页面 UI 状态值的 ViewState/title>
    </HEAD>
    <body>
        <form runat="server">
            <H3>
                在 ViewState 中存储非控件状态
            </H3>
            <P>
                此示例将一列静态数据的当前排序顺序存储在 ViewState 中。<br>
                单击列标题中的链接,可按该字段排序数据。<br>
                再次单击该链接,将按相反顺序排序。
                <br><br><br>
                <asp:datagrid id="DataGrid1" runat="server" 
OnSortCommand="SortGrid" BorderStyle="None" BorderWidth="1px" 
BorderColor="#CCCCCC" BackColor="White" CellPadding="5" AllowSorting="True">
                    <HeaderStyle Font-Bold="True" ForeColor="White" 
BackColor="#006699">
                    </HeaderStyle>
                </asp:datagrid>
            </P>
        </form>
    </body>
</HTML>
<script runat="server">

    ' 在 ViewState 中跟踪 SortField 属性
    Property SortField() As String

        Get
            Dim o As Object = ViewState("SortField")
            If o Is Nothing Then
                Return String.Empty
            End If
            Return CStr(o)
        End Get

        Set(Value As String)
            If Value = SortField Then
                ' 与当前排序文件相同,切换排序方向
                SortAscending = Not SortAscending
            End If
            ViewState("SortField") = Value
        End Set

    End Property

    ' 在 ViewState 中跟踪 SortAscending 属性
    Property SortAscending() As Boolean

        Get
            Dim o As Object = ViewState("SortAscending")
            If o Is Nothing Then
                Return True
            End If
            Return CBool(o)
        End Get

        Set(Value As Boolean)
            ViewState("SortAscending") = Value
        End Set

    End Property

    Private Sub Page_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        If Not Page.IsPostBack Then
            BindGrid()
        End If

    End Sub

    Sub BindGrid()

        ' 获取数据
        Dim ds As New DataSet()
        ds.ReadXml(Server.MapPath("TestData.xml"))
        
        Dim dv As New DataView(ds.Tables(0))

        ' 应用排序过滤器和方向
        dv.Sort = SortField
        If Not SortAscending Then
            dv.Sort += " DESC"
        End If

        ' 绑定网格
        DataGrid1.DataSource = dv
        DataGrid1.DataBind()

    End Sub
    
    Private Sub SortGrid(sender As Object, e As DataGridSortCommandEventArgs)
        DataGrid1.CurrentPageIndex = 0
        SortField = e.SortExpression
        BindGrid()
    End Sub
    
</script>

[C#]

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<HTML>
    <HEAD>
        <title>用于页面 UI 状态值的 ViewState</title>
    </HEAD>
    <body>
        <form runat="server">
            <H3>
                在 ViewState 中存储非控件状态
            </H3>
            <P>
                此示例将一列静态数据的当前排序顺序存储在 ViewState 中。<br>
                单击列标题中的链接,可按该字段排序数据。<br>
                再次单击该链接,将按相反顺序排序。
                <br><br><br>
                <asp:datagrid id="DataGrid1" runat="server" OnSortCommand="SortGrid" 
                BorderStyle="None" BorderWidth="1px" BorderColor="#CCCCCC" 
                BackColor="White" CellPadding="5" AllowSorting="True">
                    <HeaderStyle Font-Bold="True" ForeColor="White" BackColor="#006699">
                    </HeaderStyle>
                </asp:datagrid>
            </P>
        </form>
    </body>
</HTML>
<script runat="server">

    // 在 ViewState 中跟踪 SortField 属性
    string SortField {

        get {
            object o = ViewState["SortField"];
            if (o == null) {
                return String.Empty;
            }
            return (string)o;
        }

        set {
            if (value == SortField) {
                // 与当前排序文件相同,切换排序方向
                SortAscending = !SortAscending;
            }
            ViewState["SortField"] = value;
        }
    }

    // 在 ViewState 中跟踪 SortAscending 属性
    bool SortAscending {

        get {
            object o = ViewState["SortAscending"];
            if (o == null) {
                return true;
            }
            return (bool)o;
        }

        set {
            ViewState["SortAscending"] = value;
        }
    }

    void Page_Load(object sender, EventArgs e) {

        if (!Page.IsPostBack) {
            BindGrid();
        }
    }

    void BindGrid() {

        // 获取数据
        DataSet ds = new DataSet();
        ds.ReadXml(Server.MapPath("TestData.xml"));
        
        DataView dv = new DataView(ds.Tables[0]);

        // 应用排序过滤器和方向
        dv.Sort = SortField;
        if (!SortAscending) {
            dv.Sort += " DESC";
        }

        // 绑定网格
        DataGrid1.DataSource = dv;
        DataGrid1.DataBind();
   }

   void SortGrid(object sender, DataGridSortCommandEventArgs e) {

        DataGrid1.CurrentPageIndex = 0;
        SortField = e.SortExpression;
        BindGrid();
    }

</script>

下面是上述两个代码段中引用的 testdata.xml 的代码:

<?xml version="1.0" standalone="yes"?>
<NewDataSet>
  <Table>
    <pub_id>0736</pub_id>
    <pub_name>New Moon Books</pub_name>
    <city>Boston</city>
    <state>MA</state>
    <country>USA</country>
  </Table>
  <Table>
    <pub_id>0877</pub_id>
    <pub_name>Binnet &amp; Hardley</pub_name>
    <city>Washington</city>
    <state>DC</state>
    <country>USA</country>
  </Table>
  <Table>
    <pub_id>1389</pub_id>
    <pub_name>Algodata Infosystems</pub_name>
    <city>Berkeley</city>
    <state>CA</state>
    <country>USA</country>
  </Table>
  <Table>
    <pub_id>1622</pub_id>
    <pub_name>Five Lakes Publishing</pub_name>
    <city>Chicago</city>
    <state>IL</state>
    <country>USA</country>
  </Table>
  <Table>
    <pub_id>1756</pub_id>
    <pub_name>Ramona Publishers</pub_name>
    <city>Dallas</city>
    <state>TX</state>
    <country>USA</country>
  </Table>
  <Table>
    <pub_id>9901</pub_id>
    <pub_name>GGG&amp;G</pub_name>
    <city>Muenchen</city>
    <country>Germany</country>
  </Table>
  <Table>
    <pub_id>9952</pub_id>
    <pub_name>Scootney Books</pub_name>
    <city>New York</city>
    <state>NY</state>
    <country>USA</country>
  </Table>
  <Table>
    <pub_id>9999</pub_id>
    <pub_name>Lucerne Publishing</pub_name>
    <city>Paris</city>
    <country>France</country>
  </Table>
</NewDataSet>

选择会话状态还是 ViewState?

在某些情况下,将状态值保存在 ViewState 中并不是最佳选择,最常用的替代方法就是会话状态,它通常更适用于:

  • 大量的数据。由于 ViewState 增加了发送到浏览器的页面的大小(HTML 有效负载),同时也增加了回传的窗体的大小,因此不适合存储大量数据。
  • 未在 UI 中显示的安全数据。尽管 ViewState 数据已被编码,并且可以选择对其进行加密,但始终不将数据发送到客户端才是最安全的。因此,会话是更安全的选择。(由于数据库需要额外的凭据进行验证,因此将数据存储在数据库中会更安全。可以添加 SSL 以获得更安全的链接。)但是,如果在 UI 中已经显示了该专用数据,那么您应该已经确认了链接的安全性。在这种情况下,将同样的值放入 ViewState 不会降低安全性。
  • 尚未序列化到 ViewState 中的对象,如 DataSet。ViewState 序列化程序只为一小部分常用的对象类型进行了优化,如下所示。其他可序列化的类型或许可以保留在 ViewState 中,但速度会变慢,并会生成一个非常大的 ViewState。
  会话状态 ViewState
是否使用服务器资源?
是否超时? 是,20 分钟后(默认)
是否存储所有 .NET 类型? 否,仅支持:String、Integer、Boolean、Array、ArrayList、Hashtable 和自定义 TypeConverter
是否增加“HTML 有效负载”?

使用 ViewState 获得最佳性能

使用 ViewState 时,每个对象都必须先序列化到 ViewState 中,然后再通过回传进行反序列化,因此使用 ViewState 并非是没有代价的。但是,如果遵循某些简单的原则对 ViewState 的成本加以控制,则通常不会产生明显的性能影响。

  • 在不需要时禁用 ViewState。下面的“减少使用 ViewState”一节将详细介绍这一问题。
  • 使用优化过的 ViewState 序列化程序。上面列出的类型具有专门的序列化程序,这些程序运行速度很快,并已经过优化,可以生成很小的 ViewState。如果要序列化一个未在上面列出的类型,可以创建一个自定义 TypeConverter 来显著提高它的性能。
  • 尽量减少使用对象,如果可能,尽量减少放入 ViewState 中的对象的数目。例如,不要使用二维字符串数组(名称/值,其对象的数目与数组的长度一样多),而应使用两个字符串数组(只有两个对象)。但是,在将两个已知类型存储在 ViewState 中之前,在这两者之间转换不会获得任何性能提高,因为这样做实际上相当于付出了两次转换的代价。

减少使用 ViewState

默认情况下 ViewState 将被启用,并且是由每个控件(而非页面开发人员)来决定存储在 ViewState 中的内容。有时,这一信息对应用程序并没有什么用处。尽管也没什么害处,但却会明显增加发送到浏览器的页面的大小。因此如果不需要使用 ViewState,最好还是将它关闭,特别是当 ViewState 很大的时候。

可以基于每个控件、每个页面或每个应用程序来关闭 ViewState。在以下情况中将不再需要 ViewState:

页面 控件
  • 页面不回传给自身。
  • 处理的不是控件的事件。
  • 控件没有动态的或数据绑定的属性值(或对于每一个请求它们都设置在代码中)。

DataGrid 控件是 ViewState 的一个重量级用户。默认情况下,在网格中显示的所有数据也都存储在 ViewState 中,当需要一个复杂的操作(如复杂的搜索)来获取数据时,这是非常有用的。但是,DataGrid 的这种行为有时也使得 ViewState 成为累赘。

例如,这里有一个简单的页面就属于上述情况。因为页面不回传给自身,所以它并不需要 ViewState。

图 3:带有 DataGrid1 的简单页面 LessViewState.aspx

<%@ Import Namespace="System.Data" %>
<html>
    <body>
        <form runat="server">
            <asp:DataGrid runat="server" />
        </form>
    </body>
</html>
<script runat="server">

    Private Sub Page_Load(sender As Object, e As EventArgs) 

        Dim ds as New DataSet()
        ds.ReadXml(Server.MapPath("TestData.xml"))

        DataGrid1.DataSource = ds
        DataGrid1.DataBind()

    End Sub

</script>

启用 ViewState 时,这个小网格会给该页面增加 3000 多字节的 HTML 有效负载!使用 ASP.NET Tracing(英文)或查看发送到浏览器的页面的源代码(如以下代码所示),可以清楚地看到这一点。

<HTML>
    <HEAD>
        <title>减少页面的“HTML 有效负载”</title>
    </HEAD>
    <body>
    <form name="_ctl0" method="post" action="lessviewstate.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" 
value="dDwxNTgzOTU2ODA7dDw7bDxpPDE+Oz47bDx0PDtsPGk8MT47PjtsPHQ8QDA8cDxw
PGw8UGFnZUNvdW50O18hSXRlbUNvdW50O18hRGF0YVNvdXJjZUl0ZW1Db3VudDtEYXRhS2V
5czs+O2w8aTwxPjtpPDg+O2k8OD47bDw+Oz4+Oz47Ozs7Ozs7OztAMDxAMDxwPGw8SGVhZG
VyVGV4dDtEYXRhRmllbGQ7U29ydEV4cHJlc3Npb247UmVhZE9ubHk7PjtsPHB1Yl9pZDtwd
WJfaWQ7cHViX2lkO288Zj47Pj47Ozs7PjtAMDxwPGw8SGVhZGVyVGV4dDtEYXRhRmllbGQ7
U29ydEV4cHJlc3Npb247UmVhZE9ubHk7PjtsPHB1Yl9uYW1lO3B1Yl9uYW1lO3B1Yl9uYW1
lO288Zj47Pj47Ozs7PjtAMDxwPGw8SGVhZGVyVGV4dDtEYXRhRmllbGQ7U29ydEV4cHJlc3
Npb247UmVhZE9ubHk7PjtsPGNpdHk7Y2l0eTtjaXR5O288Zj47Pj47Ozs7PjtAMDxwPGw8S
GVhZGVyVGV4dDtEYXRhRmllbGQ7U29ydEV4cHJlc3Npb247UmVhZE9ubHk7PjtsPHN0YXRl
O3N0YXRlO3N0YXRlO288Zj47Pj47Ozs7PjtAMDxwPGw8SGVhZGVyVGV4dDtEYXRhRmllbGQ
7U29ydEV4cHJlc3Npb247UmVhZE9ubHk7PjtsPGNvdW50cnk7Y291bnRyeTtjb3VudHJ5O2
88Zj47Pj47Ozs7Pjs+Oz47bDxpPDA+Oz47bDx0PDtsPGk8MT47aTwyPjtpPDM+O2k8ND47a
Tw1PjtpPDY+O2k8Nz47aTw4Pjs+O2w8dDw7bDxpPDA+O2k8MT47aTwyPjtpPDM+O2k8ND47
PjtsPHQ8cDxwPGw8VGV4dDs+O2w8MDczNjs+Pjs+Ozs+O3Q8cDxwPGw8VGV4dDs+O2w8TmV
3IE1vb24gQm9va3M7Pj47Pjs7Pjt0PHA8cDxsPFRleHQ7PjtsPEJvc3Rvbjs+Pjs+Ozs+O3
Q8cDxwPGw8VGV4dDs+O2w8TUE7Pj47Pjs7Pjt0PHA8cDxsPFRleHQ7PjtsPFVTQTs+Pjs+O
zs+Oz4+O3Q8O2w8aTwwPjtpPDE+O2k8Mj47aTwzPjtpPDQ+Oz47bDx0PHA8cDxsPFRleHQ7
PjtsPDA4Nzc7Pj47Pjs7Pjt0PHA8cDxsPFRleHQ7PjtsPEJpbm5ldCAmIEhhcmRsZXk7Pj4
7Pjs7Pjt0PH_u56 ?cDxsPFRleHQ7PjtsPFdhc2hpbmd0b247Pj47Pjs7Pjt0PHA8cDxsPFRleHQ7PjtsPERDOz
4+Oz47Oz47dDxwPHA8bDxUZXh0Oz47bDxVU0E7Pj47Pjs7Pjs+Pjt0PDtsPGk8MD47aTwxP
jtpPDI+O2k8Mz47aTw0Pjs+O2w8dDxwPHA8bDxUZXh0Oz47bDwxMzg5Oz4+Oz47Oz47dDxw
PHA8bDxUZXh0Oz47bDxBbGdvZGF0YSBJbmZvc3lzdGVtczs+Pjs+Ozs+O3Q8cDxwPGw8VGV
4dDs+O2w8QmVya2VsZXk7Pj47Pjs7Pjt0PHA8cDxsPFRleHQ7PjtsPENBOz4+Oz47Oz47dD
xwPHA8bDxUZXh0Oz47bDxVU0E7Pj47Pjs7Pjs+Pjt0PDtsPGk8MD47aTwxPjtpPDI+O2k8M
z47aTw0Pjs+O2w8dDxwPHA8bDxUZXh0Oz47bDwxNjIyOz4+Oz47Oz47dDxwPHA8bDxUZXh0
Oz47bDxGaXZlIExha2VzIFB1Ymxpc2hpbmc7Pj47Pjs7Pjt0PHA8cDxsPFRleHQ7PjtsPEN
oaWNhZ287Pj47Pjs7Pjt0PHA8cDxsPFRleHQ7PjtsPElMOz4+Oz47Oz47dDxwPHA8bDxUZX
h0Oz47bDxVU0E7Pj47Pjs7Pjs+Pjt0PDtsPGk8MD47aTwxPjtpPDI+O2k8Mz47aTw0Pjs+O
2w8dDxwPHA8bDxUZXh0Oz47bDwxNzU2Oz4+Oz47Oz47dDxwPHA8bDxUZXh0Oz47bDxSYW1v
bmEgUHVibGlzaGVyczs+Pjs+Ozs+O3Q8cDxwPGw8VGV4dDs+O2w8RGFsbGFzOz4+Oz47Oz4
7dDxwPHA8bDxUZXh0Oz47bDxUWDs+Pjs+Ozs+O3Q8cDxwPGw8VGV4dDs+O2w8VVNBOz4+Oz
47Oz47Pj47dDw7bDxpPDA+O2k8MT47aTwyPjtpPDM+O2k8ND47PjtsPHQ8cDxwPGw8VGV4d
Ds+O2w8OTkwMTs+Pjs+Ozs+O3Q8cDxwPGw8VGV4dDs+O2w8R0dHJkc7Pj47Pjs7Pjt0PHA8
cDxsPFRleHQ7PjtsPE3DvG5jaGVuOz4+Oz47Oz47dDxwPHA8bDxUZXh0Oz47bDwmbmJzcFw
7Oz4+Oz47Oz47dDxwPHA8bDxUZXh0Oz47bDxHZXJtYW55Oz4+Oz47Oz47Pj47dDw7bDxpPD
A+O2k8MT47aTwyPjtpPDM+O2k8ND47PjtsPHQ8cDxwPGw8VGV4dDs+O2w8OTk1Mjs+Pjs+O
zs+O3Q8cDxwPGw8VGV4dDs+O2w8U2Nvb3RuZXkgQm9va3M7Pj47Pjs7Pjt0PHA8cDxsPFRl
eHQ7PjtsPE5ldyBZb3JrOz4+Oz47Oz47dDxwPHA8bDxUZXh0Oz47bDxOWTs+Pjs+Ozs+O3Q
8cDxwPGw8VGV4dDs+O2w8VVNBOz4+Oz47Oz47Pj47dDw7bDxpPDA+O2k8MT47aTwyPjtpPD
M+O2k8ND47PjtsPHQ8cDxwPGw8VGV4dDs+O2w8OTk5OTs+Pjs+Ozs+O3Q8cDxwPGw8VGV4d
Ds+O2w8THVjZXJuZSBQdWJsaXNoaW5nOz4+Oz47Oz47dDxwPHA8bDxUZXh0Oz47bDxQYXJp
czs+Pjs+Ozs+O3Q8cDxwPGw8VGV4dDs+O2w8Jm5ic3BcOzs+Pjs+Ozs+O3Q8cDxwPGw8VGV
4dDs+O2w8RnJhbmNlOz4+Oz47Oz47Pj47Pj47Pj47Pj47Pj47Pg==" />

看!只是禁用了该网格的 ViewState,同一页面的有效负载就大大减少了:

<HTML>
    <HEAD>
        <title>减少页面的“HTML 有效负载”</title>
    </HEAD>
    <body>
    <form name="_ctl0" method="post" action="lessviewstate.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNTgzOTU2ODA7Oz4=" />

下面是 Visual Basic 和 C# 的完整的 LessViewState 代码:

[Visual Basic]

<%@ Import Namespace="System.Data" %>
<html>
    <HEAD>
        <title>减少页面的“HTML 有效负载”</title>
    </HEAD>
    <body>
        <form runat="server">
            <H3>
                通过禁用 ViewState 来减少页面的“HTML 有效负载”
            </H3>
            <P>
                <asp:datagrid id="DataGrid1" runat="server" EnableViewState="false" 
                BorderStyle="None" BorderWidth="1px" BorderColor="#CCCCCC" 
                BackColor="White" CellPadding="5">
                    <HeaderStyle Font-Bold="True" ForeColor="White" BackColor="#006699">
                    </HeaderStyle>
                </asp:datagrid>
            </P>
        </form>
    </body>
</html><script runat="server">

    Private Sub Page_Load(sender As Object, e As EventArgs) 

        Dim ds as New DataSet()
        ds.ReadXml(Server.MapPath("TestData.xml"))

        DataGrid1.DataSource = ds
        DataGrid1.DataBind()

    End Sub

</script>

[C#]

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<html>
    <HEAD>
        <title>减少页面的“HTML 有效负载”</title>
    </HEAD>
    <body>
        <form runat="server">
            <H3>
                通过禁用 ViewState 来减少页面的“HTML 有效负载”
            </H3>
            <P>
                <asp:datagrid id="DataGrid1" runat="server" EnableViewState="false"
                BorderStyle="None" BorderWidth="1px" BorderColor="#CCCCCC"
                BackColor="White" CellPadding="5">
                    <HeaderStyle Font-Bold="True" ForeColor="White" BackColor="#006699">
                    </HeaderStyle>
                </asp:datagrid>
            </P>
        </form>
    </body>
</html>
<script runat="server">

    void Page_Load(object sender, EventArgs e) {

        DataSet ds = new DataSet();
        ds.ReadXml(Server.MapPath("TestData.xml"));
        
        DataGrid1.DataSource = ds;
        DataGrid1.DataBind();
    }

</script>

禁用 ViewState

在上述示例中,我通过将网格的 EnableViewState 属性设置为 False 禁用了 ViewState。可以针对单个控件、整个页面或整个应用程序禁用 ViewState,如下所示:

每个控件(在标记上) <asp:datagrid EnableViewState="false" ?/>
每个页面(在指令中) <%@ Page EnableViewState="False" ?%>
每个应用程序(在 web.config 中) <Pages EnableViewState="false" ?/>

使 ViewState 更安全

由于 ViewState 没有被格式化为清晰的文本,某些人有时会认为它被加密了,其实并没有。相反,ViewState 只是进行了 Base64 编码,以确保值在往返过程中不会发生变化,而并不考虑应用程序使用的响应/请求编码。

可以向应用程序中添加两种 ViewState 安全级别:

  • 防篡改
  • 加密

需要注意的是,ViewState 安全性对于处理和呈现 ASP.NET 页面所需的时间有直接的影响。简单地说,安全性越高,速度越慢。因此如果不需要,请不要为 ViewState 添加安全性。

防篡改

尽管散列代码不能确保 ViewState 字段中实际数据的安全,但它能够显著降低有人通过 ViewState 骗过应用程序的可能性,即防止回传应用程序通常禁止用户输入的值。

可以通过设置 EnableViewStateMAC 属性来指示 ASP.NET 向 ViewState 字段中追加一个散列代码:

<%@Page EnableViewStateMAC=true %>

可以在页面级别上设置 EnableViewStateMAC,也可以在应用程序级别上设置。在回传时,ASP.NET 将为 ViewState 数据生成一个散列代码,并将其与存储在回传值中的散列代码进行比较。如果两处的散列代码不匹配,该 ViewState 数据将被丢弃,同时控件将恢复为原来的设置。

默认情况下,ASP.NET 使用 SHA1 算法来生成 ViewState 散列代码。此外,也可以通过在 machine.config 文件中设置 <machineKey> 来选择 MD5 算法,如下所示:

<machineKey validation="MD5" />

加密

可以使用加密来保护 ViewState 字段中的实际数据值。首先,必须如上所述设置 EnableViewStatMAC="true"。然后,将 machineKey validation 类型设置为 3DES。这将指示 ASP.NET 使用 Triple DES 对称加密算法来加密 ViewState 值。

<machineKey validation="3DES" />

Web 领域中的 ViewState 安全性

默认情况下,ASP.NET 将创建一个随机的验证密钥,并存储在每个服务器的本地安全授权 (LSA) 中。要验证在另一台服务器上创建的 ViewState 字段,两台服务器的 validationKey 必须设置为相同的值。如果要通过上述方式之一,对运行于 Web 领域配置中的应用程序进行 ViewState 安全设置,则需要为所有服务器提供一个唯一的、共享的验证密钥。

验证密钥是一个包含 20 到 64 位密码增强字节的随机字符串,用 40 到 128 个十六进制字符表示。密钥越长越安全,因此建议使用 128 个字符的密钥(如果计算机支持)。例如:

<machineKey validation="SHA1" validationKey=" 
F3690E7A3143C185AB1089616A8B4D81FD55DD7A69EEAA3B32A6AE813ECEECD28DEA66A
23BEE42193729BD48595EBAFE2C2E765BE77E006330BC3B1392D7C73F" />

System.Security.Cryptography 名称空间包括 RNGCryptoServiceProvider 类,使用该类可以生成此字符串,如以下 GenerateCryptoKey.aspx 示例所示:

<%@ Page Language="c#" %>
<%@ Import Namespace="System.Security.Cryptography" %>
<HTML>
    <body>
        <form runat="server">
        <H3>生成随机加密密钥</H3>
        <P>
            <asp:RadioButtonList id="RadioButtonList1" 
            runat="server" RepeatDirection="Horizontal">
                <asp:ListItem Value="40">40-byte</asp:ListItem>
                <asp:ListItem Value="128" Selected="True">128-byte</asp:ListItem>
            </asp:RadioButtonList>&nbsp;
            <asp:Button id="Button1" runat="server" onclick="GenerateKey"
            Text="生成密钥">
            </asp:Button></P>
        <P>
            <asp:TextBox id="TextBox1" runat="server" TextMode="MultiLine" 
            Rows="10" Columns="70" BackColor="#EEEEEE" EnableViewState="False">
            复制并粘贴生成的结果</asp:TextBox></P>
        </form>
    </body>
</HTML>


<script runat=server>

   void GenerateKey(object sender, System.EventArgs e)
   {
       int keylength = Int32.Parse(RadioButtonList1.SelectedItem.Value);
       
      // 在此处放入用于初始化页面的用户代码
        byte[] buff = new Byte[keylength/2];

        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

        // 该数组已使用密码增强的随机字节进行填充
        rng.GetBytes(buff);

        StringBuilder sb = new StringBuilder(keylength);
        int i;
        for (i = 0; i < buff.Length; i++) {
            sb.Append(String.Format("{0:X2}",buff[i]));
        }
        
        // 粘贴到文本框,用户可从中复制
        TextBox1.Text = sb.ToString();
    }

</script>

总结

ASP.NET ViewState 是一种新的状态服务,可供开发人员基于每个用户来跟踪 UI 状态。ViewState 没有什么神秘之处,它只是利用了一个老的 Web 编程技巧:在一个隐藏的窗体字段中来回传递状态,并将它直接应用于页面处理框架中。但效果却非常好 - 在基于 Web 的窗体中只需编写并维护很少的代码。

用户可能并不总是需要它,但我想您在需要它的时候会发现,ViewState 是提供给页面开发人员的诸多 ASP.NET 新功能中非常令人满意的一种功能。


Susan Warren 是 .NET Framework 组的 ASP.NET 项目经理。

posted @ 2005-07-28 05:02 woow 阅读(205) | 评论 (0)编辑 收藏

仅列出标题
共13页: First 3 4 5 6 7 8 9 10 11 Last