LoadRunner提供了很好的對socket應用的支援,用戶可以通過錄製方法完全獲得用戶端發送和接收的資料,然後在錄製的基礎上對相應的資料進行參數化和關聯等處理。
但在有些情況下(例如,用戶端程式沒有windows上的版本),我們就很難通過錄製達成生成腳本的目標了。但如果我們能夠完全知曉服務端和用戶端的交互過程,完全手工編寫一個測試腳本也並不是一件特別困難的事情。
在本文中,我們以一個實際的例子說明如何根據服務端和用戶端交互的過程,用LoadRunner自行編寫相應的腳本。
以下是服務端工作執行緒的代碼:
但在有些情況下(例如,用戶端程式沒有windows上的版本),我們就很難通過錄製達成生成腳本的目標了。但如果我們能夠完全知曉服務端和用戶端的交互過程,完全手工編寫一個測試腳本也並不是一件特別困難的事情。
在本文中,我們以一個實際的例子說明如何根據服務端和用戶端交互的過程,用LoadRunner自行編寫相應的腳本。
以下是服務端工作執行緒的代碼:
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;
}
{
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中增加以下內容:
//建立到服務端的連接
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");
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,直接輸入:
send buf0 7
"S"
"\x00"
"1.txt"
recv buf1 2
"OK"
send buf2 3
"\x00"
"\x00"
"\x02"
"\x00"
send buf3 20
"12345678901234567890"
"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。
沒有留言:
張貼留言