发布新日志

  • 自动化工具selenium和RFT的比较

    2011-05-18 14:55:30

    与 IBM 的 Rational Functional Tester 工具相比,Selenium Remote Control 有四个主要特点:

    • Selenium 基于 Http 协议来实现。它根据测试脚本提供的 Xpath(DOM or CSS) 定位网页上面的元素并对其进行操作,就如我们通常使用绝对和相对路径查询操作系统文件一样;IBM 的 Rational Functional Tester 则基于对象模型设计实现的。它抓取浏览器在屏幕上实时展现的对象集合,根据用户输入的元素属性在上面寻找可能的对象。实践证明,当页面非常复杂时,这个方法不精确高效。
    • Selenium 是一个开源项目,可编写测试脚本的 API 包支持好几种语言,包括 Java, C++, .Net, Python 等;IBM 的 Rational Functional Tester 需要 license,并且只支持 Java。
    • Selenium 在运行时使用 JavaScript. 向应用程序发出测试命令,所以它对当前浏览器的位置和电脑的屏幕没有要求;IBM 的 Rational Functional Tester 需要获得当前浏览器和电脑屏幕的位置,要求屏幕始终打开。这需要人围的支持,提高项目开发的成本。
    • Selenium可以支持多种浏览器,包括Firefox 2+,IE 6+,Safari 2+,Opera 8+,Google Chrome等等。而RFT远没有这么多,主要是IE和firefox。
  • 关于模式匹配的分类

    2011-05-17 14:56:36

      There are three types of patterns: globbing, regular expressions, and exact.

    1. Globbing Patterns(for SQL)

      Most people are familiar with globbing as it is utilized in filename expansion at a DOS or Unix/Linux command line such as ls *.c. In this case, globbing is used to display all the files ending with a .c extension that exist in the current directory. Globbing is fairly limited. There are three types of globbing pattens we are familiar.

    * which translates to “match anything,” i.e., nothing, a single character, or many characters.

    [ ] (character class) which translates to “match any single character found inside the square brackets.” A dash (hyphen) can be used as a shorthand to specify a range of characters (which are contiguous in the ASCII character set). A few examples will make the functionality of a character class clear:

    [aeiou] matches any lowercase vowel

    [0-9] matches any digit

    [a-zA-Z0-9] matches any alphanumeric character

    ?any single character

    2.Regular Expression Patterns

    PATTERN MATCH
    . any single character
    [ ] character class: any single character that appears inside the brackets
    * quantifier: 0 or more of the preceding character (or group)
    + quantifier: 1 or more of the preceding character (or group)
    ? quantifier: 0 or 1 of the preceding character (or group)
    {1,5} quantifier: 1 through 5 of the preceding character (or group)
    | alternation: the character/group on the left or the character/group on the right
    ( ) grouping: often used with alternation and/or quantifier

    An example:description text in the web page with

    Sunrise: *[0-9]{1,2}:[0-9]{2} [ap]m

    Let’s examine the regular expression above one part at a time:

    Sunrise: * The string Sunrise: followed by 0 or more spaces
    [0-9]{1,2} 1 or 2 digits (for the hour of the day)
    : The character : (no special characters involved)
    [0-9]{2} 2 digits (for the minutes) followed by a space
    [ap]m “a” or “p” followed by “m” (am or pm)

    3.Exact Patterns

    if you needed to look for an actual asterisk character (which is special for both globbing and regular expression patterns), the exact pattern would be one way to do that. For example, if you wanted to select an item labeled “Real *” from a dropdown, the following code might work or it might not. The asterisk in the globbing pattern will match anything or nothing. So, if there was an earlier select option labeled “Real Numbers,” it would be the option selected rather than the “Real *” option.it equals "Real \*" in Regular Expression Patterns

  • EJB有哪几种类型(Bean)

    2011-04-11 13:59:45

    EJB 依照特性的不同,目前区分为三种,分别是 Session Bean ,Entity Bean ,以及 Message Driven Bean 。其中 Session Bean 与Entity Bean 算是 EJB 的始祖,这两种 EJB 在 EJB 规格 1.x 的时候就已经存在了,而 Message Driven Bean 则出现在 EJB 2.0 的规格中。


    Session Bean


    Session Bean 主要的目的是让程序开发者将逻辑层抽离,特别是复杂的逻辑可以放在 Session Bean 中。 Session Bean 还可以再细分为Stateful Session Bean 与 Stateless Session Bean ,这两种的 Session Bean都可以将系统逻辑放在 method 之中执行,不同的是 Stateful Session Bean 可以记录呼叫者的状态,因此通常来说,一个使用者会有一个相对应的 Stateful Session Bean 的实体 (Instance 注一 ) ,换言之,当使用者呼叫某个 Stateful Session Bean 的两个 methods 的时候,EJB Container( 注一 ) 会清楚的知道某个 EJB 的实体属于某一个使用者的。因此一般的设计上,不会让两个使用者同时使用某个 Stateful Session Bean ( 这并不是表示两个使用者不能使用同一个 Stateful Session Bean) 。


    Stateless Session Bean 虽然也是逻辑组件,但是他却不负责记录使用者状态,也就是说当使用者呼叫 Stateless Session Bean 的时候,EJB Container 并不会找寻特定的 Stateless Session Bean 的实体来执行这个 method ,换言之,很可能数个使用者在执行某个 Stateless Session Bean 的 methods 时,会是同一个 Bean 的 Instance 在执行。


    从内存方面来看, Stateful Session Bean 与 Stateless Session Bean 比较, Stateful Session Bean 会消耗 J2EE Server 较多的内存,然而 Stateful Session Bean 的优势却在于他可以维持使用者的状态。


    Entity Bean


    Entity Bean 主要是资料组件, Entity Bean 主要的目的,在于提供资料,也就是说程序设计师可以将 Entity Bean 当程序资料,至于 Entity Bean 实际上怎么存取实际上的数据库,那个则是另外一件事情。


    Entity Bean 实际上是针对 RDBMS 而设计,也就是说当其它的程序使用 Entity Bean 的时候, Entity Bean 的资料主要是从 RDBMS 而来,当然,如果程序设计师熟悉 Entity Bean 的运作,那么也可以很轻易的把RDBMS 用其它的数据库取代,像是 LDAP 。


    Entity Bean 主要区分为 Bean-Managed Persistence 以及 Container-Managed Persistence ( 简称 BMP 及 CMP) ,这两种 Entity Bean 的型态不同,但是目的相同,都在于提供资料!这两种 Entity Bean 主要的差别在于维护资料的角色, BMP 是由 Bean 自行维护资料的一致性,而 CMP 则是由 EJB Container 来维护。一个 Entity Bean 往往代表一张RDBMS 的表格,这个表格内的一笔一笔的资料,则是透过另外一个叫做 Primary Key( 注三 ) 的 Class 来加以区分。


    Bean-Managed Persistence


    当我们说 BMP 是由 Bean 自行维护资料的一致时,有些人会觉得太过于抽象,简单一点来说,原来的资料均由数据库而来,而当资料从资料库中取出之后,在 BMP 中需要自行宣告字段来存放这些资料,同时也要自行撰写相关的程序代码,包括相关的 JDBC 语法等等。


    Container-Managed Persistence


    CMP 是比较高阶的 Entity Bean ,比较高阶的意思是说,撰写 CMP 的程序设计师并不需要撰写大多数的 JDBC 语法,只需要透过一些定义,即可产生 CMP ,实际上的 EJB 的程序代码则是 EJB Container 依据相关的 Deployment Description ( 注四 ) 在部署(注五) EJB 的时候产生。


    BMP 与 CMP 的型态不同,适用的场合也不同,主要的差异在于如果我们想要自行控制所有的动作,那么应该使用 BMP , BMP 允许程序设计师完全的控制 BMP 的资料存取行为,而 CMP 很适合快速开发,但是由于要支持 CMP 的 J2EE Server 需要较高的技术,因此每一家对于CMP 的支持程度也不大相同。


    此外,数据库中有一个很重要的关系是 OR Mapping ,也就是常听到的一对一,一对多,多对多这种关系,在 EJB 1.x 的规格中,这种一对多的对应关系只有两种方式可以做出,一种是透过 Session Bean 来存取多个 Entity Bean ,另外一种则是透过 BMP 来控制另外数个 BMP 或是CMP ,但是在 EJB 2.0 的规格中,已经可以透过纯粹的 CMP 配合EJB-QL( 注六 ) 语法来做出这种对应关系,这也是 EJB 2.0 的一大特色。


    Message Driven Bean Message Driven Bean 与 Session Bean 或是 Entity Bean 均不相同,一般 Session Bean 或是 Entity Bean 都可以让使用者主动触发(可以在需要的时候,呼叫他们的 method 来触发他们),但是 Message Driven Bean 主要的目的在于反应 Message Queue 中的事件。


    也就是当 Message Queue 中有讯息传入时, Message Driven Bean 可以主动被触发,做出相对应的反应。因此如果说 Session Bean 与 Entity Bean 是同步模式的 EJB ( 使用者呼叫某个 Method ,就只能等 EJB 响应之后才能进行下一步 ) ,那么 Message Driven Bean 就可以当成是专门处理异步资料格式的 EJB ,也就是说程序设计师将某个讯息丢入 Message Queue 之后,就继续执行下去,另外一方面, Message Driven Bean 会受到通知,知道有某个讯息需要处理,这时候他会自行运作。因此 Message Driven Bean 并不是直接给使用者呼叫的,而是透过 MessageQueue 来触发。


    有状态会话bean :每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。

    有状态session bean生命周期
    1. 当客户调用create(args)时,容器调用newInstance()方法,剩下来的创建过程同无状态session bean。
    2. 有状态session bean的方法执行分为事务与非事务两种情况
    a. 不包含事务的方法在该bean处于ready状态后就可以执行
    b. 包含事务的方法执行就比较复杂:
    客户调用事务方法-->容器调用afterBegin()
    客户提交方法-->容器调用beforeCompletion()
    事务尝试提交,提交结果可能会成功,也可能会滚事务
    事务结束-->容器调用afterCompletion()

  • javascript 在Firefox与IE6 脚本下不同表现以及解决方法

    2011-03-23 10:52:13

    1.1 getElementById区别
    <select name="month">
      <option>Jan</option>
      <opton>Feb</option>
    </select>
    <select name="month">
      <option>Jan</option>
      <opton>Feb</option>
    </select> 
    IE中:document.getElementById("month")正确  document.getElementsByName("month")正确 
    Firefox: document.getElementById("month")错误(必须加属性Id) 
    document.getElementsByName("month")正确  故通用方法编写标准html语法,然后用document.getElementById. 
    1.2 FireFox不支持vbscrīpt 
    1.3 Firefox定位form
    Form定义 <form. name="userlist" id="userlist" method="post" action="userlist.do">
    仅仅IE支持直接访问:  userlist.pageSize.value="20";
     userlist.submit();
     通用函数  function goto(p) {
     document.forms["userlist"].page.value=p;
     document.forms["userlist"].pageSize.value="20";
     document.forms["userlist"].submit();
    }
    1.4 事件函数 
    仅仅IE支持document.onclick() {
    这种方式书写。 
    function document.onclick() {
      with(window.event.srcElement) {
       if (tagName != "INPUT" && getAttribute("Author") != "tiannet")  tiannetHideControl();
      }
    }
    在FireFox上提示:
    missing ( before formal parameters, javascrīpt error 
    1.5 Console关键字 
    <scrīpt type='text/javascrīpt'>  var http_request=null;
     alert(console);
     var console=null;
     </scrīpt> 
    在firefox上console为一个对象  setting a property that has only a getter 
    IE表现为未定义 
    1.6 事件处理 
    http://www.phpchina.com/12834/viewspace_29308.html 
    http://www.javaeye.com/topic/47917 
    如:
    function getEvent() {
    //同时兼容ie和ff的写法  if(document.all) return window.event;
     func=getEvent.caller;
     while(func!=null) {
     var arg0=func.arguments[0];
     if(arg0) {
     if((arg0.constructor==Event || arg0.constructor ==MouseEvent)  || (typeof(arg0)=="object" && arg0.preventDefault && arg0.stopPropagation)) {
     return arg0;
    }
    }
     func=func.caller;
    }
     return null;
    }
     //任意点击时关闭该控件  document.onclick=function() {
     var ōbjevent= getEvent();
    //window.event;
    //getEvent();
     if( typeof(objevent.srcElement) == "undefined") {
     with(objevent.target) {
     if (tagName != "INPUT" && getAttribute("Author") != "tiannet") {
     tiannetHideControl();
    }
    }
    }
     else {
     with(objevent.srcElement) {
     if (tagName != "INPUT" && getAttribute("Author") != "tiannet") {
     tiannetHideControl();
    }
    }
    }
    }
    1.7 Document.all 
    IE特有:  document.all.selTianMonth 
    通用  document.getElementById('selTianMonth') 
    1.8 innerText 
    http://www.cnlei.org/blog/article.asp?id=425 
    为Firefox下的DOM对象增加innerText属性: 
    <scrīpt type="text/javascrīpt">  var lBrowser = {};
     lBrowser.agt = navigator.userAgent.toLowerCase();
     lBrowser.isW3C= document.getElementById ? true:false;
     lBrowser.isIE = ((lBrowser.agt.indexOf("msie") != -1) && (lBrowser.agt.indexOf("opera") == -1) && (lBrowser.agt.indexOf("omniweb") == -1));
     lBrowser.isNS6 = lBrowser.isW3C&& (navigator.appName=="Netscape");
     lBrowser.isOpera = lBrowser.agt.indexOf("opera") != -1;
     lBrowser.isGecko = lBrowser.agt.indexOf("gecko") != -1;
     lBrowser.ieTrueBody =function () {
     return (document.compatMode && document.compatMode!="BackCompat")? document.documentElement : document.body;
    };
     //为Firefox下的DOM对象增加innerText属性  if(lBrowser.isNS6) {
    //firefox innerText define  HTMLElement.prototype.__defineGetter__( "innerText",  function() {
     return this.textContent;
    }
     );
     HTMLElement.prototype.__defineSetter__( "innerText",  function(sText) {
     this.textContent=sText;
    }
     );
    }
     alert(lBrowser.isNS6);
     </scrīpt> 
    1.9 eval  创建过程:
     for (var i=0;i<5;i++) {
     document.write (' <tr align=center id="trTiannetDay' + i + '" >');
     for (var j=0;
    j<7;
    j++) { document.write('<td align="center" id="tdTiannetDay' + n + '" '+  'onClick="tiannetDay=this.innerText;tiannetSetValue(true);" '  +' style="' + s_tiannet_day + '">&nbsp;</td>');
     n ++;
    }
     document.write (' </tr>');
    }
    IE特有:  for (var i = 0;i < 35;i++) {
     eval("document.all.tdTiannetDay"+i);
     通用:  var ōbjname="tdTiannetDay"+ i;
     var da = eval( document.getElementById(objname));
     if (typeof(da) == undefined) {
     alert("document.getElementById(tdTiannetDay"+i+"is null");
    }
     else {
     alert(da.tagName);
     //alert(typeof(da.constructor));
    }
     查找到一个区别:http://kongjian.baidu.com/freezesoul/blog/item/f6b7e5cd275752510eb34548.html  javascrīpt中dom对象和普通对象可以通过判断tagName属性来区别,但是这不是非常好的办法,因为你很难禁止他人在普通对象中定义tagName属性。另外一个比较好的方法是dom对象的constructor属性一般为undefined(同时,显然地,instanceof Object将返回false,这是它们区别其他对象的一个明显特征 
    1.10 execscrīpt
    如:window.execscrīpt("c = Asc(c)", "vbscrīpt");
    仅仅IE支持,不建议使用。

  • 关于STAF

    2011-03-14 16:23:00

    什么是STAF?

    STAFSoftware Testing Automation Framework的简称,它是一种开源的,跨平台的,多语言的框架,这个框架是为了满足一种被称作服务的(例如:处理新的扩展点,资源管理,产生日志和监视器)组件的可重用性而设计的。STAF框架剔除了在构建一个自动化基础结构中的冗余部分,因此您能够把重心放在自动化解决方案上。STAF框架提供了构建高水平的自动化解决方案的基础部分,并且可以提供插件以满足大量的测试平台和语言。

    STAF能够促使帮助解决普通的企业问题,例如频繁的产品周期,很少的准备时间,减少测试时间,多种平台的选择,多种开发语言的选择并且增加了国际化语言的需求。STAF之所以能够在这些方面起到帮助,是因为它的已经被证实了的和成熟的技术,提高自动化和重用,具有广泛的平台和多语言支持,并且提供了一种可跨项目的普通的基础结构。

    STAX是一种可执行引擎它能帮助你完全的自动分布,执行,产生测试用例分析结果。为了给测试员最大的自动化能力的帮助,STAX构建在三个技术之上的,这三个技术分别是STAF,XMLPythonSTAX也提供了极大的监控应用,它允许你交互并且监控您的工作进程。

     STAX的一些主要的特点:

    n         支持并行执行

    n         用户定义的渐进式执行控制

    n         支持用例嵌入

    n         支持执行时间长度的能力

    n         run-time导入模块

    n         支持现有的 Pythonjava模块和包

    n         扩展STAX语言和监视应用的能力

    使用这些特性,你可以构建复杂的脚本用于自动化您的整个测试环境,确保测试效率和控制的最大化。

      STAX提供放入其他服务能够帮助你创建端到端的自动化解决方案,通过在测试用例和自动化方案中使用这些服务,你能够开发更加健壮的,动态的测试用例和测试环境。

     

  • javascript读写Cookie的简单实例

    2011-03-09 10:44:13

    1. 代码
    <html>
    <head>
    <title>读写cookies</title>
    </head>
    <body>
    <script. language="JavaScript">
    document.cookie="a=a;"
    document.cookie="b=b;"
    var mycookie = document.cookie;
    function readcookie(name)
    {
    var start1 = mycookie.indexOf(name + "=");
    if (start1== -1)
    alert("cookies not found");
    else
    {
    start=mycookie.indexOf("=",start1)+1;
    var end = mycookie.indexOf(";",start);
    if (end==-1)
    {
     end=mycookie.length;}
     var value=unescape(mycookie.substring(start,end));
     if (value==null)
     {alert("No cookies found!");}
     else
     {alert("cookie变量:"+name+" 的值是 :"+value);}
     }
    }
    readcookie("a");
    readcookie("b");
    </script>
    </body>
    </html>
    2.3.2 一篇写的比较好的js cookie的文章(转)
    Cookies,有些人喜欢它们,有些人憎恨它们。但是,很少有人真正知道如何使用它们。现在你可以成为少数人中的成员-可以自傲的Cookie 大师。-->如果你象作者一样记性不好,那么你可能根本记不住人们的名字。我遇到人时,多半只是点点头,问句“吃了嘛!”,而且期望问候到此为止。如果还需要表示些什么,那么我就得求助于一些狡猾的技巧,好让我能想对方是谁。比如胡扯起一些和对方有关的人,不管他们之间关系多远,只要能避免不记得对方名字的尴尬就好: “你隔壁邻居的侄子的可爱小狗迈菲斯特怎么样?”通过这个方法,我希望能让对方感到,我确实很重视他(她),甚至还记得这些琐事,虽然实际上连名字都忘记了。但是,不是我不重视,而是我的记忆力实在是糟糕,而且要记住的名字又实在太多。如果我能给每个人设置cookies,那么我就不会再犯这种记忆力问题了。在这篇文章里,我们要学习:1. 什么是 Cookies?2. Cookie 的构成3. 操纵 Cookies4. Cookie 怪兽什么是Cookies?你会问,什么是cookies呢? cookie 是浏览器保存在用户计算机上的少量数据。它与特定的WEB页或WEB站点关联起来,自动地在WEB浏览器和WEB服务器之间传递。比如,如果你运行的是Windows操作系统,使用Internet Explorer上网,那么你会发现在你的“Windows”目录下面有一个子目录,叫做“Temporary Internet Files”。如果你有空看看这个目录,就会发现里面有一些文件,文件名称看起来就象电子邮件地址。比如在我机器上的这个目录里,就有 “jim@support.microsoft.com”这样的文件。这是一个cookie 文件,这个文件从哪来呢?猜一猜,它来自微软的支持站点。顺便说一句,这不是我的电子邮件地址,特此澄清。对于管理细小的、不重要的、不想保存在中央数据库里的细节信息,Cookies 是个很不错的方案。(这不是说大家的名字不重要。)比如,目前网站上不断增长的自定义服务,可以为每个用户定制他们要看的内容。如果你设计的就是这样一个站点,那么你怎么来管理这样的信息:一个用户喜欢绿色的菜单条,而另一个喜欢红色的。确实是个累人的问题。不过,这样的信息,可以很安全地记录到cookie,并保存在用户的计算机上,而你自己的数据库空间可以留给更长久更有意义的数据。FYI: Cookies 对于安全用途,通常很有用。我不想在此就这一问题过于深入,只是提供一个示例,可以看到如何使用在一段时间之后过期的cookies来保证站点安全:1. 使用用户名和口令,通过 SSL 登录。2. 在服务器的数据库里检查用户名和口令。如果登录成功,建立一个当前时间标签的消息摘要 (比如 MD5) ,并把它保存在cookie和服务器数据库里。把用户的登录时间保存在服务器数据库里面的用户记录里。3. 在进行每个安全事务时(用户处于登录状态的任何事务),把cookie的消息摘要和保存在服务器数据库里的摘要进行比较,如果比较失败,就把用户引导到登录界面。4. 如果第3步检查通过,那么检查当前时间和登录时间之音经过的时间是否超过允许的时间长度。如果用户已经超时,那么就把用户引到登录界面。5. 如果第3步和第4步都通过了,那么把登录时间重新设置成当前时间,允许事务发生。那些需要你登录的安全站点,可能多数使用的都是和这里介绍的类似的方法。Cookie的构成Cookies最初设计时,是为了CGI编程。但是,我们也可以使用Javascript脚本来操纵cookies。在本文里,我们将演示如何使用Javascript脚本来操纵cookies。(如果有需求,我可能会在以后的文章里介绍如何使用Perl进行cookie管理。但是如果实在等不得,那么我现在就教你一手:仔细看看CGI.pm。在这个CGI包里有一个cookie()函数,可以用它建立cookie。但是,还是让我们先来介绍cookies的本质。在Javascript脚本里,一个cookie 实际就是一个字符串属性。当你读取cookie的值时,就得到一个字符串,里面当前WEB页使用的所有cookies的名称和值。每个cookie除了name名称和value值这两个属性以外,还有四个属性。这些属性是: expires过期时间、 path路径、 domain域、以及 secure安全。Expires – 过期时间。指定cookie的生命期。具体是值是过期日期。如果想让cookie的存在期限超过当前浏览器会话时间,就必须使用这个属性。当过了到期日期时,浏览器就可以删除cookie文件,没有任何影响。Path – 路径。指定与cookie关联的WEB页。值可以是一个目录,或者是一个路径。如果http://www.zdnet.com/devhead/index.html 建立了一个cookie,那么在http://www.zdnet.com/devhead/目录里的所有页面,以及该目录下面任何子目录里的页面都可以访问这个cookie。这就是说,在http://www.zdnet.com/devhead/stories/articles 里的任何页面都可以访问http://www.zdnet.com/devhead/index.html建立的cookie。但是,如果http://www.zdnet.com/zdnn/ 需要访问http://www.zdnet.com/devhead/index.html设置的cookes,该怎么办?这时,我们要把cookies 的path属性设置成“/”。在指定路径的时候,凡是来自同一服务器,URL里有相同路径的所有WEB页面都可以共享cookies。现在看另一个例子:如果想让 http://www.zdnet.com/devhead/filters/ 和http://www.zdnet.com/devhead/stories/共享cookies,就要把path设成“/devhead”。Domain – 域。指定关联的WEB服务器或域。值是域名,比如zdnet.com。这是对path路径属性的一个延伸。如果我们想让 catalog.mycompany.com 能够访问shoppingcart.mycompany.com设置的cookies,该怎么办? 我们可以把domain属性设置成“mycompany.com”,并把path属性设置成“/”。FYI:不能把cookies域属性设置成与设置它的服务器的所在域不同的值。Secure – 安全。指定cookie的值通过网络如何在用户和WEB服务器之间传递。这个属性的值或者是“secure”,或者为空。缺省情况下,该属性为空,也就是使用不安全的HTTP连接传递数据。如果一个 cookie 标记为secure,那么,它与WEB服务器之间就通过HTTPS或者其它安全协议传递数据。不过,设置了secure属性不代表其他人不能看到你机器本地保存的cookie。换句话说,把cookie设置为secure,只保证cookie与WEB服务器之间的数据传输过程加密,而保存在本地的cookie文件并不加密。如果想让本地cookie也加密,得自己加密数据。操纵Cookies请记住,cookie就是文档的一个字符串属性。要保存cookie,只要建立一个字符串,格式是name=<value>(名称=值),然后把文档的 document.cookie 设置成与它相等即可。比如,假设想保存表单接收到的用户名,那么代码看起来就象这样:document.cookie = "username" + escape(form.username.value);在这里,使用 escape() 函数非常重要,因为cookie值里可能包含分号、逗号或者空格。这就是说,在读取cookie值时,必须使用对应的unescape()函数给值解码。我们当然还得介绍cookie的四个属性。这些属性用下面的格式加到字符串值后面:name=<value>[; expires=<date>][; domain=<domain>][; path=<path>][; secure]名称=<值>[; expires=<日期>][; domain=<域>][; path=<路径>][; 安全]<value>, <date>, <domain> 和 <path> 应当用对应的值替换。<date> 应当使用GMT格式,可以使用Javascript脚本语言的日期类Date的.toGMTString() 方法得到这一GMT格式的日期值。方括号代表这项是可选的。比如在 [; secure]两边的方括号代表要想把cookie设置成安全的,就需要把"; secure" 加到cookie字符串值的后面。如果"; secure" 没有加到cookie字符串后面,那么这个cookie就是不安全的。不要把尖括号<> 和方括号[] 加到cookie里(除非它们是某些值的内容)。设置属性时,不限属性,可以用任何顺序设置。下面是一个例子,在这个例子里,cookie "username" 被设置成在15分钟之后过期,可以被服务器上的所有目录访问,可以被"mydomain.com"域里的所有服务器访问,安全状态为安全。// Date() 的构造器设置以毫秒为单位// .getTime() 方法返回时间,单位为毫秒// 所以要设置15分钟到期,要用60000毫秒乘15分钟var expiration = new Date((new Date()).getTime() + 15 * 60000);document.cookie = "username=" + escape(form.username.value)+ "; expires ="+ expiration.toGMTString() + "; path=" + "/" + "; _domain=" + "mydomain.com" + "; secure";读取cookies值有点象个小把戏,因为你一次就得到了属于当前文档的所有cookies。// 下面这个语句读取了属于当前文档的所有cookiesvar allcookies = document.cookie;现在,我们得解析allcookies变量里的不同cookies,找到感兴趣的指定cookie。这个工作很简单,因为我们可以利用Javascript语言提供的扩展字符串支持。如果我们对前面分配的cookie "username" 感兴趣,可以用下面的脚本来读取它的值。// 我们定义一个函数,用来读取特定的cookie值。function getCookie(cookie_name){var allcookies = document.cookie;var cookie_pos = allcookies.indexOf(cookie_name);// 如果找到了索引,就代表cookie存在,// 反之,就说明不存在。if (cookie_pos != -1){// 把cookie_pos放在值的开始,只要给值加1即可。cookie_pos += cookie_name.length + 1;var cookie_end = allcookies.indexOf(";", cookie_pos);if (cookie_end == -1){cookie_end = allcookies.length;}var value = unescape(allcookies.substring(cookie_pos, cookie_end));}return value;}// 调用函数var cookie_val = getCookie("username");上面例程里的 cookie_val 变量可以用来生成动态内容,或者发送给服务器端CGI脚本进行处理。现在你知道了使用Javascript脚本操纵cookies的基本方法。但是,如果你跟我一样,那么我们要做的第一件事,就是建立一些接口函数,把cookies处理上的麻烦隐藏起来。不过,在你开始编程之前,稍候片刻。这些工作,早就有人替你做好了。你要做的,只是到哪去找这些接口函数而已。比如,在David Flangan的Javascript. The Definitive Guide 3rd Ed.这本书里,可以找到很好的cookie应用类。你也可以在Oreilly的WEB站点上找到这本书里的例子。本文最后的链接列表里,有一些访问这些cookie示例的直接链接。Cookies 怪兽因为某些原因Cookies 的名声很不好。许多人利用cookies做一些卑鄙的事情,比如流量分析、点击跟踪。Cookies 也不是非常安全,特别是没有secure属性的cookies。不过,即使你用了安全的cookies,如果你和别人共用计算机,比如在网吧,那么别人就可以窥探计算机硬盘上未加密保存的cookie文件,也就有可能窃取你的敏感信息。所以,如果你是一个WEB开发人员,那么你要认真考虑这些问题。不要滥用cookies。不要把用户可能认为是敏感的数据保存在cookies里。如果把用户的社会保险号、信用卡号等保存在cookie里,等于把这些敏感信息放在窗户纸下,无异于把用户投到极大危险之中。一个好的原则是,如果你不想陌生人了解你的这些信息,那就不要把它们保存在cookies里。另外,cookies还有一些实际的限制。Cookies保留在计算机上,不跟着用户走。如果用户想换计算机,那么新计算机无法得到原来的cookie。甚至用户在同一台计算机上使用不同浏览器,也得不到原来的cookie:Netscape 不能读取Internet Explorer 的cookies。还有,用户也不愿意接受cookies。所以不要以为所有的浏览器都能接受你发出的cookies。如果浏览器不接受cookies,你要保证自己的WEB站点不致因此而崩溃或中断。另外WEB 浏览器能保留的cookies不一定能超过300个。也没有标准规定浏览器什么时候、怎么样作废cookies。所以达到限制时,浏览器能够有效地随机删除cookies。浏览器保留的来自一个WEB服务器上的cookies,不超过20个,每个cookie的数据(包括名称和值),不超过4K字节。(不过,本文里的cookie尺寸没问题,它只占了12 K字节,保存在3个3 cookies里。)简而言之,注意保持cookie简单。不要依赖cookies的存在,不要在每个cookie里保存太多信息。不要保存太多的cookes。但是,抛除这些限制,在技巧高超的WEB管理员手里,cookie的概念是一个有用的工具。外部链接每个 Javascript. 程序员都应当有一份Javascript: David Flanagan 的The Definitive Guide。 这本书里找到cookie 类例程可以帮助你把不止一个变量编码到单一的cookie,克服掉“每个WEB服务器20 个cookies的限制”。

    2.3.3 JavaScript操作cookie
    最近做网站要在多个网页之间保存一些用户数据,网页又是静态的,参考了其它网站才知道原来JavaScript就可以操作cookie,特收集 了点这方面的知识如下:
    2.3.3.1 cookie概述
    cookie是浏览器提供的一种机制,它将document对象的cookie属性提供给JavaScript。可以由JavaScript对其进行控制,而并不是JavaScript本身的性质。cookie是存于用户硬盘的一个文件,这个文件通常对应于一个域名,当浏览器再次访问这个域名时,便使这个cookie可用。因此,cookie可以跨越一个域名下的多个网页,但不能跨越多个域名使用。
    不同的浏览器对cookie的实现也不一样,但其性质是相同的。例如在Windows 2000以及Windows xp中,cookie文件存储于documents and settings\userName\cookie\文件夹下。通常的命名格式为:
    userName@domain.txt
    cookie机制将信息存储于用户硬盘,因此可以作为全局变量,这是它最大的一个优点。它可以用于以下几种场合。
    ? 保存用户登录状态。例如将用户id存储于一个cookie内,这样当用户下次访问该页面时就不需要重新登录了,现在很多论坛和社区都提供这样的功能。cookie还可以设置过期时间,当超过时间期限后,cookie就会自动消失。因此,系统往往可以提示用户保持登录状态的时间:常见选项有一个月、三个月、一年等。
    ? 跟踪用户行为。例如一个天气预报网站,能够根据用户选择的地区显示当地的天气情况。如果每次都需要选择所在地是烦琐的,当利用了cookie后就会显得很人性化了,系统能够记住上一次访问的地区,当下次再打开该页面时,它就会自动显示上次用户所在地区的天气情况。因为一切都是在后台完成,所以这样的页面就像为某个用户所定制的一样,使用起来非常方便。
    ? 定制页面。如果网站提供了换肤或更换布局的功能,那么可以使用cookie来记录用户的选项,例如:背景色、分辨率等。当用户下次访问时,仍然可以保存上一次访问的界面风格。
    ? 创建购物车。正如在前面的例子中使用cookie来记录用户需要购买的商品一样,在结账的时候可以统一提交。例如淘宝网就使用cookie记录了用户曾经浏览过的商品,方便随时进行比较。
    当然,上述应用仅仅是cookie能完成的部分应用,还有更多的功能需要全局变量。cookie的缺点主要集中于安全性和隐私保护。主要包括以下几种:
    ? cookie可能被禁用。当用户非常注重个人隐私保护时,他很可能禁用浏览器的cookie功能;
    ? cookie是与浏览器相关的。这意味着即使访问的是同一个页面,不同浏览器之间所保存的cookie也是不能互相访问的;
    ? cookie可能被删除。因为每个cookie都是硬盘上的一个文件,因此很有可能被用户删除;
    ? cookie安全性不够高。所有的cookie都是以纯文本的形式记录于文件中,因此如果要保存用户名密码等信息时,最好事先经过加密处理。
    2.3.3.2 设置cookie

    每个cookie都是一个名/值对,可以把下面这样一个字符串赋值给document.cookie:
    document.cookie="userId=828";
    如果要一次存储多个名/值对,可以使用分号加空格(; )隔开,例如:
    document.cookie="userId=828; userName=hulk";
    在cookie的名或值中不能使用分号(;)、逗号(,)、等号(=)以及空格。在cookie的名中做到这点很容易,但要保存的值是不确定的。如何来存储这些值呢?方法是用escape()函数进行编码,它能将一些特殊符号使用十六进制表示,例如空格将会编码为“20%”,从而可以存储于cookie值中,而且使用此种方案还可以避免中文乱码的出现。例如:
    document.cookie="str="+escape("I love ajax");
    相当于:
    document.cookie="str=I%20love%20ajax";
    当使用escape()编码后,在取出值以后需要使用unescape()进行解码才能得到原来的cookie值,这在前面已经介绍过。
    尽管document.cookie看上去就像一个属性,可以赋不同的值。但它和一般的属性不一样,改变它的赋值并不意味着丢失原来的值,例如连续执行下面两条语句:
    document.cookie="userId=828";
    document.cookie="userName=hulk";
    这时浏览器将维护两个cookie,分别是userId和userName,因此给document.cookie赋值更像执行类似这样的语句:
    document.addCookie("userId=828");
    document.addCookie("userName=hulk");
    事实上,浏览器就是按照这样的方式来设置cookie的,如果要改变一个cookie的值,只需重新赋值,例如:
    document.cookie="userId=929";
    这样就将名为userId的cookie值设置为了929。
    2.3.3.3 获取cookie的值

    下面介绍如何获取cookie的值。cookie的值可以由document.cookie直接获得:
    var strCookie=document.cookie;
    这将获得以分号隔开的多个名/值对所组成的字符串,这些名/值对包括了该域名下的所有cookie。例如:
    <script. language="JavaScript" type="text/javascript">
    <!--
    document.cookie="userId=828";
    document.cookie="userName=hulk";
    var strCookie=document.cookie;
    alert(strCookie);
    //-->
    </script>
       由此可见,只能够一次获取所有的cookie值,而不能指定cookie名称来获得指定的值,这正是处理cookie值最麻烦的一部分。用户必须自己分析这个字符串,来获取指定的cookie值,例如,要获取userId的值,可以这样实现:
    <script. language="JavaScript" type="text/javascript">
    <!--
    //设置两个cookie
    document.cookie="userId=828";
    document.cookie="userName=hulk";
    //获取cookie字符串
    var strCookie=document.cookie;
    //将多cookie切割为多个名/值对
    var arrCookie=strCookie.split("; ");
    var userId;
    //遍历cookie数组,处理每个cookie对
    for(var i=0;i<arrCookie.length;i++){
           var arr=arrCookie[i].split("=");
           //找到名称为userId的cookie,并返回它的值
           if("userId"==arr[0]){
                  userId=arr[1];
                  break;
           }
    }
    alert(userId);
    //-->
    </script>
    这样就得到了单个cookie的值
    用类似的方法,可以获取一个或多个cookie的值,其主要的技巧仍然是字符串和数组的相关操作。
    2.3.3.4 给cookie设置终止日期
    到现在为止,所有的cookie都是单
    2.3.4 javascript操作cookie
    问题:
        使得在访问页面的时候能够沿用上次的设置,或者在不同的页面间共享数据。比如用户在访问网站的时候设置了页面字体的大小,那么会希望下次访问的时候仍然能使用同样的设置进行浏览,而不用重复设置。
    解决方案:
        在用户浏览页面并进行设置时,将这些设置保存在cookie中,下次访问的时候读取cookie中的设置。
        参考下面的脚本:
        // utility function to retrieve an expiration data in proper format;
        function getExpDate(days, hours, minutes)
        {
            var expDate = new Date();
            if(typeof(days) == "number" && typeof(hours) == "number" && typeof(hours) == "number")
            {
                expDate.setDate(expDate.getDate() + parseInt(days));
                expDate.setHours(expDate.getHours() + parseInt(hours));
                expDate.setMinutes(expDate.getMinutes() + parseInt(minutes));
                return expDate.toGMTString();
            }
        }
        //utility function called by getCookie()
        function getCookieVal(offset)
        {
            var endstr = document.cookie.indexOf(";", offset);
            if(endstr == -1)
            {
                endstr = document.cookie.length;
            }
            return unescape(document.cookie.substring(offset, endstr));
        }
        // primary function to retrieve cookie by name
        function getCookie(name)
        {
            var arg = name + "=";
            var alen = arg.length;
            var clen = document.cookie.length;
            var i = 0;
            while(i < clen)
            {
                var j = i + alen;
                if (document.cookie.substring(i, j) == arg)
                {
                    return getCookieVal(j);
                }
                i = document.cookie.indexOf(" ", i) + 1;
                if(i == 0) break;
            }
            return;
        }
        // store cookie value with optional details as needed
        function setCookie(name, value, expires, path, domain, secure)
        {
            document.cookie = name + "=" + escape(value) +
                ((expires) ? "; expires=" + expires : "") +
                ((path) ? "; path=" + path : "") +
                ((domain) ? "; domain=" + domain : "") +
                ((secure) ? "; secure" : "");
        }
        // remove the cookie by setting ancient expiration date
        function deleteCookie(name,path,domain)
        {
            if(getCookie(name))
            {
                document.cookie = name + "=" +
                    ((path) ? "; path=" + path : "") +
                    ((domain) ? "; domain=" + domain : "") +
                    "; expires=Thu, 01-Jan-70 00:00:01 GMT";
            }
        }
        使用getCookie(name)函数来读取cookie中保存的值,参数name为cookie项的名称。如果该cookie项不存在则返回一个空字符串。
        使用setCookie()函数来保存cookie项的值,其中第一、二两个参数分别为cookie项的名称和值。如果想为其设置一个过期时间,那么就需要设置第三个参数,这里需要通过getExpDate()获得一个正确格式的参数。
        最后,使用deleteCookie()来删除一个已存在的cookie项,实际上是通过让该项过期。
        cookie将数据保存在客户端。页面的脚本只能读取所在域和服务器的cookie值,如果域内有多个服务器,那么需要设置第五个参数,以指定服务器。浏览器的容量一般限定为每服务器20个name/value对,每个cookie项不超过4000个字符,更现实点,单个cookie项应少于2000字符,也就是说不要用cookie在客户端保存大容量数据。
        不同的浏览器保存cookie的方式也有所不同。IE为每个域的cookie建立一个文本文件,而Netscape则将所有的cookie存储在同一个文本文件中。
        注意:cookie存放在客户端,所以会受到浏览器设置的影响,比如用户可能会禁用cookie。要检测浏览器是否支持cookie,使用属性navigator.cookieEnabled来判断。
    2.3.5   js操作cookie的类
    <script. language="JavaScript">
    function Cookie(delim){//操作Cookie类
    Array.prototype.splice=function(){
    var len=arguments.length,tarray=[],i=arguments[0]+arguments[1];
    if(len>1){
    while(i<this.length)tarray[tarray.length]=this[i++];
    this.length=arguments[0];
    if(len>2)for(var i=2;i<len;i++)this[this.length]=arguments[i];
    var tlen=tarray.length,i=0;
    while(i<tlen)this[this.length]=tarray[i++];
    }
    return this;
    }
    this._Cookie=[];
    this.Load=function(){
    if(document.cookie.indexOf(";")!=-1){
    var _sp,_name,_tp,_tars,_tarslength;
    var _item=document.cookie.split("; ");
    var _itemlength=_item.length;
    while(_itemlength>0){
    _sp=_item[--_itemlength].split("=");
    _name=_sp[0];
    _tp=_sp[1].split(",");
    _tars=_tp.slice(1,_tp.length);
    this._Cookie[_name]=[];
    this._Cookie[_name]=_tars;
    this._Cookie[_name]["timeout"]=_tp[0];
    }
    return true;
    }
    return false;
    }
    this.Save=function(){
    var _str,_ars,_mars,_marslength,timeout,i,key;
    for(key in this._Cookie){
    if(!this._Cookie[key])return;
    _str=[];
    _mars=CookieClass._Cookie[key];
    _marslength=_mars.length;
    for(i=0;i<_marslength;i++)_str[_str.length]=escape(_mars[i]);
    document.cookie=key+"="+_mars["timeout"]+(_str.length>0?",":"")+_str+";expires="+new Date(parseInt(_mars["timeout"])).toGMTString();
    }

    }
    this.GetCookieCount=function(){
    var _length=0,key;
    for(key in this._Cookie)_length++;
    return _length;
    }
    this.Create=function(name,days){
    this._Cookie[name]=[];
    this._Cookie[name]["timeout"]=new Date().getTime()+days*86400000;
    }
    this.Modify=function(name,days){
    this.Create(name,days);
    }
    this.GetTime=function(name){
    return new Date(parseInt(this._Cookie[name]["timeout"]));
    }
    this.Delete=function(name){
    this.Create(name,0);
    }
    this.AddItem=function(name,value){
    this._Cookie[name][this._Cookie[name].length]=value;
    }
    this.DelItem=function(name,index){
    this._Cookie[name].splice(index,1);
    }
    this.GetCount=function(name){
    return this._Cookie[name].length;
    }
    this.GetItem=function(name,index){
    return this._Cookie[name][index];
    }
    }
    </script>
    <script. language="JavaScript">
    /*
    ====================================
    Design :Flashsoft
    ====================================
    浏览器能够通过Cookies保留有关数据。象Windows的注册表一样,用户不必知道Cookies的具体位置,浏览器能找到这些数据。第六代的浏览器,不管是IE还是NS都支持document.cookie属性。通过这个属性来读取或修改Cookies的值。不过Cookies的存储形式是非结构化的长字符串,需要经过相应的解析后才有意义。

    Cookies的表达如下,除了name=value以外,其它均为可选:
    name=value;
    expires=date;
    domain=domainname
    path=pathname;
    secure;

    例如:
    User=HockeyDude; expires=Thu,01-Jan-70 00:00:01 GMT; domain=www.mydomain.com; path=/images; secure;
    Pass=Gretzky; expires=Thu,01-Jan-70 00:00:01 GMT; domain=www.mydomain.com; path=/images; secure;

    这么长的两个字符串只代表了两个Cookies。如果还要再加上电子信箱或其他信息就还得加长字符串。通常都是通过分解这样的字符串来取得各个变量或元素的。这实在是费时费力的事。

    使用面向对象设计(Object Oriented Design,OOD)的思路来编写Cookies处理函数,其特点如下:

    便于增删子项。这是很重要的,有些浏览器限制Cookies的使用数量。
    通过修改函数可以容易地修改时效数据。通常的做法很麻烦,要拷贝Cookies,删除原Cookies,修改并重写Cookies。
    Cookies和它的子项存放在数组里。可以根据需要快速而有效地进行修改。这样也无须解析那长长的字符串。

    Cookies对象的使用
    以下是对象的公有方法:

    方括号[]内是可选参数
    //构造
    Cookie([定界符,缺省为句点]) - 构造函数

    //初始化
    GetCookieCount() - 返回Cookies数量
    Create(name, days) - 创建Cookies及其时效天数
    Modify(name, days) - 修改Cookies的时效天数
    Delete(name) - 删除Cookies及其子项
    GetTime(name) - 返回指定Cookies的过期时间
    GetCount(name) - 返回Cookies的子项数量
    AddItem(name,value) - 增加一个子项
    GetItem(name,index) - 返回指定索引的子项
    DelItem(name,index) - 删除指定的子项

    //存取
    Load() - 读取Cookies
    Save() - 存储Cookies

    下面是应用实例:
    */
    var CookieClass=new Cookie();
    if(!CookieClass.Load()){
    CookieClass.Create("Pass",1);
    CookieClass.Create("User",1);
    CookieClass.AddItem("Pass","Ps1");
    CookieClass.AddItem("Pass","Ps2");
    CookieClass.AddItem("Pass","Ps3");
    CookieClass.AddItem("Pass","Ps4");
    CookieClass.AddItem("Pass","Ps5");
    CookieClass.AddItem("Pass","Ps6");
    CookieClass.DelItem("Pass",1);
    CookieClass.Save();
    }
    alert("Cookie过期时间:"+CookieClass.GetTime("Pass").toLocaleString());
    alert(document.cookie);
    </script>

    <script. language="Javascript">
    function eyunCookie(){
    this.key="";//初始化key。
    this.value="";//初始化key's value。
    this.expires=0;//初始化cookie的有效时间,单位毫秒。
    this.init=function(){//对象初始化
    this.key="";
    this.value="";
    this.expires=0;
    }
    this.set=function(key,value,expires){//设置cookie
    if(this.key=="")this.key=key;
    if(this.value=="")this.value=value;
    if(this.expires<=0)this.expires=expires;
    if(this.key==""||typeof(this.key)!="string"){
    alert("请先设置欲保存的cookie名称!");
    this.init();
    return false;
    }
    if(this.key.match(/[,; ]/)){
    alert("cookie名称中不能包含“,”、“;”或空格!");
    this.init();
    return false;
    }
    if(this.value.toString().match(/[,; ]/)||typeof(this.value)=="undefined"){
    alert("cookie值中不能包含“,”、“;”或空格!");
    this.init();
    return false;
    }
    if(this.expires<=0||typeof(this.expires)!="number"){
    alert("请先正确设置cookie的有效时间!");
    this.init();
    return false;
    }
    var cookie=document.cookie;
    if(cookie.indexOf(this.key+"=")!=-1){
    if(!confirm("欲保存的cookie名称已经存在,是否要进行替换?")){
    this.init();
    return false;
    }
    }
    var dt=new Date();
    dt.setTime(dt.getTime()+this.expires);
    document.cookie=this.key+"="+this.value+";expires="+dt.toGMTString();
    this.init();
    return true;
    }

    this.get=function(key){//取得名为key的cookie的值
    if(key==""||key.match(/[,; ]/)){
    alert("请正确设置欲查找的cookie名称!")
    return false;
    }
    var cookie=document.cookie;
    var start=cookie.indexOf(key+"=");
    if(start==-1){
    alert("欲查找的cookie不存在!")
    return false;
    }
    var end=cookie.indexOf(";",start);
    if(end==-1)end=cookie.length;
    var getCookie=cookie.substring(start+key.length+1,end);
    alert("cookie:"+key+"的值为"+getCookie);
    return getCookie;
    }
    this.showAll=function(){//显示所有cookie
    alert("共有以下cookie对:\n"+document.cookie.split(";").toString().replace(/,/g,"\n"));
    }
    this.del=function(key){//删除名为key的cookie
    if(key==""||key.match(/[,; ]/)){
    alert("请正确设置欲删除的cookie名称!")
    return false;
    }
    var dt=new Date();
    dt.setTime(dt.getTime());
    document.cookie=key+"=eyunDelete;expires="+dt.toGMTString();
    this.init();
    return true;
    }
    this.destroy=function(){//销毁所有cookie
    var dt=new Date();
    dt.setTime(dt.getTime());
    while(document.cookie!=""){
    document.cookie=document.cookie+";expires="+dt.toGMTString();
    this.init();
    return true;
    }
    }
    }
    var cookieTest=new eyunCookie()
    function settest(){
    cookieTest.key="test"
    cookieTest.value="ok"
    cookieTest.expires=31536000000
    cookieTest.set()
    }
    </script>

    <input type=button nclick=cookieTest.showAll() value=read>
    <input type=button nclick="cookieTest.set('a','test',31536000000)" value=setA>
    <input type=button nclick="settest();" value=setTest>
    <input type=button nclick="cookieTest.destroy()" value=clear>
    <input type=button nclick=cookieTest.get("test") value=gettest>
    <input type=button nclick=cookieTest.get("a") value=geta>
    <input type=button nclick=cookieTest.set("test",1,31536000000) value=resetTest>
    <input type=button nclick=cookieTest.del("test") value=delTest>

     

    Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1404572
    2.3.6 js操作Cookie的相关函数
    <script. language ="javascript" type="text/javascript">
    //----设置Cookie------
             function setCookie(name,value,domain)
             {
                 var value = escape(value);
                 var nameString = "Drag_"+name + "=" + value; //给cookie变量增加前缀
                 var extime = new Date();           
                 extime.setTime (extime.getTime () + 315360000);
                 var expiryString = ";expires=" + extime.toGMTString();
                 var domainString = "";
                 var pathString = ";path=/";   
                 var setvalue = nameString + expiryString;
                     document.cookie = setvalue;
             }
             //----设置Cookie 结束------
             //---读取Cookie-------
             function getcookie (name)
             {
                 var CookieFound = false;
                 var start = 0;
                 var end = 0;
                 var CookieString = document.cookie;
               
                 var i = 0;
                 name="Drag_"+name; //增加前缀
                 while (i <= CookieString.length)
                 {
                     start = i ;
                     end = start + name.length;
                     if (CookieString.substring(start, end) == name)
                     {
                         CookieFound = true;
                         break;
                     }
                     i++;
                 }

                 if (CookieFound)
                 {
                     start = end + 1;
                     end = CookieString.indexOf(";",start);
                     if (end < start)
                         end = CookieString.length;
                     var getvalue = CookieString.substring(start, end);
                     return unescape(getvalue);
                 }
                 return "";
             }

             //---读取Cookie 结束-------
               
             //检测是否禁用了cookie
             function isEnableCookie() {               
                 try {
                 var cookie_helper = new CookieHelper("");
                 var test_cookie_name = "test_cookie_name";
                 var test_cookie_value = "test_cookie_value";
                 var test_cookie_value_ret = "";
               
                 cookie_helper.writeCookie(test_cookie_name,test_cookie_value);
                 test_cookie_value_ret = cookie_helper.getCookieValue(test_cookie_name);           
               
                 if ( test_cookie_value_ret != null ) {
                     cookie_helper.removeCookie(test_cookie_name);
                     return true;
                 } else {               
                     return false;
                 }                   
             } catch (error) {           
                 return false;
             }
         }   


    </script>

  • 测试计划文档举例

    2011-03-02 11:18:07

    测试计划模板.zip(43.6 KB)附件是软件测试计划文档举例。

    FYI~

     

  • 关于globalization testing的知识

    2011-03-02 11:13:09

    Globalization Testing.zip(11.9 KB)附件是关于globalization相关的文本
  • 关于jacob的用法

    2011-01-25 16:01:10

    很多人用java进行文档操作时经常会遇到一个问题,就是如何获得word,excel,pdf等文档的内容?现在总结抽取word,pdf的一种方法。

    其实jacob是一个bridage,连接java和com或者win32函数的一个中间件,jacob并不能直接抽取word,excel等文件,需要自己写dll哦,不过已经有为你写好的了,就是jacob的作者一并提供了。

    jacob jar与dll文件下载: http://www.matrix.org.cn/down_view.asp?id=13

    下载了jacob并放到指定的路径之后(dll放到path,jar文件放到classpath),就可以写你自己的抽取程序了,下面是一个简单的例子:

    import java.io.File;
    import com.jacob.com.*;
    import com.jacob.activeX.*;
    /**
    * Title: pdf extraction
    * @version 1.0,who use this example pls remain the declare
    */
    public class FileExtracter{
    public static void main(String[] args) {
    ActiveXComponent component = new ActiveXComponent("Word.Application");
    String inFile = "c:\\test.doc";
    String tpFile = "c:\\temp.htm";
    String tFile = "c:\\temp.xml";
    boolean flag = false;
    try {
    component.setProperty("Visible", new Variant(false));
    Object wordacc = component.getProperty("document.").toDispatch();
    Object wordfile = Dispatch.invoke(wordacc,"Open", Dispatch.Method,
    new Object[]{inFile,new Variant(false), new Variant(true)},
    new int[1] ).toDispatch();
    Dispatch.invoke(wordfile,"SaveAs", Dispatch.Method, new Object[]{tpFile,new Variant}, new int[1]);
    Variant f = new Variant(false);
    Dispatch.call(wordfile, "Close", f);
    flag = true;
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    component.invoke("Quit", new Variant[] {});
    }
    }
    }

  • Derby 数据库使用笔记

    2011-01-12 10:33:20

    一、关于Derby

           Derby是Apache Software Foundation (ASF)的一个的孵化器项目。它是由IBM把自己的 Cloudscape(一种纯关系 Java 数据库)数据库作为开放源代码发布给 Apache Software Foundation (ASF)而建立的。
    Cloudscape 是一种基于 Java 的、具有全面事务支持能力的关系数据库技术。它是一种纯嵌入式数据库,可以用在应用程序中,也可以作为更传统的客户机-服务器应用程序的数据库。它体积小,而且不需要数据库管理员;您只需编写应用程序。在需要时直接调用数据库,Cloudscape 就可以为您服务。

    IBM Cloudscape 的未来商业版本都将以 Derby 代码为基础。

    Cloudscape 最有趣的一个用法就是作为一个 Java 数据存储,直接将数据库输入到 Java 应用程序中。我曾提到过,Cloudscape 数据库非常小。它只是一个 2MB 的 .jar 文件。有了这样一个 Java 数据库,当一个 Java 应用程序想要使用它时,根本不需要做任何工作,不需要进行任何管理。Java 应用程序只需发出 JDBC 调用,然后访问数据,之所以可以这样做,是因为数据库自始至终都是以一个 .jar 文件的形式存在。


    Cloudscape 能满足新的和正在增长的市场的需求。作为面向 Java 的数据库,Cloudscape 的定位非常恰当,它很好地弥补了IBM的其他数据库:DB2 Universal Database、Informix Dynamic Server、DB2 Express 等等。又因为它是建立在开放标准基础上的,任何针对 Cloudscape 编写的应用程序都可以很容易地迁移到 DB2 UDB 和其他数据库上运行,这正是企业级的解决方案所需要的。

    与其他开放源代码数据库相比,Derby 是独一无二的。它的确是功能丰富的关系型数据库。而且,它完全是免费提供的,没有商业许可限制。

    MyEclipse Derby 服务器:自动进行JDBC连接;可配置的启动选项。

    是一个内嵌在MyEclipse中的数据库,据说性能优于Mysql和Access。(Derby是小型数据库)


    二、使用Derby

    1、启动Derby服务:在MyEclipse7自带Derby,而且在默认启动时候就会启动Derby服务,位置同应用服务器在一起,名字叫MyEclipse Derby。

    2、建立自己的数据库:首先切到Database Explorer视图上,你可以通过COPY MyEclipse Derby来生成默认的配置,然后增加链接名并修改数据库名,切记如果是新建数据库 请在 数据库的名字后面增加  ;create=true 的参数,这里的链接用户名和密码就是你将要创建的用户名密码, 也可随意。

    例如:

    Driver template: 选择derby;

    Driver name: 随便比如 MyDB;

    Connection URL : jdbc:derby://localhost:1527/MyDb;create=true

    Username:zhangsan Password:123456

    Driver JARs:

    C:\Program Files\Genuitec\MyEclipse 8.5\configuration\org.eclipse.osgi\bundles\13\1\.cp\lib\derbyclient.jar;

    Driver classname = org.apache.derby.jdbc.ClientDriver

    3、构建权限用户表: 成功创建并链接后,使用下面语句建立用户管理表:

    create table <用户名>.userinfo(
      user_id int primary key,
      user_name varchar(15)
    );
    --插入用户数据
    insert into <用户名>.userinfo(1,'<用户名>');

    三、Derby在Hibernate上的配置

    1、链接字符串URL 格式为:jdbc:derby://<DB服务器地址>:<端口默认为1527>/<DB名><;参数 如create=true>
    2、方言
    Dialect为 org.hibernate.dialect.DerbyDialect

  • 黑盒测试方法之因果图法实例

    2011-01-11 17:26:15

    实例1:

    软件规格说明中包含这样的要求:
    第一列字符必须是A或B,第二列字符必须是一个数字,在此情况下进行文件的修改。但如果第一列字符不正确,则给出信息L;如果第二列字符不是数字,则给出信息M。

    分开原因和结果

    原因:1----第一列字符是A;
    2----第一列字符是B;
    3----第二列字符是一数字。
    结果:21----修改文件;
    22----给出信息L;
    23----给出信息M。

    此例子是讲解利用因果图设计测试用例的一个小例子。以中国象棋中走马的测试用例设计为例学习因果图的使用方法。

    一、 分析中国象棋中走马的实际情况(下面未注明的均指的是对马的说明)
    1、如果落点在棋盘外,则不移动棋子;2、如果落点与起点不构成日字型,则不移动棋子;3、如果落点处有自己方棋子,则不移动棋子;4、如果在落点方向的邻近交叉点有棋子(绊马腿),则不移动棋子;5、如果不属于1-4条,且落点处无棋子,则移动棋子;6、如果不属于1-4条,且落点处为对方棋子(非老将),则移动棋子并除去对方棋子;7如果不属于1-4条,且落点处为对方老将,则移动棋子,并提示战胜对方,游戏结束。

    二、 根据分析明确原因和结果

    原因:
    1、 落点在棋盘上;
    2、 落点与起点构成日字;
    3、 落点处为自己方棋子;
    4、 落点方向的邻近交叉点无棋子;
    5、 落点处无棋子;
    6、 落点处为对方棋子(非老将);
    7、 落点处为对方老将。
    结果:
    21、不移动棋子;
    22、移动棋子;
    23、移动棋子,并除去对方棋子;
    24、移动棋子,并提示战胜对方,结束游戏。

    添加中间节点11,目的是作为导出结果的进一步原因,简化因果图导出的判定表

    考虑结果不能同时发生,所以对其施加唯一约束O。原因5、6、7不能同时发生,所以对其施加异约束E.

    根据因果图建立判定表:(分为两表)

    注:1、以上判定表中由于表格大小限制没有列出最后所选的测试用例;2、第2表中部分列被合并表示不可能发生的现象;3、通过中间节点将用例的判定表简化为两个小表。减少工作量。

    四、根据判定表写测试用例表(略)

  • struts2.0中namespace问题及解决方法

    2011-01-07 09:50:15

       在struts.xml中配置了namespace="/"但是在页面上的form中没有配置namespace="/",然后运行就出现了:org.apache.struts2.components.Form. evaluateExtraParamsServletRequest
    警告: No configuration found for the specified action: '****' in namespace: ''. Form. action defaulting to 'action' attribute's literal value.。

    解决办法总结下:

    一:如果jsp页面用的struts2标签,则xml中有namespace,页面的form中也必须有namespace同时form提交时那个action可以直接写xml中的<action name="login" class="action.LoginAction">中的name属性的值,不需要加.action,如果有namespace的话

    二:如果jsp页面用的struts1标签,则form提交时那个action必须写上action的绝对路径,如:/login.action,需要加.action,和namespace。


  • waitr基本语句

    2010-12-14 15:53:30

    1.   请求watir

    require ‘watir’

    2.   访问web页面

    test_site = ‘http://192.168.1.178:9992/TOOLKIT/User/SignIn.aspx’

    3.   打开一个新的ie

    a)   ie = Watir::IE.new

    b)   Watir::Browser.default = ‘firefox’

    Browser = Watir::Browser.new

    Browser.goto(“http://192.168.1.178:9992/TOOLKIT/User/SignIn.aspx”)

    4.   进入到测试web页面

    ie.goto(test_site)

    5.   输入文本

    ie.text_field(:name,”ctl00$ContentPlaceHolderBody$TextBoxUserName”).set(“Jane.liu”)

    6.   点击按钮

      ie.button(:name,”ctl00$ContentPlaceHolderBody$ButtonSignIn”).click

    7.   进入到新的页面

      Ie = Watir::IE.attach(:title, ‘Select OPEN Appraisal’)

    8.   预期结果

    If ie.contains_text(“1GCGG256291100045”)

    puts “test passed!”

    else

    puts “Test Failed!”

    end

    9.   下拉列表

    ie.select_list(:name, 'name').select_value('value')

    10. 上传图片

    ie.file_field(:id,”the_file”).set(“c:\\image.jpg”)

    *如果是中文操作系统,那么需要修改C:\ruby\lib\ruby\gems\1.8\gems\watir-1.5.6\watir\input_elements.rb文件中找到filefield类下的set方法修改中间的“choose file成“选择文件”(其中“选择文件”是根据自己操作系统的弹出对话框的title来决定的。因为有些即使是英文操作系统但是“choose file to upload”就需要也进行修改。

    并且需要去掉button2按钮。

    system("rubyw -e \"require 'win32ole'; @autoit=WIN32OLE.new('AutoItX3.Control'); waitresult=@autoit.WinWait '选择文件', '', 15; sleep 1; if waitresult == 1\" -e \"@autoit.ControlSetText '选择文件', '', 'Edit1', '#{setPath}'; @autoit.ControlSend '选择文件', '', '', '{ENTER}';\" -e \"end\"")

    11. combobox

    ie.image(:id,”ext-gen11”).click

    ie.div(:text,”Changshaone”).click

    12. radio button

    ie.radio(:id,”ct100_ContentPlaceHolderBody_rbviClean”).set

    13. 输出结构判断

    if ie.text.include? "Congratulations,vehicle info has sent to lot successfully!"

     puts "Test Passed. complete a Appraisal . Actual Results match Expected Results."

    else

     puts "Test Failed! Don't complete a apprasial."

    14Test suite

        require "#{File.dirname(__FILE__)}/LiveNetPriceCheck.rb"

    15undefined method `radio' for nil:NilClass

       因为:id"ctl00_ContentPlaceHolderBody_RadioNew"之间多了个空格

    $ie.radio(:id,"ctl00_ContentPlaceHolderBody_RadioNew").set

    16IE窗口最大化

    ie.maximize()

    运行cmd

    regsvr32 C:\ruby\lib\ruby\gems\1.8\gems\watir-1.5.4\watir\AutoItX3.dll

  • 用watir控制IE对象

    2010-12-14 15:38:54

    1、控制超级链接:
     源代码如:<a href=>Pickaxe</a>这样的代码,可以有两种方法来控制
    使用文本属性(Text Attribute)
    ie.link(:text, "Pickaxe").click
    使用url属性
    ie.link(:url,  ).click
    2、控制checkbox:
    源代码如:<input type = "checkbox" name = "checkme" value = "1">,可以用两种方法进行控制:
    使用name属性:
    ie.checkbox(:name, "checkme").set     #选中
    ie.checkbox(:name, "checkme").clear  #清空
    使用name属性和value属性:
    ie.checkbox(:name, "checkme","1").set     #选中
    ie.checkbox(:name, "checkme","1").clear  #清空
    3、控制radio;
    源代码如:<input type = "radio" name = "clickme" id = "1">,可以用两种方法进行控制:
    使用name属性:
    ie.radio(:name, "clickme").set     #选中
    ie.radio(:name, "clickme").clear  #清空
    使用name属性和value属性:
    ie.radio(:name, "clickme","1").set     #选中
    ie.radio(:name, "clickme","1").clear  #清空
    4、控制选择框;
    源代码如:"<select name = "selectme" > <option name=1> <option name=2>Web Testing <option name=3>in Ruby <option name=4>is fun </select>"
    选择其中的一个属性:
    ie.select_list( :name , "selectme").select("is fun")
    清空属性:
    ie.select_list( :name, "selectme").clearSelection
    在web页的文本框中输入文本,可以通过name属性和id属性来进行识别和操作,如源代码:
    <input type = "text" name = "typeinme" >
    填充数据:
    ie.text_field(:name,"typeinme").set("Watir World")
    清空数据:
    ie.text_field(:name, "typeinme").clear

    5、控制按钮;
    提交数据,在web应用程序中,一般通过点击buttons按钮,图片按钮,或点击Enter/Return来提交数据,如:
    源代码为:<input type = "button" name = "clickme" value = "Click Me">
    的方法可以使用name属性和value属性:
    ie.button(:name, "clickme").click    #利用name属性
    ie.button(:value, "Click Me).click    #利用value属性
    Forms表单中的按钮,也可以通过name或者value属性来表示,源代码为:<form. action = "submit" name = "submitform" method="post"><input type = "submit" value = "Submit"></input></form>
    ie.button(:value, "Submit").click
    Forms表单中的图片按钮,可以通过name属性来表示,

    如源代码为:<form. action = "submit" name = "doitform" method="post"><input type="image" src = "images/doit.gif" name = "doit"></form>
    ie.button(:name, "doit").click
    有些Forms表单可能没有按钮进行提交,这时可以通过提交forms表单本身的name,action和method来实现,<form. action = "login" name = "loginform" method="get"><input name="username" type="text"></input></form>
    ie.form(:name, "loginform").submit
    ie.form(:action, "login").submit

  • 软件bug的状态以及管理流程

    2010-11-30 10:00:32

     软件错误的主要状态包括以下内容:

    •  New:测试中新报告的软件Bug。
    • Open:被确认并分配给相关开发人员处理。
    • Fixed:开发人员已经修正,等待测试人员验证。
    • Declined:拒绝修改Bug.
    • Deferred:不在本版本修复的错误,下一版本修复。
    • Closed:Bug已被修复。

    Bug管理流程可以概括为以下几项内容.

    • 测试人员提交新的错误入库,错误状态为‘New’.
    • 高级测试人员验证错误。
    1.    确认是错误,分配给相应的开发人员,设置状态为‘Open'
    2.    不是错误,则拒绝,设置状态为‘Declined'
    • 开发人员查询状态为‘Open’的错误,做如下处理
    1. 如果不是错误,拒绝,设置为‘Declined'
    2. 如果是错误,则修复并设置状态为’Fixed'
    3. 如果是不能解决的错误,要留下文字并说明且保持错误为‘Open’状态
    4. 对于不能解决和延期解决的错误,不能由开发人员自己决定,一般要通过某种会议(评审会)通过才能确认。
    • 测试人员查询状态为‘Fixed'的错误验证错误是否已解决,做如下处理
    1. 如果已经解决,设置状态为‘Closed'
    2. 如果没有解决,设置状态为'Reopen'

      

     

  • 性能工程的生命周期

    2010-10-26 15:40:36

    1.Concept and design

      Mainly activities:Review and planning

    2.Development and Test

      Mainly activities:Measurement,Analysis and solution

    3.Supporting Team

      Mainly activity:Benchmark

    4.Deployment

     Mainly activity:Tuning Guilde

    5.Customer

      Mainly activity:Troubleshooting services

     

  • netperf 与网络性能测量

    2010-10-26 15:02:10

    在构建或管理一个网络系统时,我们更多的是关心网络的可用性,即网络是否连通,而对于其整体的性能往往考虑不多,或者即使考虑到性能的问题,但是却发现没有合适的手段去测试网络的性能。

    当开发出一个网络应用程序后,我们会发现,在实际的网络环境使用中,网络应用程序的使用效果不是很理想,问题可能出现在程序的开发上面,也有可能由于实际的网络环境中存在着瓶颈。面对这种问题,程序员一般会一筹莫展,原因就在于不掌握一些网络性能测量的工具。

    在本文中,首先介绍网络性能测量的一些基本概念和方法,然后结合 netperf 工具的使用,具体的讨论如何测试不同情况下的网络性能。

    网络性能测试概述

    网络性能测量的五项指标

    测量网络性能的五项指标是:

    • 可用性(availability)
    • 响应时间(response time)
    • 网络利用率(network utilization)
    • 网络吞吐量(network throughput)
    • 网络带宽容量(network bandwidth capacity)

    1. 可用性

    测试网络性能的第一步是确定网络是否正常工作,最简单的方法是使用 ping 命令。通过向远端的机器发送 icmp echo request,并等待接收 icmp echo reply 来判断远端的机器是否连通,网络是否正常工作。

    Ping 命令有非常丰富的命令选项,比如 -c 可以指定发送 echo request 的个数,-s 可以指定每次发送的 ping 包大小。

    网络设备内部一般有多个缓冲池,不同的缓冲池使用不同的缓冲区大小,分别用来处理不同大小的分组(packet)。例如交换机中通常具有三种类型的包缓冲:一类针对小的分组,一类针对中等大小的分组,还有一类针对大的分组。为了测试这样的网络设备,测试工具必须要具有发送不同大小分组的能力。Ping 命令的 -s 就可以使用在这种场合。

    2. 响应时间

    Ping 命令的 echo request/reply 一次往返所花费时间就是响应时间。有很多因素会影响到响应时间,如网段的负荷,网络主机的负荷,广播风暴,工作不正常的网络设备等等。

    在网络工作正常时,记录下正常的响应时间。当用户抱怨网络的反应时间慢时,就可以将现在的响应时间与正常的响应时间对比,如果两者差值的波动很大,就能说明网络设备存在故障。

    3. 网络利用率

    网络利用率是指网络被使用的时间占总时间(即被使用的时间+空闲的时间)的比例。比如,Ethernet 虽然是共享的,但同时却只能有一个报文在传输。因此在任一时刻,Ethernet 或者是 100% 的利用率,或者是 0% 的利用率。

    计算一个网段的网络利用率相对比较容易,但是确定一个网络的利用率就比较复杂。因此,网络测试工具一般使用网络吞吐量和网络带宽容量来确定网络中两个节点之间的性能。

    4. 网络吞吐量

    网络吞吐量是指在某个时刻,在网络中的两个节点之间,提供给网络应用的剩余带宽。

    网络吞吐量可以帮组寻找网络路径中的瓶颈。比如,即使 client 和 server 都被分别连接到各自的 100M Ethernet 上,但是如果这两个 100M 的Ethernet 被 10M 的 Ethernet 连接起来,那么 10M 的 Ethernet 就是网络的瓶颈。

    网络吞吐量非常依赖于当前的网络负载情况。因此,为了得到正确的网络吞吐量,最好在不同时间(一天中的不同时刻,或者一周中不同的天)分别进行测试,只有这样才能得到对网络吞吐量的全面认识。

    有些网络应用程序在开发过程的测试中能够正常运行,但是到实际的网络环境中却无法正常工作(由于没有足够的网络吞吐量)。这是因为测试只是在空闲的网络环境中,没有考虑到实际的网络环境中还存在着其它的各种网络流量。所以,网络吞吐量定义为剩余带宽是有实际意义的。

    5. 网络带宽容量

    与网络吞吐量不同,网络带宽容量指的是在网络的两个节点之间的最大可用带宽。这是由组成网络的设备的能力所决定的。

    测试网络带宽容量有两个困难之处:在网络存在其它网络流量的时候,如何得知网络的最大可用带宽;在测试过程中,如何对现有的网络流量不造成影响。网络测试工具一般采用 packet pairs 和 packet trains 技术来克服这样的困难。

    收集网络性能数据的方式

    当确定了网络性能的测试指标以后,就需要使用网络测试工具收集相应的性能数据,分别有三种从网络获取数据的方式:

    1. 通过snmp协议直接到网络设备中获取,如net-snmp工具

    2. 侦听相关的网络性能数据,典型的工具是tcpdump

    3. 自行产生相应的测试数据,如本文中使用的netperf工具

    Netperf

    Netperf是一种网络性能的测量工具,主要针对基于TCP或UDP的传输。Netperf根据应用的不同,可以进行不同模式的网络性能测试,即批量数据传输(bulk data transfer)模式和请求/应答(request/reponse)模式。Netperf测试结果所反映的是一个系统能够以多快的速度向另外一个系统发送数据,以及另外一个系统能够以多块的速度接收数据。

    Netperf工具以client/server方式工作。server端是netserver,用来侦听来自client端的连接,client端是netperf,用来向server发起网络测试。在client与server之间,首先建立一个控制连接,传递有关测试配置的信息,以及测试的结果;在控制连接建立并传递了测试配置信息以后,client与server之间会再建立一个测试连接,用来来回传递着特殊的流量模式,以测试网络的性能。

    TCP网络性能

    由于TCP协议能够提供端到端的可靠传输,因此被大量的网络应用程序使用。但是,可靠性的建立是要付出代价的。TCP协议保证可靠性的措施,如建立并维护连接、控制数据有序的传递等都会消耗一定的网络带宽。

    Netperf可以模拟三种不同的TCP流量模式:

    1) 单个TCP连接,批量(bulk)传输大量数据

    2) 单个TCP连接,client请求/server应答的交易(transaction)方式

    3) 多个TCP连接,每个连接中一对请求/应答的交易方式

    UDP网络性能

    UDP没有建立连接的负担,但是UDP不能保证传输的可靠性,所以使用UDP的应用程序需要自行跟踪每个发出的分组,并重发丢失的分组。

    Netperf可以模拟两种UDP的流量模式:

    1) 从client到server的单向批量传输

    2) 请求/应答的交易方式

    由于UDP传输的不可靠性,在使用netperf时要确保发送的缓冲区大小不大于接收缓冲区大小,否则数据会丢失,netperf将给出错误的结果。因此,对于接收到分组的统计不一定准确,需要结合发送分组的统计综合得出结论。

    Netperf的命令行参数

    在unix系统中,可以直接运行可执行程序来启动netserver,也可以让inetd或xinetd来自动启动netserver。

    当netserver在server端启动以后,就可以在client端运行netperf来测试网络的性能。netperf通过命令行参数来控制测试的类型和具体的测试选项。根据作用范围的不同,netperf的命令行参数可以分为两大类:全局命令行参数、测试相关的局部参数,两者之间使用--分隔:

    netperf [global options]-- [test-specific options]
    

    这里我们只解释那些常用的命令行参数,其它的参数读者可以查询netperf的man手册。

    -H host :指定远端运行netserver的server IP地址。

    -l testlen:指定测试的时间长度(秒)

    -t testname:指定进行的测试类型,包括TCP_STREAM,UDP_STREAM,TCP_RR,TCP_CRR,UDP_RR,在下文中分别对它们说明。

    在后面的测试中,netserver运行在192.168.0.28,server与client通过局域网连接(100M Hub)。

    Netperf测试网络性能

    测试批量(bulk)网络流量的性能

    批量数据传输典型的例子有ftp和其它类似的网络应用(即一次传输整个文件)。根据使用传输协议的不同,批量数据传输又分为TCP批量传输和UDP批量传输。

    1. TCP_STREAM

    Netperf缺省情况下进行TCP批量传输,即-t TCP_STREAM。测试过程中,netperf向netserver发送批量的TCP数据分组,以确定数据传输过程中的吞吐量:

     ./netperf -H 192.168.0.28 -l 60
    TCP STREAM TEST to 192.168.0.28
    Recv   Send    Send
    Socket Socket  Message  Elapsed
    Size   Size    Size     Time     Throughput
    bytes  bytes   bytes    secs.    10^6bits/sec
     
     87380  16384  16384    60.00      88.00
     

    从netperf的结果输出中,我们可以知道以下的一些信息:

    1) 远端系统(即server)使用大小为87380字节的socket接收缓冲

    2) 本地系统(即client)使用大小为16384字节的socket发送缓冲

    3) 向远端系统发送的测试分组大小为16384字节

    4) 测试经历的时间为60秒

    5) 吞吐量的测试结果为88Mbits/秒

    在缺省情况下,netperf向发送的测试分组大小设置为本地系统所使用的socket发送缓冲大小。

    TCP_STREAM方式下与测试相关的局部参数如下表所示:

    参数 说明
    -s size 设置本地系统的socket发送与接收缓冲大小
    -S size 设置远端系统的socket发送与接收缓冲大小
    -m size 设置本地系统发送测试分组的大小
    -M size 设置远端系统接收测试分组的大小
    -D 对本地与远端系统的socket设置TCP_NODELAY选项

    通过修改以上的参数,并观察结果的变化,我们可以确定是什么因素影响了连接的吞吐量。例如,如果怀疑路由器由于缺乏足够的缓冲区空间,使得转发大的分组时存在问题,就可以增加测试分组(-m)的大小,以观察吞吐量的变化:

     ./netperf -H 192.168.0.28 -l 60 -- -m 2048
    TCP STREAM TEST to 192.168.0.28
    Recv   Send    Send
    Socket Socket  Message  Elapsed
    Size   Size    Size     Time     Throughput
    bytes  bytes   bytes    secs.    10^6bits/sec
     
     87380  16384   2048    60.00      87.62
     

    在这里,测试分组的大小减少到2048字节,而吞吐量却没有很大的变化(与前面例子中测试分组大小为16K字节相比)。相反,如果吞吐量有了较大的提升,则说明在网络中间的路由器确实存在缓冲区的问题。

    2. UDP_STREAM

    UDP_STREAM用来测试进行UDP批量传输时的网络性能。需要特别注意的是,此时测试分组的大小不得大于socket的发送与接收缓冲大小,否则netperf会报出错提示:

    ./netperf -t UDP_STREAM -H 192.168.0.28 -l 60
    UDP UNIDIRECTIONAL SEND TEST to 192.168.0.28
    udp_send: data send error: Message too long
    

    为了避免这样的情况,可以通过命令行参数限定测试分组的大小,或者增加socket的发送/接收缓冲大小。UDP_STREAM方式使用与TCP_STREAM方式相同的局部命令行参数,因此,这里可以使用-m来修改测试中使用分组的大小:

     ./netperf -t UDP_STREAM -H 192.168.0.28 -- -m 1024
    UDP UNIDIRECTIONAL SEND TEST to 192.168.0.28
    Socket  Message  Elapsed      Messages
    Size    Size     Time         Okay Errors   Throughput
    bytes   bytes    secs            #      #   10^6bits/sec
     
     65535    1024   9.99       114127      0      93.55
     65535           9.99       114122             93.54
     

    UDP_STREAM方式的结果中有两行测试数据,第一行显示的是本地系统的发送统计,这里的吞吐量表示netperf向本地socket发送分组的能力。但是,我们知道,UDP是不可靠的传输协议,发送出去的分组数量不一定等于接收到的分组数量。

    第二行显示的就是远端系统接收的情况,由于client与server直接连接在一起,而且网络中没有其它的流量,所以本地系统发送过去的分组几乎都被远端系统正确的接收了,远端系统的吞吐量也几乎等于本地系统的发送吞吐量。但是,在实际环境中,一般远端系统的socket缓冲大小不同于本地系统的socket缓冲区大小,而且由于UDP协议的不可靠性,远端系统的接收吞吐量要远远小于发送出去的吞吐量。

    测试请求/应答(request/response)网络流量的性能

    另一类常见的网络流量类型是应用在client/server结构中的request/response模式。在每次交易(transaction)中,client向server发出小的查询分组,server接收到请求,经处理后返回大的结果数据。如下图所示:



    1. TCP_RR

    TCP_RR方式的测试对象是多次TCP request和response的交易过程,但是它们发生在同一个TCP连接中,这种模式常常出现在数据库应用中。数据库的client程序与server程序建立一个TCP连接以后,就在这个连接中传送数据库的多次交易过程。

    ./netperf -t TCP_RR -H 192.168.0.28
    TCP REQUEST/RESPONSE TEST to 192.168.0.28
    Local /Remote
    Socket Size   Request  Resp.   Elapsed  Trans.
    Send   Recv   Size     Size    Time     Rate
    bytes  Bytes  bytes    bytes   secs.    per sec
     
    16384  87380  1        1       10.00    9502.73
    16384  87380
    

    Netperf输出的结果也是由两行组成。第一行显示本地系统的情况,第二行显示的是远端系统的信息。平均的交易率(transaction rate)为9502.73次/秒。注意到这里每次交易中的request和response分组的大小都为1个字节,不具有很大的实际意义。用户可以通过测试相关的参数来改变request和response分组的大小,TCP_RR方式下的参数如下表所示:

    参数 说明
    -r req,resp 设置request和reponse分组的大小
    -s size 设置本地系统的socket发送与接收缓冲大小
    -S size 设置远端系统的socket发送与接收缓冲大小
    -D 对本地与远端系统的socket设置TCP_NODELAY选项

    通过使用-r参数,我们可以进行更有实际意义的测试:

    ./netperf -t TCP_RR -H 192.168.0.28 -- -r 32,1024
    TCP REQUEST/RESPONSE TEST to 192.168.0.28
    Local /Remote
    Socket Size   Request  Resp.   Elapsed  Trans.
    Send   Recv   Size     Size    Time     Rate
    bytes  Bytes  bytes    bytes   secs.    per sec
     
    16384  87380  32       1024    10.00    4945.97
    16384  87380
    

    从结果中可以看出,由于request/reponse分组的大小增加了,导致了交易率明显的下降。注:相对于实际的系统,这里交易率的计算没有充分考虑到交易过程中的应用程序处理时延,因此结果往往会高于实际情况。

    2. TCP_CRR

    与TCP_RR不同,TCP_CRR为每次交易建立一个新的TCP连接。最典型的应用就是HTTP,每次HTTP交易是在一条单独的TCP连接中进行的。因此,由于需要不停地建立新的TCP连接,并且在交易结束后拆除TCP连接,交易率一定会受到很大的影响。

    ./netperf -t TCP_CRR -H 192.168.0.28 
    TCP Connect/Request/Response TEST to 192.168.0.28
    Local /Remote
    Socket Size   Request  Resp.   Elapsed  Trans.
    Send   Recv   Size     Size    Time     Rate
    bytes  Bytes  bytes    bytes   secs.    per sec
     
    131070 131070 1        1       9.99     2662.20
    16384  87380
    

    即使是使用一个字节的request/response分组,交易率也明显的降低了,只有2662.20次/秒。TCP_CRR使用与TCP_RR相同的局部参数。

    3. UDP_RR

    UDP_RR方式使用UDP分组进行request/response的交易过程。由于没有TCP连接所带来的负担,所以我们推测交易率一定会有相应的提升。

    ./netperf -t UDP_RR -H 192.168.0.28 
    UDP REQUEST/RESPONSE TEST to 192.168.0.28
    Local /Remote
    Socket Size   Request  Resp.   Elapsed  Trans.
    Send   Recv   Size     Size    Time     Rate
    bytes  Bytes  bytes    bytes   secs.    per sec
     
    65535  65535  1        1       9.99     10141.16
    65535  65535
    

    结果证实了我们的推测,交易率为10141.16次/秒,高过TCP_RR的数值。不过,如果出现了相反的结果,即交易率反而降低了,也不需要担心,因为这说明了在网络中,路由器或其它的网络设备对UDP采用了与TCP不同的缓冲区空间和处理技术。

    结束语

    除了netperf以外,还有很多其它的网络性能测试工具,如dbs, iperf, pathrate, nettest, netlogger, tcptrace, ntop等。这些工具有其各自的特色和不同的侧重点,我们可以根据具体的应用环境,有选择的使用它们,这样就可以使这些工具发挥出最大的功效。虽然都是开放源代码的软件,但是这些工具的功能与商业的网络测试工具同样强大,而且也得到了广泛的应用,熟悉这些工具对我们的实际工作一定会有很大的帮助。

  • Eclipse 插件RDT(Ruby Development Tools)下载安装

    2010-08-27 10:22:53

      The RDT(ruby development tools) download address can not find very easily,I give you it to everyone,the address is http://sourceforge.net/projects/rubyeclipse/

      RDT 是一个标准的 Eclipse 插件,选择下载org.rubypeople.rdt-0.6.0.zip到本地,将 zip 文件直接解压缩到 Eclipse 文件夹。存档文件中的路径会建立目录结构。

      Ruby 是一种解释语言,所以必须将一种解释器与环境相关联,然后 RDT 才能运行或调试应用程序。这种关联是在 Windows > Preferences 对话框中 Ruby 标题下面的 Installed Interpreters 项中设置的。将“Location”文本域指向您使用的 Ruby 版本的 bin 目录。RDT 会找到所需的其他信息。关联了解释器之后,就可以运行应用程序了。如果这样不可以的话,就选择bin下面的ruby.exe作为解释器变可以进行操作。

  • Ruby+watir安装

    2010-08-26 17:57:09

    在线安装不好用,我是本地安装的,需要把ruby和watir安装软件download到本地

    1.从官网http://rubyforge.org/frs/?group_id=167 上下载最新的ruby安装软件rubyinstaller-1.9.1-p378.exe,并且安装

    2.下载watir-1.5.5.gem到本地并保存到ruby的安装目录下;

    3.开始-运行-转到watir-1.5.5.gem的保存目录下,输入:“gem install watir-1.5.5.gem”

    安装完成。

    上面安装的是比较新的ruby,没有带SCiTE等工具,我们可以安装比较旧一点的工具,步骤如下:

    1.下载并安装Ruby.

      在Ruby的官方网站http://rubyforge.org/frs/?group_id=167 上下载ruby186-26.exe,并进行安装.

    2.下载gem并update

      网速好的情况下可用命令:gem update system。否则,采用本地安装,打开http://rubyforge.org/frs/?group_id=126,下载rubygems-1.3.4.zip,j解压,双击setup.rb安装。最好下载zip包在本地安装,因为我当时下载的是gem然后用命令行安装的,安装成功之后,再安装watir的时候提示win32-proccess版本不正确。

    3.下载watir并进行安装.

      网速好的情况下可用命令:gem install watir。否则,打开http://rubyforge.org/frs/?group_id=104,下载watir-1.5.6.gem,命令行进入下载文件所在目录,运行gem install watir-1.5.6.gem。

    4.检测:打开SciTE,输入

    require 'watir'
    ie = Watir::IE.new
    ie.goto("
    http://www.baidu.cn")

    保存为a.rd文件,F5执行,输出

    >ruby a.rb
    >Exit code: 0

    并且百度主页打开

    到此安装成功。

  • js刷新页面代码

    2010-07-23 16:14:00



    先来看一个简单的例子:
    下面以三个页面分别命名为frame.html、top.html、bottom.html为例来具体说明如何做。

    frame.html 由上(top.html)下(bottom.html)两个页面组成,代码如下:
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    <HTML>
    <HEAD>
    <TITLE> frame. </TITLE>
    </HEAD>
    <frameset rows="50%,50%">
    <frame. name=top  src="top.html">
    <frame. name=bottom  src="bottom.html">
    </frameset>
    </HTML>

    现在假设top.html (即上面的页面) 有七个button来实现对bottom.html (即下面的页面) 的刷新,可以用以下七种语句,哪个好用自己看着办了。

    语句1. window.parent.frames[1].location.reload();
    语句2. window.parent.frames.bottom.location.reload();
    语句3. window.parent.frames["bottom"].location.reload();
    语句4. window.parent.frames.item(1).location.reload();
    语句5. window.parent.frames.item('bottom').location.reload();
    语句6. window.parent.bottom.location.reload();
    语句7. window.parent['bottom'].location.reload();

    top.html 页面的代码如下:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    <HTML>
    <HEAD>
      
    <TITLE> top.html </TITLE>
    </HEAD>
    <BODY>
    <input type=button value="刷新1" onclick="window.parent.frames[1].location.reload()"><br>
    <input type=button value="刷新2" onclick="window.parent.frames.bottom.location.reload()"><br>
    <input type=button value="刷新3" onclick="window.parent.frames['bottom'].location.reload()"><br>
    <input type=button value="刷新4" onclick="window.parent.frames.item(1).location.reload()"><br>
    <input type=button value="刷新5" onclick="window.parent.frames.item('bottom').location.reload()"><br>
    <input type=button value="刷新6" onclick="window.parent.bottom.location.reload()"><br>
    <input type=button value="刷新7" onclick="window.parent['bottom'].location.reload()"><br>
    </BODY>
    </HTML>

    下面是bottom.html页面源代码,为了证明下方页面的确被刷新了,在装载完页面弹出一个对话框。

    bottom.html 页面的代码如下:
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    <HTML>
    <HEAD>
      
    <TITLE> bottom.html </TITLE>
    </HEAD>
    <BODY onload="alert('我被加载了!')">
    <h1>This is the content in bottom.html.</h1>
    </BODY>
    </HTML>


    解释一下:
    1.window指代的是当前页面,例如对于此例它指的是top.html页面。
    2.parent指的是当前页面的父页面,也就是包含它的框架页面。例如对于此例它指的是framedemo.html。
    3.frames是window对象,是一个数组。代表着该框架内所有子页面。
    4.item是方法。返回数组里面的元素。
    5.如果子页面也是个框架页面,里面还是其它的子页面,那么上面的有些方法可能不行。

    附:
    Javascript刷新页面的几种方法:
    1    history.go(0)
    2    location.reload()
    3    location=location
    4    location.assign(location)
    5    document.execCommand('Refresh')
    6    window.navigate(location)
    7    location.replace(location)
    8    document.URL=location.href

    自动刷新页面的方法:
    1.页面自动刷新:把如下代码加入<head>区域中
    <meta. http-equiv="refresh" content="20">
    其中20指每隔20秒刷新一次页面.

    2.页面自动跳转:把如下代码加入<head>区域中
    <meta. http-equiv="refresh" content="20;url=http://www.wyxg.com">
    其中20指隔20秒后跳转到http://www.wyxg.com页面

    3.页面自动刷新js版
    <script. language="JavaScript">
    function myrefresh()
    {
           window.location.reload();
    }
    setTimeout('myrefresh()',1000); //指定1秒刷新一次
    </script>

    ASP.NET如何输出刷新父窗口脚本语句
    1.   this.response.write("<script>opener.location.reload();</script>");  

    2.   this.response.write("<script>opener.window.location.href = opener.window.location.href;</script>");   

    3.   Response.Write("<script. language=javascript>opener.window.navigate(''你要刷新的页.asp'');</script>")


    JS刷新框架的脚本语句

    //如何刷新包含该框架的页面用   
    <script. language=JavaScript>
       parent.location.reload();
    </script>   

    //子窗口刷新父窗口
    <script. language=JavaScript>
        self.opener.location.reload();
    </script>

    ( 或 <a href="javascript.:opener.location.reload()">刷新</a>   )

    //如何刷新另一个框架的页面用   
    <script. language=JavaScript>
       parent.另一FrameID.location.reload();
    </script>

    如果想关闭窗口时刷新或者想开窗时刷新的话,在<body>中调用以下语句即可。

    <body nload="opener.location.reload()">
    开窗时刷新
    <body nUnload="opener.location.reload()">
    关闭时刷新

    <script. language="javascript">
    window.opener.document.location.reload()
    </script>

1676/9<123456789>
Open Toolbar