3.1 LoadRunner 的运行原理
安装LoadRunner 后,在菜单“开始” 一“MercuryLoadRunner” 中,你会看 到这样一组程序,如图 3-1 所示。
• 其中Applications 下面的Analysis、Controller 和Virtual User Generator 是我们 做性能测试最常用的 三个工具。
3.1.1 LoadRunner三大高手
点击Virtual User Generator,VU就会被启动,我们会看到这样 一个窗又界面 ( LoadRunner8.0 ) ,如 图 3- 2 所 示 。
启动VU后 ,我们会在任务管理器中看到多了一个Vugen.exe进程 。虚拟用户产生器Vugen实际上是一套开发调试脚本的环境,它的任务是生成编译/解释成功的脚本,供Controller调用。注意:虽然都叫做VUScript,但其实不同的Vuser 类型却暗地里“各自为政” ,有的是解释执行的真脚本,比如CVuser;有的是编 译执行的“ 伪脚本” ,如Java Vuser 脚本。它们都将有一定的接又与Controller对接,以便Controller 调用。
现在我们选择Web(http/html) 协 议 , 新建一个空白脚本,然后保存成一个名为“example” 的脚本在C盛下,打开C盘,我们会看到在C盘下生成了一个 example” 的目录,没错,脚本其实就是一个日录,所有该脚本的信息都将放在 这个目录下 , 如 图 了- 3 所 示 , 我 们 一 一 解 读 。
首先有三个c 文件,分别是Action.c、vuser init.c 和vuser end.c,就是我们在 VU编辑器中看到的action.c、vuser_init、vuser_end两数。如果是Java Vuser,则 只有一个action.java 文件。
Default.cfg:是脚本运行的配置文件。通过VU界面设置的run-time 等信息存 储在这个文件里,同样我们也可以直接修改这个文件而达到设置run-time 的目的。 这其实为我们的性能测试驱动自动化提供了接又。
如果脚本经过解释/编译后,还会生成以下几个文件:
Pre ci.c:打开这个文件,里面藏着很多宝贝,VU的“后台支持”都在这里 呢,可以看到,虽然我们在VU编辑器中只有三行代码,但生成的最终C 文件已 经达到了几百行,包括LoadRuner 引用的各个程序文件和外部西数。“前端简单, 后台复杂” 的道理再次被验证。Java脚本则编译后直接生成了一个action.class 文件。
脚本运行的同时,还会生成output.txt 和mdrv. 1og 等文件,这些文件记载了 VU运行的1og,如果我们在碰到一个无从下手的奇怪错误,可以到这些文件里试 试运气。
下面 ,我们再来看看Controller 。 启动Controller ,我们看到如图了- 4 所 示 的 界 面:
Controller 是整个性能测试的核心,它是一个框架程序,与LoadRunner 的各个模块都有交互 ,可 以说 Controller是 “ 集大权于一身 ” ,控 制 着 整 个 性 能 测 试 的 过 程 。 第 一 , 定 义 日 标 , Controller将 Vugen 开 发 的 脚 本 按 一定 规 则 组 织 成 性 能 测 试场景,其中有面向目标场景和手工场景 (如图中的 Maunaul Scenario 和 Goal-Oriented Scenario)。第二,分派执行,Controller 规划测试拓扑,把测试任 务指定在若干个Load Generator 上以多进程/ 多线程的方式在运行。第 三,度量评 估,Controller 同时实现无代理方式的监控功能,监控多种服务器的多个性能参 数,并把监控的结果数据提供给Analysis 进行分析,后者得出此轮测试是否通过 的结论。
提示:“ 定义” , “执行” 和“ 评估” 是科学做事的三个基本而关键的要素, 在我们的社会生活中其实处处都有体现,小到一个软件项目,大到一个国家政 体。比如立法、行政、司法三权分立思想,为什么不是五权分立或七权分立? 因为三权恰恰对应了这三个要素,立法对应“ 定义” ,行政对应“ 执行” ,司 法对应“评估” 。很显然LoadRunner 的设计者考虑到了这些要素同样存在于性 能测试中,并通过Controller 来实现。
而Anal ysis 实际上是LoadRunner 里最“聪明” 的模块。它负责在一大堆数据 里寻找不同数据之间的关联关系,构建线性函数,以报表、图示的方式展现给用 户。我们做性能测试的目的是为了找到软件系统的性能瓶颈,而Anaylysis 会给我 们分析瓶颈提供重要线索。
3.1.2 三大高手联手的 一场性能测试盛大演出
好, 三大高手已经亮相了,我们下面开始介绍它们在性能测试中的演出。
(1)首先确定哪些功能需要做性能测试,然后启动vU 录制相应的业务,生 成自动化脚本。在这个录制过程中,VU就是一个超强模份秀,用户所做的每一个 操作,都暗记在心,并认真记录笔记,也就是生成脚本。
(2)如果说性能测试的,总导演是性能测试工程师,那么Controller 就是当之无 愧的执行导演了。Control ler 首先把演员(虚拟用户)都召集齐,然后告诉把剧本 交给它们 (VU脚本),分配到各自的舞台(Load Generator),一声令下,演出 开始!
注意:当Controller 运行场景任务时,在任务管理器里可以找到一个名为 “Tr bri dge ” 的进程,顾名思义,它就是起到联系Control ler 和Generat or 的桥梁 作 用 ,Controller 的 指 令 就 是 通 过 它 来 下 达 到 LoadGenerator 的 。而 LoadGenerator 接到运行指令后,会启动 一个mdrv.exe,以多进程/多线程方式调用运行脚本文 件。通常 一个mdrv.exe 可以驱动50 个线程或一个进程。如果在测试场景中定义 更大的Vuser 数目,那就要开启更多的mdrvo
(3)执行导演Controller 在开机之后,它最关心的是演出效果是否达到预期, 于是它在演出的同时,还在现场搜集台下的观众对这场演出的评价。其中最重要 的就是各个Load Generator 记录的最终用户响应时间和脚本执行的日志,这是演 出是否达到预期最直接的判断依据。
(4)演出终于结束了,Controller 执行导演命令各个Load Generator 把它们记 录的数据传送回Controller,然后Controller 执行负责将数据汇总,存储在结果目 录下的output.mdb 文件里。这其实是 一个Access 数据库,如果我们打开这个数据 库会发现有多个数据表,存储着不同类型的数据。
( 5 )在 没 有 保 存 之 前 ,结 果 数 据 是 存 放 在 Wi n d o w s 临 时 目 录 下的 。 保 存 之后 , 数据分析工具Analysis 从output.mdb 中读取测试结果数据,进行分析工作,展现 出最后这场盛大演出的总结报告,而我们的性能测试工程师可以从这份报告里发 现蛛丝马迹,确定瓶颈,找出关键的调优因素。
提示:多进程/多线程方式: 用广可以在Controller的runtimesetting中选择Vuser的运行方式 :多线程/多进程。多进程和多线程方式的区别是:Controller 將使用驱动程序mdrv 运行 Vuser。如果按进程方式运行每个 Vuser,则对于每个Vuser 实例,都将启动 一个mdrv 进程。多个mdrv 进程会占 用大量内存及其他系统资源,这就限制了可以在任一负载生成器上运行的Vuser 的数量。如果选择按线程方式运行,在默认情况下,Controller 为每50 个Vuser 仅启动 一个mdrv.exe 进程,而每个Vuser 都按线程远行,这些线程Vuser 将共 享父进程的内存段。这就节省了大量内存空间,从而可以在一个负载生成器上 远行更多的 Vuser 。
提示: 但任何选样都是有两西性的。选择线程方式运行Vuser 会带来一些安全 问题。因为线程的资源是从进程资源中分配出来的,因此同一个进程中的多个 线程会有共享的内。存空间,这样可能会引起多个线程的同步问题,调度不好, 就会出问题,比如A线程要用的资源必须等待B线程释放,而B 也在等待其 他资源释放才能继续。这就是有些网友碰见的问题:同一个测试场景,用线程 并发就会超时失败或报错,而用进程并发就没错。
注意:在LoadRumer中,只有线程安全协议才能作为线程运行。需要注意的是 下列协议不是线程安全协议:Sybase-Ctlib、Sybase-Dblib、Informix、Tuxedo 和PeopleSoft-Tuxedo.
3.2 LoadRunner的录制原理
3.2.1 网络协议与LoadRunner的Vuser
LoadRunner提供了多种Vuser技术,通过这些Vuser技术,LoadRunner 可以 在不同类型的客户端/服务器体系结构下生成相应的脚本。
我们在新建 一个VU脚本时,会弹出这样一个选择对话框页面,如图3-5所 示。
在全部协议列表(Al Protocols)里,有几十种协议供选择,相信刚开始接触LoadRunner 的朋友,看到这么多的名词,必然心生许多疑惑:
(1)VU提供的Protocol 是干什么用的?也就是说,在录制脚本时,为什么要选择 Protocol 类型?
(2)被测软件系统采用的协议与LR提供的Protocol 类型的关系?比如VU里的SMTPProtocol 和邮件系统的SMTP邮件发送协议有什么样的关系?
(3 )在对一个己知协议的软件系统做性能测试时,我们有什么技巧或原则去选择VUProtocol 类型么? 在得到这些问题答案之前,我们有必要再次翻出计算机网络教材,温习 一下网络知识。
网络协议:
网络协议是网络通信技术与计算机技术相结合的产物。所谓计算机网络,就 是把分布在不同地理区域的计算机与专门的外部设备用通信线路互联成 一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息,共享硬 件、软件、数据信息等资源。
通俗地说,网络就是通过电缆、电话线或无线通信等互联的计算机的集合。 那么网络上的计算机又是如何交换信息的呢?就像我们说话用某种语言一样,在 网络上的各台计算机之间也有一种“语言” ,这就是网络协议,不同的计算机之 间必须使用相同的“语言〞,才能进行通信。网络协议可以用一个层状模型来描 述,如图 3-6 所示。
各层的关系是,上层协议的功能都是在其下层协议功能的基础上实现的。 从物理层的原始信号处理开始,每 一层协议完成自己的功能,下层协议为上层 协议提供服务,直到应用层协议得到数据,向上提供给网络应用。层次越低的 协议就越接近机器语言,也就越难懂:层次越高的协议就越接近人类语言,也 就越好理解。
通常,网络层是 一个分水岭,网络层向上是有关计算机软件技术,网络层向 下是有关计算机通信技术。因此软什人员能接触到的通常是在应用层和传输层。 应用层协议种类繁多,层出不穷,常用的网络应用协议如表3- 1所示
除了这些标准开放协议外,还有一些常见的中问件,如oracle、sql server、Tuxedo 等,它们的客户端和服务器通信的方式,也可看做是应用协议的一种。
LoadRunner Protocol 对网络协议的映射:
在VU新建一个脚本的时候,LoadRunner 会提示让我们选择协议。选择并 进入协议环境中,才能录制和编辑脚本。同时,LoadRunner 的手册会告诉我们 LoadRunner的协议类型有corba, Com, DB2, DNS, EJB, FTP, i-mode, SQL Server, Oracle, Tuxedo, SMTP, HTTP, Winsocket 等多种类型。但手册并没有 告诉我们LoadRunner 为什么会有不同的协议类型?为什么是这些Vuser,而不 是 其他 的 ?
图3-7说明了LoadRunner Vuser 和网络协议的关系。
由此可见,LoadRunner 的每个Vuser 类型对应 一种网络协议结构模型。如 FTP Vuser 对应的是FTP 协议http Vuser 对应的是HTTP 协议,而Tuxedo Vuser 对应的是Tuxedo 客户端/ 服务器结构。而在计算机网络产业迅速发展的今天,网 络协议架构也是“后浪推前浪",层出不穷的。LoadRuaner 则能很快地增加新 的Vuser来适应新的网络协议,比如,在LoadRunner 8.0之前没有WebService的Vuser,由于WebService 系统架构的普及,LoadRunner从8.0开始增加了WebService Vuser.
案例
可以用个例 子来更加通俗形象地说明这个关系:网络协议是计算机之间沟通 交流的“语言” ,计算机都是通过“语言” 来说话的,LoadRuner 要知道计算机 之间说什么,它只需派出一个翻译问谍(Vuser )监听计算机的对话就可以了,听 完了,还要记下来生成脚本,这样做就OK 了。但是很遗憓,这个世界上不是只 有一门语言,不仅有汉语,还有英语、法语等几十种语言,计算机也面临这种问 题,〝语言” (网络协议)有上百种,不同的计算机之间用不同的语言说话,甚 至方言也都有(增强协议),在这种情况下,我们可怜的LoadRunner 只好增加人 手,培养不同语言的翻译人才(LoadRunner Protocol),如果计算机是用A语言 说话,LoadRunner 就要派出A语言翻译,如果计算机用B语言说话,LoadRunner 就要派出B 语言翻译,这样问题就能得到解决了。但同时也要注意的是,一定不要 派错了翻译(选错了Protocol 类型),如果计算机用A语言,而LoadRupner 派出 B语言翻译,毫无悬念,LoadRunner 要为自己的错误负责,可怜的B翻译将会什 么也听不懂,交了白卷。
LoadRunner 的Vuser 和网络协议区别在于, 一般地,网络协议都会有自己的 一套操作原语,而LoadRunner 的协议则对这些原语进行了一些封装工作,让它们 变得更加容易被理解和使用。比如HTTP 协议有get、post 原语,LoadRunner 的httpVuser则有web wrl、web_submit data两数相对应。无疑这样增加了脚本的可 读性和可维护性。
需要注意的是,中间件协议和开放标淮协议不同的是,开放标淮协议是基于 TCP 协议上提供了 一套操作原语,使用此协议不需要任何额外的安装,而中间件 协议是以API 形式提供给上层调用。因此LoadRunner 在使用中间件类型协议时, 需要保证本机己经安装了本中间件的客户端。
另外LoadRunner 同时还提供了一些模板虚拟用户(Template Vuser ),主要 是针对不同的计算机语言,有JavaVuser、CVuser, VBVuser、JavaSeript Vuser、 VBScriptVuser。这些TemplateVuser 不针对任何特定协议,因此也没有录制的功 能,适合自编写脚本。
正确地选择LoadRunner 协议之后,我们就可以录制基于此协议的採作,并自 动生成脚本了。如果错误地选择 了协议,那我们的录制就一无所获。
3.2.2 选择LoadRunner Protocol 的两大定律
在选择LoadRunner 协议之前,我们首先要弄懂被测软件系统的架构,知道系 统由哪些节点组成,每个节点之间的通信方式。然后根据以下原则来选择 LoadRunner 协议。
选择第一定律,以客户端和其直接连按的Server通信方式为准。
因为LoadRunner 的Vuser 模拟的是客户端,所以Vuser 和真实的客户端一样, 和离它最近的Server 打交道,而和整个系统的功能无关。比如一个webMail 系统, 虽然它是实现了Mail 的功能,但作为Client 端的正直接连接的是Web Server 而 不是MailServer,在这种情况下我们只考虑I 和WebServer的通信方式,也就是 HTTP 协议。
选择第二定律:匹配原则由高协议到低协议。
确定了被测系统的网络协议后,选择LoadRunner Vuser应该以实际网络协议性匹配为佳,如不能匹配,LoadRunner Vuser 则降一个协议层次再次匹配。
比如,我们如果碰到一个网络应用,其采用的应用协议比较独特,在 LoadRunner 里没有找到合适的协议,那么我们就降低 一个层次,用Winsock * 录 制,那是肯定没有问题的。因为几乎所有的网络传输中都是基于TCP 协议或UDP 协议的,而Socket 协议正是这一级上的协议。但是由于Socket 协议级别太低,你 录下来的东西是很难理解的,都是Socket、Port、Data 之类的东西。所以,尽量 用高层协议来录制,我们就能看懂了。
为什么要这样选择Vuser 类型,这和LoadRunner 的录制技术有关,下面我们 就看看LoadRunner 是如何录制的。
3.2.3 LoadRunner 录制技术
Vuser 脚本开发是LoadRunner 做性能测试一个很关键的任务。Vuser 脚本通 过执行对服务器API 的调用来直接与服务器通信,而不需要依赖客户端软件。这 样,甚至在客户端软件的用广界面完全开发好之前,便可以使用vuser 来检查服 务器性能。
能够录制脚本是Load Runner 提供给用户的一项非常重要的功能,它使生成脚 本的工作变得简单,用户先录制,然后在自动产生的脚本的基础上进行修改,从 而快速开发出一个逻辑功能和容户端软什完全一样的压力脚本程序。这会大大减 少性能测试中开发脚本的难度和工作量。 我们在感受录制脚本方便的同时,也会觉得Vuser 很“聪明” ,人工操作一 遍,Vuser 就能根据我们的操作生成相应的脚本。那么Vuser 是怎么做到的呢?
1. 录制实例
Vuser 通过录制客户端和后台服务器之间的通信包,分析其中的协议,自动产 生脚本。
录制技术主要是通过Pro xy 的方式来实现的,如图3- 8 所示。
在录制时,用户在操作客户端的同时,Vugen 会捕获软件客广端到服务器的 网络传输数据,然后对捕获的数据包进行拆包。由于协议的不同就是体现在数据 包的结构不同上,LR 通过对包结构的分析,判断是不是它支持的协议,对包数据 的分析,来获取用户发送的东西
比如,你用FIP 协议去录制一个访问网页的正操作,那肯定是无所收获的。 因为LR没有在网络中截获到FTP 协议格式的包,都是HTTP 协议格式的包,它不认, 当然就是一个录制为空的结果了。
如果我们选择了LoadRunner 的WinsockVuser,去录制一个FTP登录的操作, FIP Server是192.168.1.1,FTP端又号是21,那么VU在录制之后,会将登录的 操作以Socket 方式记录下来,生成Socket层次的脚本,类似如下示例:
Action()
{
Irs_create_socket ("socketI',
"ТСР" "LocalHost=0" "RemoteHost=196.168.1.1:21", LrsLastArg) :
Irs_receive ("socketl" , "buf2", LrsLastArg) ;
1rs_send ("socketl",
"buf3", LrsLastArg) ; 1rs_receive("socket1", "buf4", LrsLastArg);
Irs_send ("socket]", "buf5", LrsLastArg) ; 1rs_receive ("socket]", "buf6", LrsLastArg) ;
Irs_send ("socketl",
"buf?", LrsLastArg) ; Irs_receive ("socket]", "buf8", LrsLastArg) ;
Irs_send ("socketl", "buf9", IrsLastArg); Irs_receive ("socket]", "bufl0", LrsLastArg) ; Irs_close_socket ("socket3");
return U;
}
可以看到一次FIP登录,其实是在Socket层次上,按照Socket “发-收-发-收-发-收” 完成了10 次交互。其中bur2、bur3、buf4 等为Socket 数据缓存区,包含着 录 制 时 捕 获 的 数 据 包 。 我 们 查 看 一 下 b u f f e r , 可 以 看 到 如 下内 容 :
bur2 是客户端发出建立FTP Socket 连接请求之后,服务器返回的消息:
recv buf2 148
"220-Filezilla Server version 0.9.11 beta\r\n"
"220-written by Tim Kosse (Tim. Kosse@gmx.de) \r\n"
220 Please visit http: //sourceforge.net/projects/filezilla/\rin"
recv-一这是一个从Server 接收的数据,数据存放在名为“bur2” 的数组变量 里。
148- buf2 的长度是148 个字节。
220 —是FTPServer返回的状态码,它的意思是"Servicereadyfor newuser.” 这说明FTP Server 已经准备好,等待客户端发登录请求了。其余的是Server 的- 些描述信息。
bur3 是客户端向FTP Server 发送的登录请求:
send buf3 11
"USER test\r\n"
可 以 看 到 , 这 个 b u t 3 的 长 度 是 1 1 个 字 节, 内 容 就 是 “ USERtestrin ” , 登 录 用户名为“rest ” ,注意: “上” 和“四” 是转义字符,为命令换行,因此它们各 自只占用一 个字 节。
FTPServer 返 回 b u f 4 , 信 息 如 下, 要 求 输 入 密 码 :
recv buf4 35
"331 Password required for cdctest\r\n"
客户端紧接着发送密码信息,如buf s:
send buf5 11
"BASS test\rin" Server
又返回信息:
recv buf6 15
"230 Logged on\r\n"
230- 说明用Ser ver 已经确认用户FTP 登录成功。
bur7 , buf8 , burg 是 获 取 日 录 信 息 的 请 求 和 响 应 , 不 再 一 一分 析 。
可以看到,Socket 脚本按照Socket 的交互过程来回放,但是它的最大缺点是 处于太底层。要分析和修改Socket API 的脚本以及数据包的具体内容将是一个繁 重而且烦人的工作,进行关联的工作的难度也将大大提高 (关联是一个重要的概 念,我们將在后面详细叙述)。
因此,vugen 应该尽可能地产生更高层网络协议的脚本,方便用户的阅读和 修改工作。
下面 我们 再 用 F T P 协 议 录 制 同 样 的 F I P 登 录 操 作 :
// Logon to the FTP server
ftp_logon ("FTP',"URL-ftp://user:pwa@192. 168. 1. 1:21", "LocalAddr=0"LAST);
这个fp_1ogon 两数的调用将产生TCP 层的10 次收发动作,由此可见,高层 协议录制产生的脚本更加简单,而且更加容易被我们理解。
当然,Socket 方式是一切应用层协议的基础,Socket脚本是一种通用的方式。 对于Vugen 不支持的应用层协议,只能通过Socket 层次来录制。因此LoadRunner 的Winsock 协议可算得上是一个万能协议,但它的使用概率却可能是LoadRunner 协 议 族 中 最 低 的 。 一方 面 是 L o a d R u n n e r 的 协 议 基 本 覆 盖 到 所 有 的 主 流 软 件 系 统 网 络架构,除非特殊的网络应用,用户 一般都能在LoadRunner 协议族中找到合适的 解 决 方 案 : 另 一 方 面 , W i n s o c k 协 议 由 于 其 过 于 底 层 , 决 定 了 它 “ 什 么 都 能 干” 但其实是“什么都干不好” 的命运。
2. 食客问题
所 谓 的 关 联 (C o r r e l a t i o n ) 就 是 把 脚 本 中 某 些 写 死 的 (h a r d - c o d e d ) 数 据 , 转变成是撷取自服务器所送的、动态的、每次都不一样的数据。如果这样说还不太 明白的话,可以想象,我们去北京一家小吃店吃饭,我点了一碗鸭血粉丝汤(南 京著名的小吃),店里的化计记下我点的菜,然后给我 一张带有数字的小票,比 如97号,过了一刻钟,伙计喊“97 号好丁” ,于是,我可以拿着这张97号小 票去出货窗又,伙计看到97 号小票,跟底票对照,OK,给我一碗鸭血粉丝汤。 我于是乎就可以享用我的鸭血粉丝汤了。可以预见的是,吃完之后,如果我拿着 97 号小票去窗又试图再领一碗鸭血粉丝汤,不出意外的话,会遭来饭店伙计的 白眼。
我们完全可以把这个食客的场景换成客户端- 服务器交互场景,服务器就是一 个饭店,食客就是客户端。第一次用户操作,vU 开始录制,服务器发给客户端一 张带有数字的“ 小票” ,客户端凭这张“小票〞去服务器要求服务,这个过程还 有•小票” 上的数字都被VU录制下来(叫做hard-coded),生成了脚本, 一切 O K 。 但 当脚 本 回 放 的 时 候 , 出 问 题 了, 客 户 端 拿 着 一 张 “ 过 期 ” 的 “ 小 票 ” 向 服 务器请求服务时,毫无疑问,服务器亳不留情地拒绝了这次服务。 显而易见,要避免出现 “食客问题“,就不能hard-code,就要保证脚本每次 运行时,客户端的“小票” 上的数字与服务器最新提供给它的小票数字保持一致。 解决方法就是我们本节要提到的关联,即建立客户端和服务器数据的联系。其原 理是第一次客户端接收服务器的数据时,将“小票” 保存在一个变量中,然后容 广端在后面的发向Server 的请求中,从变量中取出“ 小票” 发给服务器。
来,让我们通过一个实际的例子来看看关联是如何实现的。
在Web 系统架构中,浏览器向Web 服务器发送第一个请求后,在服务器的 回应数据包中,就有一个“小票” ,不过在这里不叫“小票”,而是叫做Session D。以后浏览器再向服务器发送请求时,都会在请求数据包中夹带这个Sesion ID, Web服务器接下来就会利用这个Session D 来辨识跟它要数据的是不是同一个浏 览器。对于每个新的交易,服务器都会产生新的Session ID给浏览器。如果Vugen 还 是 用 旧 的 S e s s i o n I D 向 服 务 器 要 数 据 ,服 务 器 会 发 现 这 个 S e s s i o n 1 D 是 失 效 的 或 是它根本不认识这个Session ID,当然就不会传送正确的网页数据给Vugen 了。
下面的图3-9 说明了这样的情形。
要对付这种服务器,我们必须想办法找出这个Session 四D到底是什么,位于 何处,然后把它撤取下来,放到某个参数中,并日取代掉脚本中有用到Session D 的部分,这样就可以成功骗过服务器,正确地完成整个交易了。
关联是Vugen 开发脚本一个非常重要的功能。因此我们在VU 一章中将会详 细地介绍如何使用关联。