性能測試中Socket協議

其實在性能測試中HTTP協議居多。但是Socket也是偶爾能遇到

1. 如何開始錄製一個最簡單的收發數據包腳本

開始錄製腳本的時候,使用了一個綠色軟件SocketTool.exe,在本機啓動了一個TCP服務器端:

使用loadrunner錄製windows application,啓動一個新的SocketTool.exe,創建一個TCP Client,鏈接剛纔啓動的服務器,鉤選上顯示十六進制值,發送313233,別寫空格進去,點擊發送數據,然後再在服務器端發送點數據回客戶端,最後客戶端點擊斷開,腳本就錄製完成了。

腳本就四句:

lrs_create_socket("socket0", "TCP", "LocalHost=0", "RemoteHost=server:60000", LrsLastArg);

lrs_send("socket0", "buf0", LrsLastArg);

lrs_receive("socket0", "buf1", LrsLastArg);

lrs_close_socket("socket0");

數據文件data.ws:

;WSRData 2 1

send  buf0 3

         "123"

recv  buf1 3

         "456"

-1

後面的腳本就在此基礎上修改了。

2. 寫日誌文件

 

假設腳本併發了五個用戶,如果都往一個日誌文件裏面寫入內容,就可能出現各個用戶日誌交織在一起的情況,如果需要每個用戶獨立使用自己的日誌文件,可以創建一個參數vurid

sprintf(cReqSeqNo,"%s%s20d459b3412a2b",cNow,);

定義變量:

char cLogFile[100]="\0";   //日誌文件

long filedeslog=0;         //日誌文件句柄

在vuser_init中打開日誌文件:

sprintf(cLogFile,"lrsocket%s.log",lr_eval_string("{vurid}"));

if((filedeslog = fopen(cLogFile, "a+")) == NULL)

{

         lr_output_message("Open File Failed!");

         return -1;

}

//寫入日誌文件內容

fwrite("\nopen file:", strlen("\nopen file:"), 1, filedeslog);

fwrite(cFileName, strlen(cFileName), 1, filedeslog);

fwrite("\n", 1, 1, filedeslog);

在vuser_end中關閉日誌文件:

fclose(filedeslog);

3. 一行一行讀數據包文件

定義部分:

char cFileName[100]="\0";     //數據包文件名

long filedes=0;            //數據包文件句柄

char cLine[2048]="\0";      //文件中一行

讀文件方法:

sprintf(cFileName,"%s","data.txt");

if((filedes = fopen(cFileName, "r")) == NULL)

{

         lr_output_message("Open File Failed!");

         return -1;

}

while (!feof(filedes)) {

         fscanf(filedes, "%s", cLine);

         lr_output_message("read:%s", cLine);

}

fclose(filedes);

4. 字符串轉換爲十六進制數據包

定義:

unsigned char cOut[1024]="\0";      //記錄轉換出來的數據包,發送出去的數據包

在這裏雖然表面是字符數組,不過請大家千萬別把cOut[]當成字符串來處理,而應該理解爲一個存放一系列十六進制數據的數組。這有什麼區別嗎?當然有。

比如你現在要發出一個數據包16進制是:31 32 00 33 34,該數組中就該存儲着(十進制):

cOut[0]=49

cOut[1]=50

cOut[2]= 0

cOut[3]=51

cOut[4]=52

發送數據包的時候就應該發送長度爲5,如果處理爲了字符串,發送strlen(cOut),可以想象,逢零就停止了,只發出去了前兩個字節。接收的時候自然也不可以使用strcpy(cOut,BufVal),因爲遇到零就會停止,如果包中有00字節,就會造成數據不完整。

//進制轉換

m=0;

memset(cOut,0,sizeof(cOut));

for (k=0;k<strlen(cLine);k++) {

         if (k % 2==1) {

                   cTmp[0]=cLine[k-1];

                  cTmp[1]=cLine[k];

                   cTmp[2]=0;

                   sscanf(cTmp,"%x", &lngTrans);

                   cOut[m]=lngTrans;

                   m++;

         }

}

首先初始化cOut的所有字節爲0;

讀取從文件中取出的一行;

每遇到偶數字符,就讀出來兩個字符,放入cTmp字符串,使用sscanf(cTmp,"%x", &lngTrans);

比如cTmp中存着”31”,理解爲16進制轉換出來,lngTrans=0x31;

然後再把轉換出來的數據放入cOut中,得到要發出的數據包

如果想看看cOut裏面存的內容:

unsigned char *p;

 

p=cOut;

for (i=0;i<strlen(cLine)/2;i++) {

         lr_output_message("package ready:%x,%d,%x",p,*p,*p);

         p++;

}

