发布新日志

  • 用LoadRunner下载文件并保存到本地

    2008-09-11 14:59:09

    转载自关河BLOG

    论坛上经常有人问起,如何让LoadRunner可以下载文件并保存在本地。

    初看起来,这个问题应该不是问题:LoadRunner录制的是Client(浏览器)和服务器之间的交互,自然也就能记录到下载文件的动作和收到下载文件的全部内容,但如果真用LoadRunner去尝试这样一个工作,却会发现在LoadRunner录制的脚本中根本没有任何与下载文件相关的语句。

    其实,在HTTP协议中,本来就没有任何一个方法或是动作能够标识“下载文件”这个动作,对HTTP协议来说,下载文件或是请求页面,都只是一个GET方法,至于说我们下载文件时看到的那个提示我们给出用户名的对话框,那完全是客户端自身判断到我们请求的是一个文件而自行处理的。

    那么,究竟该如何才能让LoadRunner完成这个工作呢?

    通过上面的分析,我们已经知道了这样一些事实:

    1、文件请求是通过GET方法请求的;

    2、LoadRunner已经记录了客户端发出的对文件的请求,并能够收到文件内容;

    因此,我们完全可以通过关联的方法,从LoadRunner发出的请求的响应中获取到文件的内容,然后通过LoadRunner的文件操作方法,自行生成文件。

    从LoadRunner录制时的记录(Recording Log)中,我们可以看到具体的请求和请求的响应信息:

    从这里可以看到,我们只需要对以下语句进行关联,就能获得文件的内容。

    web_url("viewfile.asp",
            
    "URL=http://www.testage.net/bbs/viewFile.asp?BoardID=25&ID=217",
            
    "Resource=0",
            
    "RecContentType=text/html",
            
    "Referer=http://www.testage.net/bbs/dispbbs.asp?boardID=25&ID=5187&page=1",
            
    "Snapshot=t16.inf",
            
    "Mode=HTML",
            EXTRARES,
            
    "Url=viewFile.asp?BoardID=25&ID=217""Referer=http://www.testage.net/bbs/dispbbs.asp?boardID=25&ID=5187&page=1", ENDITEM,
            LAST);

    获得文件内容后,通过LoadRunner的fopen,fwrite,fclose函数,就可以将这部分内容保存成本地文件了。

        //获取响应中的文件长度
        flen = web_get_int_property(HTTP_INFO_DOWNLOAD_SIZE);

        
    if(flen > 0)    
        {
            
    //以写方式打开文件
            if((filedes = fopen("c:\\test.rar""wb")) == NULL)
            {
                lr_output_message(
    "Open File Failed!");
                
    return -1;
            }
            
    //写入文件内容
            fwrite(lr_eval_string("{fcontent}"), flen, 1, filedes);
            
    //关闭文件
            fclose(filedes);
        }

    点击下载完整的脚本文件

    注意,使用该脚本文件时,请自行修改参数username和passwd的值。

    当然,本脚本只是简单实现了文件下载并保存本地的功能,如果我们希望每次下载保存到本地的文件采用不同的名称,或是希望根据实际文件的后缀名动态生成后缀名,则还需要额外的工作。读者可以自行思考如何实现。^_^

  • sprintf详解

    2008-09-11 13:44:27

    转摘声明:选自《CSDN 社区电子杂志——C/C++杂志》

    在将各种类型的数据构造成字符串时,sprintf 的强大功能很少会让你失望。由于sprintf 跟printf 在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出。这也导致sprintf 比printf 有用得多。

    sprintf 是个变参函数,定义如下:
    int sprintf( char *buffer, const char *format [, argument] ... );
    除了前两个参数类型固定外,后面可以接任意多个参数。而它的精华,显然就在第二个参数:
    格式化字符串上。


    printf 和sprintf 都使用格式化字符串来指定串的格式,在格式串内部使用一些以“%”开头的格式说明符(format specifications)来占据一个位置,在后边的变参列表中提供相应的变量,最终函数就会用相应位置的变量来替代那个说明符,产生一个调用者想要的字符串。

    格式化数字字符串
    sprintf 最常见的应用之一莫过于把整数打印到字符串中,所以,spritnf 在大多数场合可以替代
    itoa。

    如:
    //把整数123 打印成一个字符串保存在s 中。
    sprintf(s, "%d", 123); //产生"123"
    可以指定宽度,不足的左边补空格:
    sprintf(s, "%8d%8d", 123, 4567); //产生:" 123 4567"
    当然也可以左对齐:
    sprintf(s, "%-8d%8d", 123, 4567); //产生:"123 4567"
    也可以按照16 进制打印:
    sprintf(s, "%8x", 4567); //小写16 进制,宽度占8 个位置,右对齐
    sprintf(s, "%-8X", 4568); //大写16 进制,宽度占8 个位置,左对齐

    这样,一个整数的16 进制字符串就很容易得到,但我们在打印16 进制内容时,通常想要一种左边补0 的等宽格式,那该怎么做呢?很简单,在表示宽度的数字前面加个0 就可以了。
    sprintf(s, "%08X", 4567); //产生:"000011D7"
    上面以”%d”进行的10 进制打印同样也可以使用这种左边补0 的方式。


    这里要注意一个符号扩展的问题:比如,假如我们想打印短整数(short)-1 的内存16 进制表示形式,在Win32 平台上,一个short 型占2 个字节,所以我们自然希望用4 个16 进制数字来打印它:
    short si = -1;
    sprintf(s, "%04X", si);
    产生“FFFFFFFF”,怎么回事?因为spritnf 是个变参函数,除了前面两个参数之外,后面的参数都不是类型安全的,函数更没有办法仅仅通过一个“%X”就能得知当初函数调用前参数压栈时被压进来的到底是个4 字节的整数还是个2 字节的短整数,所以采取了统一4 字节的处理方式,导致参数压栈时做了符号扩展,扩展成了32 位的整数-1,打印时4 个位置不够了,就把32 位整数-1 的8 位16 进制都打印出来了。

    如果你想看si 的本来面目,那么就应该让编译器做0 扩展而不是符号扩展(扩展时二进制左边补0 而不是补符号位):
    sprintf(s, "%04X", (unsigned short)si);
    就可以了。或者:
    unsigned short si = -1;
    sprintf(s, "%04X", si);


    sprintf 和printf 还可以按8 进制打印整数字符串,使用”%o”。注意8 进制和16 进制都不会打
    印出负数,都是无符号的,实际上也就是变量的内部编码的直接的16 进制或8 进制表示。

    控制浮点数打印格式
    浮点数的打印和格式控制是sprintf 的又一大常用功能,浮点数使用格式符”%f”控制,默认保
    留小数点后6 位数字,比如:
    sprintf(s, "%f", 3.1415926); //产生"3.141593"
    但有时我们希望自己控制打印的宽度和小数位数,这时就应该使用:”%m.nf”格式,其中m 表
    示打印的宽度,n 表示小数点后的位数。比如:
    sprintf(s, "%10.3f", 3.1415626); //产生:" 3.142"
    sprintf(s, "%-10.3f", 3.1415626); //产生:"3.142 "
    sprintf(s, "%.3f", 3.1415626); //不指定总宽度,产生:"3.142"

    注意一个问题,你猜
    int i = 100;
    sprintf(s, "%.2f", i);
    会打出什么东东来?“100.00”?对吗?自己试试就知道了,同时也试试下面这个:
    sprintf(s, "%.2f", (double)i);
    第一个打出来的肯定不是正确结果,原因跟前面提到的一样,参数压栈时调用者并不知道跟i相对应的格式控制符是个”%f”。而函数执行时函数本身则并不知道当年被压入栈里的是个整数,于是可怜的保存整数i 的那4 个字节就被不由分说地强行作为浮点数格式来解释了,整个乱套了。不过,如果有人有兴趣使用手工编码一个浮点数,那么倒可以使用这种方法来检验一下你手工编排的结果是否正确。

    字符/Ascii 码对照
    我们知道,在C/C++语言中,char 也是一种普通的scalable 类型,除了字长之外,它与short,
    int,long 这些类型没有本质区别,只不过被大家习惯用来表示字符和字符串而已。(或许当年该把
    这个类型叫做“byte”,然后现在就可以根据实际情况,使用byte 或short 来把char 通过typedef 定义出来,这样更合适些)于是,使用”%d”或者”%x”打印一个字符,便能得出它的10 进制或16 进制的ASCII 码;反过来,使用”%c”打印一个整数,便可以看到它所对应的ASCII 字符。以下程序段把所有可见字符的ASCII 码对照表打印到屏幕上(这里采用printf,注意”#”与”%X”合用时自动为16 进制数增加”0X”前缀):
    for(int i = 32; i < 127; i++) {
    printf("[ %c ]: %3d 0x%#04X\n", i, i, i);
    }


    连接字符串
    sprintf 的格式控制串中既然可以插入各种东西,并最终把它们“连成一串”,自然也就能够连
    接字符串,从而在许多场合可以替代strcat,但sprintf 能够一次连接多个字符串(自然也可以同时
    在它们中间插入别的内容,总之非常灵活)。比如:
    char* who = "I";
    char* whom = "CSDN";
    sprintf(s, "%s love %s.", who, whom); //产生:"I love CSDN. "
    strcat 只能连接字符串(一段以’’结尾的字符数组或叫做字符缓冲,null-terminated-string),但有时我们有两段字符缓冲区,他们并不是以 ’’结尾。比如许多从第三方库函数中返回的字符数组,从硬件或者网络传输中读进来的字符流,它们未必每一段字符序列后面都有个相应的’’来结尾。如果直接连接,不管是sprintf 还是strcat 肯定会导致非法内存操作,而strncat 也至少要求第一个参数是个null-terminated-string,那该怎么办呢?我们自然会想起前面介绍打印整数和浮点数时可以指定宽度,字符串也一样的。比如:
    char a1[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
    char a2[] = {'H', 'I', 'J', 'K', 'L', 'M', 'N'};
    如果:
    sprintf(s, "%s%s", a1, a2); //Don't do that!
    十有八九要出问题了。是否可以改成:
    sprintf(s, "%7s%7s", a1, a2);
    也没好到哪儿去,正确的应该是:
    sprintf(s, "%.7s%.7s", a1, a2);//产生:"ABCDEFGHIJKLMN"
    这可以类比打印浮点数的”%m.nf”,在”%m.ns”中,m 表示占用宽度(字符串长度不足时补空格,超出了则按照实际宽度打印),n 才表示从相应的字符串中最多取用的字符数。通常在打印字符串时m 没什么大用,还是点号后面的n 用的多。自然,也可以前后都只取部分字符:
    sprintf(s, "%.6s%.5s", a1, a2);//产生:"ABCDEFHIJKL"
    在许多时候,我们或许还希望这些格式控制符中用以指定长度信息的数字是动态的,而不是静态指定的,因为许多时候,程序要到运行时才会清楚到底需要取字符数组中的几个字符,这种动态的宽度/精度设置功能在sprintf 的实现中也被考虑到了,sprintf 采用”*”来占用一个本来需要一个指定宽度或精度的常数数字的位置,同样,而实际的宽度或精度就可以和其它被打印的变量一样被提供出来,于是,上面的例子可以变成:
    sprintf(s, "%.*s%.*s", 7, a1, 7, a2);
    或者:
    sprintf(s, "%.*s%.*s", sizeof(a1), a1, sizeof(a2), a2);
    实际上,前面介绍的打印字符、整数、浮点数等都可以动态指定那些常量值,比如:
    sprintf(s, "%-*d", 4, 'A'); //产生"65 "
    sprintf(s, "%#0*X", 8, 128); //产生"0X000080","#"产生0X
    sprintf(s, "%*.*f", 10, 2, 3.1415926); //产生" 3.14"


    打印地址信息
    有时调试程序时,我们可能想查看某些变量或者成员的地址,由于地址或者指针也不过是个32 位的数,你完全可以使用打印无符号整数的”%u”把他们打印出来:
    sprintf(s, "%u", &i);
    不过通常人们还是喜欢使用16 进制而不是10 进制来显示一个地址:
    sprintf(s, "%08X", &i);
    然而,这些都是间接的方法,对于地址打印,sprintf 提供了专门的”%p”:
    sprintf(s, "%p", &i);
    我觉得它实际上就相当于:
    sprintf(s, "%0*x", 2 * sizeof(void *), &i);
    利用sprintf 的返回值
    较少有人注意printf/sprintf 函数的返回值,但有时它却是有用的,spritnf 返回了本次函数调用
    最终打印到字符缓冲区中的字符数目。也就是说每当一次sprinf 调用结束以后,你无须再调用一次
    strlen 便已经知道了结果字符串的长度。如:
    int len = sprintf(s, "%d", i);
    对于正整数来说,len 便等于整数i 的10 进制位数。
    下面的是个完整的例子,产生10 个[0, 100)之间的随机数,并将他们打印到一个字符数组s 中,
    以逗号分隔开。
    #include
    #include
    #include
    int main() {
    srand(time(0));
    char s[64];
    int ōffset = 0;
    for(int i = 0; i < 10; i++) {
    offset += sprintf(s + offset, "%d,", rand() % 100);
    }
    s[offset - 1] = '\n';//将最后一个逗号换成换行符。
    printf(s);
    return 0;
    }
    设想当你从数据库中取出一条记录,然后希望把他们的各个字段按照某种规则连接成一个字
    符串时,就可以使用这种方法,从理论上讲,他应该比不断的strcat 效率高,因为strcat 每次调用
    都需要先找到最后的那个’’的位置,而在上面给出的例子中,我们每次都利用sprintf 返回值把这
    个位置直接记下来了。


    使用sprintf 的常见问题
    sprintf 是个变参函数,使用时经常出问题,而且只要出问题通常就是能导致程序崩溃的内存访
    问错误,但好在由sprintf 误用导致的问题虽然严重,却很容易找出,无非就是那么几种情况,通
    常用眼睛再把出错的代码多看几眼就看出来了。


    ?? 缓冲区溢出
    第一个参数的长度太短了,没的说,给个大点的地方吧。当然也可能是后面的参数的问
    题,建议变参对应一定要细心,而打印字符串时,尽量使用”%.ns”的形式指定最大字符数。


    ?? 忘记了第一个参数
    低级得不能再低级问题,用printf 用得太惯了。//偶就常犯。:。(


    ?? 变参对应出问题
    通常是忘记了提供对应某个格式符的变参,导致以后的参数统统错位,检查检查吧。尤
    其是对应”*”的那些参数,都提供了吗?不要把一个整数对应一个”%s”,编译器会觉得你
    欺她太甚了(编译器是obj 和exe 的妈妈,应该是个女的,:P)。

    strftime
    sprnitf 还有个不错的表妹:strftime,专门用于格式化时间字符串的,用法跟她表哥很像,也
    是一大堆格式控制符,只是毕竟小姑娘家心细,她还要调用者指定缓冲区的最大长度,可能是为
    了在出现问题时可以推卸责任吧。这里举个例子:
    time_t t = time(0);
    //产生"YYYY-MM-DD hh:mm:ss"格式的字符串。
    char s[32];
    strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", localtime(&t));
    sprintf 在MFC 中也能找到他的知音:CString::Format,strftime 在MFC 中自然也有她的同道:

  • LoadRunner下载文件的实验

    2008-09-11 13:43:04

    转载的一篇文章(收藏用,谢谢那位仁兄了)

    今天用LoadRunner进行下载web网站上文件的试验,首先学习了关河Blog中的文章(用LoadRunner下载文件并保存到本地 )写了个脚本,运行后却发现该脚本对于二进制文件类型(如rar、bmp等)下载正常,但对于文本文件类型(如txt、xml)等确有问题,问题在于对文件大小的判断问题。

    在关河的脚本中,通过flen = web_get_int_property(HTTP_INFO_DOWNLOAD_SIZE);来获取要下载文件的大小。
    对于二进制文件,运行日志:
    web_link("aa.bmp ") highest severity level was "warning", 824 body bytes, 197 header bytes
    flen:1021
    文件大小1021字节,包括197字节的header和824字节的body

    对于文本文件,运行日志:
    web_link("test.xml") was successful, 277 body bytes, 203 header bytes
    flen:480
    下载生成的文件大小480字节,包括了227字节的body和字节203的header,打开该xml文件内容有错。其实真正的文件大小是227字节,就是body部分。

    修改了脚本,把上面获取文件大小的语句改为flen = strlen(lr_eval_string("{fcontent}")); ,只取body部分作为文件内容,结果就可以了。
    运行日志:
    web_link("test.xml") was successful, 277 body bytes, 203 header bytes
    flen:277

    但是,用该方式却又无法正确下载二进制文件,估计对于二进制文件不能用该方式来获取文件大小,目前没有两全其美的方法,希望有朋友可以指点迷津。

    脚本如下:

    Action()
    {
        
    int flen;
        
    long filedes;
        
    char filename[1024];    

        web_add_cookie(
    "seraph.os.cookie=ElKlHkQmJlOkFjGjJi; DOMAIN=172.20.16.4");

        web_set_max_html_param_len(
    "1024000");    
        web_url(
    "172.20.16.4:8080"
            
    "URL=http://172.20.16.4:8080/"
            
    "Resource=0"
            
    "RecContentType=text/html"
            
    "Referer="
            
    "Snapshot=t4.inf"
            
    "Mode=HTML"
            EXTRARES, 
            
    "Url=/styles/global.css""Referer=http://172.20.16.4:8080/secure/Dashboard.jspa", ENDITEM, 
            LAST);

        web_link(
    "TEST-306"
            
    "Text=TEST-306"
            
    "Snapshot=t5.inf"
            EXTRARES, 
            
    "Url=../styles/global.css", ENDITEM, 
            LAST);    
        
      web_reg_save_param(
    "fcontent""LB=""RB=""SEARCH=BODY", LAST);
        web_link(
    "test.xml"
            
    "Text=test.xml"
            
    "Snapshot=t6.inf"
            LAST);
        
        
    //获取文本类型的文件大小
        flen = strlen(lr_eval_string("{fcontent}"));
        
    //获取二进制文件类型的文件大小
        
    //flen = web_get_int_property(HTTP_INFO_DOWNLOAD_SIZE);    
        lr_message("-----------------flen:%d",flen);
        
        
    //生成随机的文件名称,便于并发
        strcpy(filename,"d:\\123\\aa_");
      strcat(filename,lr_eval_string(
    "{Num}"));
        strcat(filename,
    ".xml");
        
        
    if(flen > 0)    
        
    {
            
    //以写方式打开文件
            if((filedes = fopen(filename, "wb")) == NULL)
            
    {
                lr_output_message(
    "Open File Failed!");
                
    return -1;
            }

            
    //写入文件内容
            fwrite(lr_eval_string("{fcontent}"), flen, 1, filedes);
            
    //关闭文件
            fclose(filedes);
        }


        
    return 0;
    }

    web_get_int_property函数获得的是上一次web函数获得响应的总的大小(包括head和body部分),由于对二进制文件,返回的数据包是没有head部分的,因此得到的大小就是二进制文件的大小,可以用web_get_int_property函数得到的值作为该文件的实际大小,至于不能用strlen方式获得下载二进制文件的大小,原因是二进制文件的内容中肯定会出现'\0'这样的字符,导致strlen函数判断失败。

    而对于Txt和XML等文件,返回的数据包中是包含head部分的,因此不能用web_get_int_property函数获得的download size来作为文件本身的大小。

    我能想到的可行的解决方案是:针对不同的文件采用不同的方法,如果需要在脚本中判断文件类型的话,可以针对下载文件的文件名后缀决定采用何种方式。

  • LoadRunner字符串与参数的操作及转换技巧

    2008-09-11 13:41:58

    转载的一篇文章

    //字符串复制
    strcpy(str,"Hello ") ;

    //字符串连接
    strcat(str,"World !");
    lr_message("str: %s",str);


    //变量转为参数,将变量str的值存到参数Param中
    lr_save_string(str,"Param");


    //参数复制
    lr_save_string(lr_eval_string("{Param}"),"Param_1");

     

    //参数转为变量
    strcpy(str1,lr_eval_string("{Param_1}"));
    lr_message("str1: %s",str1);


    //参数名称格式化输出到变量中
    sprintf(str2,"{Param_%d}",1);
    lr_message("str2: %s",lr_eval_string(str2));


    运行结果:
    str: Hello World !
    vuser_init.c(14): Notify: Saving Parameter "Param = Hello World !"
    vuser_init.c(19): Notify: Parameter Substitution: parameter "Param" =  "Hello World !"
    vuser_init.c(19): Notify: Saving Parameter "Param_1 = Hello World !"
    vuser_init.c(24): Notify: Parameter Substitution: parameter "Param_1" =  "Hello World !"
    str1: Hello World !
    vuser_init.c(30): Notify: Parameter Substitution: parameter "Param_1" =  "Hello World !"
    str2: Hello World !

  • 用loadrunner参数化下载的文件名(续下载文件并保存到本地 )

    2008-09-11 13:38:43

    转自wangyong3552128空间

    今天看了关河老师的博客一篇《用LoadRunner下载文件并保存到本地》,我很受启发,根据关河老师提供的原创脚本,我把他早博客中没有写出的那部分提供出来“我们希望每次下载保存到本地的文件采用不同的名称,或是希望根据实际文件的后缀名动态生成后缀名”,就是对下载的文件名做参数化操作,也算自己对测试经验的一次积累吧,并且希望想用loadrunner录制下载脚本/参数化文件名称的同学一个很好的学习机会,尽量少走弯路。其实我在这次的编写C脚本中得到一个很大的启发,那就是平时多去学习C语言,多去看lr帮助,多去查看LR函数。

    http://www.cnblogs.com/guanhe/archive/2006/06/27/436746.html

    我在网上找了一个网站进行录制脚本的,这个网站不做,感觉很适合我连手呵呵:http://www.netqin.com。(里面的软件很适合智能手机哦,包月很便宜的,服务也好!)

    根据关河老师的那个脚本,录制/编写完下载的脚本后,就开始参数化那个下载的文件名啦(如果我有这个需求的话)。我尝试过很多方法:

    1.对 fopen("c:\\NetQin_P919_CommMaster.SIS", "wb")里的文件名的一部分919,直接在fopen()中进行参数化:fopen("c:\\NetQin_P{随机码}_CommMaster.SIS", "wb"),结果:测试失败。

    2.就是使用“设一个数值的变量,然后把它转成字符串,再与前面的字符串连起来”,这里用到了strcat()连接函数。结果:参数设置成功,运行通过。

    具体做法:

    Action()
    {
        int flen;
        long filedes;
        char file[256]="\0";
        char fuzhi[10]="\0";
        char * chNumber=fuzhi;

        chNumber=lr_eval_string("{随机码}");
     
        strcat(file,"c:\\NetQin_P");
        strcat(file,chNumber);
        strcat(file,"_CommMaster.SIS");


       //设置参数的最大长度,注意该值必须大于文件的大小
      web_set_max_html_param_len("2000000");

     //获取响应的全部内容,作为文件内容保存
     web_reg_save_param("fcontent", "LB=", "RB=", "SEARCH=BODY", LAST);

     lr_start_transaction("down");

     web_url("down.jsp",
      "URL=http://www.netqin.com/down.jsp?downID=30&action=Url_1",
      "Resource=1",
      "RecContentType=application/vnd.symbian.install",
      "Referer=",
      "Snapshot=t3.inf",
      LAST);

        //获取响应中的文件长度
     flen = web_get_int_property(HTTP_INFO_DOWNLOAD_SIZE);

     if(flen > 0) 
     {
      //以写方式打开文件
      if((filedes = fopen(file, "wb")) == NULL)
      {
       lr_output_message("Open File Failed!");
       return -1;
      }
      //写入文件内容
      fwrite(lr_eval_string("{fcontent}"), flen, 1, filedes);
      //关闭文件
      fclose(filedes);
     }

     lr_end_transaction("down",LR_AUTO);

       // lr_think_time(3);

     return 0;
    }

    运行结果:在我的机器C盘产生了三个文件(只三个参数哦,迭代三次),NetQin_P110_CommMaster.SIS、NetQin_P112_CommMaster.SIS、NetQin_P113_CommMaster.SIS

    提供一个不错的文章去供大家练习字符串的操作:http://www.cnblogs.com/pent/archive/2007/12/17/1003363.html

     

    LoadRunner下载文件的实验

    http://www.cnblogs.com/pent/archive/2007/09/04/881708.html

Open Toolbar