LoadRunner提供了很好的对socket应用的支持,用户可以通过录制方法完全获得客户端发送和接收的数据,然后在录制的基础上对相应的数据进行参数化和关联等处理。
但在有些情况下(例如,客户端程序没有windows上的版本),我们就很难通过录制达成生成脚本的目标了。但如果我们能够完全知晓服务端和客户端的交互过程,完全手工编写一个测试脚本也并不是一件特别困难的事情。
在本文中,我们以一个实际的例子说明如何根据服务端和客户端交互的过程,用LoadRunner自行编写相应的脚本。
以下是服务端工作线程的代码:
从这段代码中可以看到,当客户端和服务端建立连接后,客户端会先向服务端发送一个请求,该请求的第一个字节是大写的“S”或是“R”,分别向服务端写文件或是从服务端读取文件。从第三个字节开始,后面的内容是请求文件的文件名。
服务端在接收到客户端的请求后,根据请求的类型,如果是“S”,则打开指定的文件,并返回一个字符串“OK”;如果是“R”,则打开指定的文件并向客户端发送“OK”+“文件长度”。
随后,如果是“S”,则由客户端发送写入的文件长度和文件内容给服务端;如果是“R”,则向客户端发送文件的内容。
到此我们已经完全明了了客户端和服务端的交互过程,因此,我们可以尝试在LR中建立一个脚本用户模拟客户端行为。
下面我们以“S”的处理过程为例编写脚本。
1、打开VUGen应用;
2、新建脚本,选择“windows sockets”协议,不需录制;
3、在Action Section中增加以下内容:
4、这样就成功的描述了整个交互过程,但还没有给出实际要发送的数据。在采用“Windows Sockets”协议的脚本中,实际发送的数据存放在data.ws Section中,因此,打开该Section,直接输入:
每个发送和接收的数据包在这里都有登记,“send”和“recv”表示数据的方向;“buf0”等表示数据包的描述,和脚本中的内容对应;接下来的一个整数表示数据包的长度;然后是数据包的内容,“\x00”表示16进制的00。
该脚本描述了客户端向服务端请求写入一个文件1.txt,文件内容为“12345678901234567890”的过程。
但在有些情况下(例如,客户端程序没有windows上的版本),我们就很难通过录制达成生成脚本的目标了。但如果我们能够完全知晓服务端和客户端的交互过程,完全手工编写一个测试脚本也并不是一件特别困难的事情。
在本文中,我们以一个实际的例子说明如何根据服务端和客户端交互的过程,用LoadRunner自行编写相应的脚本。
以下是服务端工作线程的代码:
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> DWORD WINAPI mythread( LPVOID lpParameter) // 客户线程
{
struct My my;
memcpy( & my,lpParameter, sizeof (My));
printf( " One client connect!\n " );
char str1[ 1024 ]; // 接收字符串
char str2[ 1024 ];
int i;
i = recv(my.skt,str1, sizeof (str1), 0 ); // 接收客户请求
str1[i] = 0 ;
char * filename;
filename = new char [ 255 ];
for ( int j = 2 ;j < i;j ++ ) // 获得文件名
{
filename[j - 2 ] = str1[j];
}
filename[i - 2 ] = 0 ;
if (str1[ 0 ] == ' S ' )
{
printf( " The file name : %s\n " ,filename);
ofstream out (filename); // 创文件流
if ( ! out )
{
printf( " cannot open file.\n " ); // 文件是否正确打开,打开错误则退出
send(my.skt, " q " , 1 , 0 ); // 向客户发送退出信息
closesocket(my.skt); // 解除客户连接;
return 0 ;
}
str2[ 0 ] = ' O ' ;
str2[ 1 ] = ' K ' ;
str2[ 2 ] = 0 ;
send(my.skt,str2,strlen(str2), 0 ); // 回复OK信息
i = recv(my.skt,str1, sizeof (str1), 0 ); // 接收文件长度
str1[ 4 ] = 0 ;
int len;
len = str1[ 0 ] * 1000 + str1[ 1 ] * 100 + str1[ 2 ] * 10 + str1[ 3 ];
printf( " The File lenght is: %d Byte\n " ,len);
for ( int j = 0 ;j < len;j ++ )
{
char str[ 1 ];
i = recv(my.skt,str, sizeof (str), 0 ); // 接收文件,按字节接收,接收字符串为2个字节
str[i] = 0 ;
out .put(str[ 0 ]);
}
out .close(); // 关闭文件
printf( " over!One client quit!\n " ); // 接收文件完毕
closesocket(my.skt); // 解除此客户连接
return 0 ;
}
if (str1[ 0 ] == ' R ' )
{
ifstream in (filename);
if ( ! in )
{
printf( " cannot open file or file not exist.\n " ); // 文件是否正确打开,打开错误则退出
send(my.skt, " q " , 1 , 0 ); // 向客户发送退出信息
closesocket(my.skt); // 解除客户连接;
return 0 ;
}
char ch;
int len = 0 ;
while ( in . get (ch))
{
len ++ ; // get file lenght
}
in .close();
str2[ 0 ] = ' O ' ;
str2[ 1 ] = ' K ' ;
str2[ 2 ] = len / 1000 ;
str2[ 3 ] = (len % 1000 ) / 100 ;
str2[ 4 ] = (len % 100 ) / 10 ;
str2[ 5 ] = len % 10 ;
printf( " %s " ,str2);
send(my.skt,str2, 6 , 0 ); // 发OK+文件长度
in .open(filename);
if ( ! in )
{
printf( " cannot open file or file not exist.\n " ); // 文件是否正确打开,打开错误则退出
send(my.skt, " q " , 1 , 0 ); // 向客户发送退出信息
closesocket(my.skt); // 解除客户连接;
return 0 ;
}
while ( in . get (ch)) // 发文件
{
char str[ 1 ];
strcpy(str, "" );
str[ 0 ] = ch;
str[ 1 ] = 0 ;
send(my.skt,str, 1 , 0 ); // 发送一个字符
}
in .close();
printf( " over,One client quit!\n " ); // 传输文件完毕
closesocket(my.skt); // 解除此客户连接
return 0 ;
}
printf( " Bad command!\n " );
closesocket(my.skt);
return 0 ;
}
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> DWORD WINAPI mythread( LPVOID lpParameter) // 客户线程
{
struct My my;
memcpy( & my,lpParameter, sizeof (My));
printf( " One client connect!\n " );
char str1[ 1024 ]; // 接收字符串
char str2[ 1024 ];
int i;
i = recv(my.skt,str1, sizeof (str1), 0 ); // 接收客户请求
str1[i] = 0 ;
char * filename;
filename = new char [ 255 ];
for ( int j = 2 ;j < i;j ++ ) // 获得文件名
{
filename[j - 2 ] = str1[j];
}
filename[i - 2 ] = 0 ;
if (str1[ 0 ] == ' S ' )
{
printf( " The file name : %s\n " ,filename);
ofstream out (filename); // 创文件流
if ( ! out )
{
printf( " cannot open file.\n " ); // 文件是否正确打开,打开错误则退出
send(my.skt, " q " , 1 , 0 ); // 向客户发送退出信息
closesocket(my.skt); // 解除客户连接;
return 0 ;
}
str2[ 0 ] = ' O ' ;
str2[ 1 ] = ' K ' ;
str2[ 2 ] = 0 ;
send(my.skt,str2,strlen(str2), 0 ); // 回复OK信息
i = recv(my.skt,str1, sizeof (str1), 0 ); // 接收文件长度
str1[ 4 ] = 0 ;
int len;
len = str1[ 0 ] * 1000 + str1[ 1 ] * 100 + str1[ 2 ] * 10 + str1[ 3 ];
printf( " The File lenght is: %d Byte\n " ,len);
for ( int j = 0 ;j < len;j ++ )
{
char str[ 1 ];
i = recv(my.skt,str, sizeof (str), 0 ); // 接收文件,按字节接收,接收字符串为2个字节
str[i] = 0 ;
out .put(str[ 0 ]);
}
out .close(); // 关闭文件
printf( " over!One client quit!\n " ); // 接收文件完毕
closesocket(my.skt); // 解除此客户连接
return 0 ;
}
if (str1[ 0 ] == ' R ' )
{
ifstream in (filename);
if ( ! in )
{
printf( " cannot open file or file not exist.\n " ); // 文件是否正确打开,打开错误则退出
send(my.skt, " q " , 1 , 0 ); // 向客户发送退出信息
closesocket(my.skt); // 解除客户连接;
return 0 ;
}
char ch;
int len = 0 ;
while ( in . get (ch))
{
len ++ ; // get file lenght
}
in .close();
str2[ 0 ] = ' O ' ;
str2[ 1 ] = ' K ' ;
str2[ 2 ] = len / 1000 ;
str2[ 3 ] = (len % 1000 ) / 100 ;
str2[ 4 ] = (len % 100 ) / 10 ;
str2[ 5 ] = len % 10 ;
printf( " %s " ,str2);
send(my.skt,str2, 6 , 0 ); // 发OK+文件长度
in .open(filename);
if ( ! in )
{
printf( " cannot open file or file not exist.\n " ); // 文件是否正确打开,打开错误则退出
send(my.skt, " q " , 1 , 0 ); // 向客户发送退出信息
closesocket(my.skt); // 解除客户连接;
return 0 ;
}
while ( in . get (ch)) // 发文件
{
char str[ 1 ];
strcpy(str, "" );
str[ 0 ] = ch;
str[ 1 ] = 0 ;
send(my.skt,str, 1 , 0 ); // 发送一个字符
}
in .close();
printf( " over,One client quit!\n " ); // 传输文件完毕
closesocket(my.skt); // 解除此客户连接
return 0 ;
}
printf( " Bad command!\n " );
closesocket(my.skt);
return 0 ;
}
从这段代码中可以看到,当客户端和服务端建立连接后,客户端会先向服务端发送一个请求,该请求的第一个字节是大写的“S”或是“R”,分别向服务端写文件或是从服务端读取文件。从第三个字节开始,后面的内容是请求文件的文件名。
服务端在接收到客户端的请求后,根据请求的类型,如果是“S”,则打开指定的文件,并返回一个字符串“OK”;如果是“R”,则打开指定的文件并向客户端发送“OK”+“文件长度”。
随后,如果是“S”,则由客户端发送写入的文件长度和文件内容给服务端;如果是“R”,则向客户端发送文件的内容。
到此我们已经完全明了了客户端和服务端的交互过程,因此,我们可以尝试在LR中建立一个脚本用户模拟客户端行为。
下面我们以“S”的处理过程为例编写脚本。
1、打开VUGen应用;
2、新建脚本,选择“windows sockets”协议,不需录制;
3、在Action Section中增加以下内容:
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> // 建立到服务端的连接
lrs_create_socket( " socket1 " , " TCP " , " RemoteHost=127.0.0.1:8000 " ,LrsLastArg);
// 发送“S”和文件名
lrs_send( " socket1 " , " buf0 " , LrsLastArg);
lrs_receive( " socket1 " , " buf1 " , LrsLastArg);
// 发送要写入的数据的长度
lrs_send( " socket1 " , " buf2 " , LrsLastArg);
// 发送数据内容
lrs_send( " socket1 " , " buf3 " , LrsLastArg);
// 关闭连接
lrs_close_socket( " socket1 " );
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> // 建立到服务端的连接
lrs_create_socket( " socket1 " , " TCP " , " RemoteHost=127.0.0.1:8000 " ,LrsLastArg);
// 发送“S”和文件名
lrs_send( " socket1 " , " buf0 " , LrsLastArg);
lrs_receive( " socket1 " , " buf1 " , LrsLastArg);
// 发送要写入的数据的长度
lrs_send( " socket1 " , " buf2 " , LrsLastArg);
// 发送数据内容
lrs_send( " socket1 " , " buf3 " , LrsLastArg);
// 关闭连接
lrs_close_socket( " socket1 " );
4、这样就成功的描述了整个交互过程,但还没有给出实际要发送的数据。在采用“Windows Sockets”协议的脚本中,实际发送的数据存放在data.ws Section中,因此,打开该Section,直接输入:
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> send buf0 7
" S "
" \x00 "
" 1.txt "
recv buf1 2
" OK "
send buf2 3
" \x00 "
" \x00 "
" \x02 "
" \x00 "
send buf3 20
" 12345678901234567890 "
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--> send buf0 7
" S "
" \x00 "
" 1.txt "
recv buf1 2
" OK "
send buf2 3
" \x00 "
" \x00 "
" \x02 "
" \x00 "
send buf3 20
" 12345678901234567890 "
每个发送和接收的数据包在这里都有登记,“send”和“recv”表示数据的方向;“buf0”等表示数据包的描述,和脚本中的内容对应;接下来的一个整数表示数据包的长度;然后是数据包的内容,“\x00”表示16进制的00。
该脚本描述了客户端向服务端请求写入一个文件1.txt,文件内容为“12345678901234567890”的过程。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/1384/viewspace-615952/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/1384/viewspace-615952/