在loadrunner中不可以直接引用cOut[0]的方式打印值,需要使用指針。連指針的地址都打給你看了,這下夠清楚了吧。

5. 發送自己定義的數據包

建立鏈接我就不寫了,發送自己定義的數據包:

lrs_set_send_buffer("socket0", (char *)cOut, strlen(cLine)/2 );

lrs_send("socket0", "buf0", LrsLastArg);

說明:

1.         (char *)cOut 是因爲函數的參數定義
int lrs_set_send_buffer ( char *s_desc,char *buffer, int size );

2.         strlen(cLine)/2不可寫爲strlen(cOut),一定要牢牢記住這裏不是發送的字符串,而是一個二進制數據包;

6. 接收數據包到自定義緩衝區

代碼:

char *BufVal;              //記錄接收到的數據包

int intGetLen=0;           //記錄接收數據包的長度

 

lrs_receive_ex("socket0", "buf1", "NumberOfBytesToRecv=4", LrsLastArg);

lrs_get_last_received_buffer("socket0",&BufVal, &intGetLen);

說明:

1.         intGetLen必須定義爲int,而不可是long,爲啥?函數定義決定的:
int lrs_get_last_received_buffer ( char *s_desc, char **data,int *size );

2.         "NumberOfBytesToRecv=4"此處loadrunner的幫助中例子寫錯了,當時我照着粘貼下來,死活報那個恐怖的<memory violation : Exception ACCESS_VIOLATION received>,後來仔細看了看,明白了,例子上NumberOfBytesToRecv前面多了一個空格,刪除了就可以了;

3.         定義接收數據包長度,這個參數只適應於TCP協議,UDP就不行了

7. 從自定義緩衝區讀出數據

代碼:

char cGetLen[5]="\0";      //記錄接收到的前四個字節

 

memset(cGetLen,0,sizeof(cGetLen));

for (j=0;j<intGetLen;j++) {

         sprintf(cT1,"%02x",(unsigned char)*BufVal);

         strcat(cGetLen,cT1);

         BufVal++;

}       

說明:

1.         初始化接收數組cGetLen所有字節爲0;

2.         (unsigned char)*BufVal將BufVal指向的值一個個字節讀出,按照無符號數解讀爲16進制和十進制,如果不設定爲無符號數,碰到諸如0xA0,轉換成十進制字符串就不是”160”,會變成一個負值”-95”,高位被解讀爲了符號;

3.         cGetLen不用定義爲無符號的,他只是用來將16進制串轉化爲字符串寫入日誌用的,並不是存儲的數據包

8. 如何釋放自定義緩衝區

代碼:

for (j=0;j<intGetLen;j++) {

         BufVal--;

}

lrs_free_buffer(BufVal);

用完了緩衝區BufVal後需要釋放,否則BufVal不斷的取得返回,就會越來越長,定位就變得麻煩,用起來不方便。最初釋放的時候也是遭遇<memory violation : Exception ACCESS_VIOLATION received>。查看了例子,想了半天,終於明白了,我之前讀取緩衝區操作了指針,而釋放需要是初始的頭指針,於是寫了一段狗血的代碼,通過循環,回到初始狀態進行釋放。-_-|||

9. 如何根據數據包返回計算爲十進制數

接收數據的時候是分成兩個步驟,首先取得四個字節,計算出後續數據包的長度,然後再指定長度進行接收。所以得到返回的四個字節後,需要計算出長度。這裏我是一個字節一個字節轉換爲十進制的值,例如:

0x11 0x22 0x33 0x44=0d17 0d34 0d51 0d68=256^3*17+256^2*34+256^1*51+256^0*68

代碼:

定義:

unsigned char cT2[3]="\0"; //記錄接收到的10進制字符串

long lngGetData=0;         //記錄後續數據包長度

int iByte=0;               //四個字節的單個字節的10進制數

int iaR[4]={0,0,0,0};      //記錄四個字節的十進制值

 

 

for (j=0;j<intGetLen;j++) {

         sprintf(cT2,"%d",(unsigned char)*BufVal);

         iByte=atoi(cT2);

         iaR[j]=iByte;

         BufVal++;

}

 

lngGetData=iaR[0]*16777216+iaR[1]*65536+iaR[2]*256+iaR[3];

通過atoi把ASCII碼轉換爲int值,比如cT2=”160”,atoi後就成了數值的160;

 

五.小節

 

學多用少是一個大的戰略原則,儘可能用最簡單最適合的法子解決問題,loadrunner的socket測試本篇中沒有提到如何和參數打交道的問題,也沒有描述UDP和TCP的細節差異,接收報文也只是長度數據兩段式的收取,沒有講到不確定長度使用終止串的收取方法,一篇文章終歸難以盡言,拋磚引玉,如有錯漏,不吝賜教。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章