在LoadRunner中使用protobuf

本文记录的是在LoadRunner中模拟客户端使用protobuf发送和接收消息的过程,需要将使用的protobuf相关函数封装在dll内并在虚拟用户脚本中调用,涉及到的文件和源代码可以到这里下载。

编写message

在实际应用中,可以直接使用客户端的中相关的代码文件,这里由于做演示,因此新生成一个简单的message。使用的protobuf版本为2.5,Visual Studio版本为2012。

编写MsgName.proto文件如下,只有一个字符型的数据name

1
2
3
4
5
option optimize_for = LITE_RUNTIME;
message MsgName
{
required string name = 1;
}

运行脚本生成消息的相关代码:

1
2
3
protoc --cpp_out=. *.proto
copy *.cc *.cpp /Y
del *.cc

得到MsgName.hMsgName.cpp

构建dll工程

建立工程并包含依赖文件

在Visual Studio中构建Win32工程,并设置为DLL类型。

fig

fig

完成工程建立之后,在工程中导入protobuf相关的文件,更新附加包含目录。导入之前生成的MsgName.hMsgName.cpp

接口编写

新建一组h和cpp文件,如pb.hpb.cpp,输出给LoadRunner使用的函数将在这两个文件中编写。首先在pb.h中声明函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma once;  

#ifdef DLL_IMPLEMENT
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

#include <string>

// 辅助函数 内部使用 将protobuf序列化后的字符串封装成消息发送给服务器
int constructMessage(char* des , short nType, int nIndex, const std::string &dataStr);

// 导出函数 给LoadRunner调用 输入字符串name 赋值给消息并序列化 传给des
extern "C" DLL_API int getSerializedName(char* des, char* name);

// 导出函数 给LoadRunner调用 输入接收到的消息src及长度len 反序列化 并得出name字段传给name
extern "C" DLL_API int getParsedName(char* name, char* src, int len);

在pb.cpp编写函数实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#define DLL_IMPLEMENT  

#pragma comment(lib, "ws2_32.lib")

#include "pb.h"
#include <Windows.h>
#include <intrin.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "MsgName.pb.h"

int constructMessage(char* des , short nType, int nIndex, const std::string &dataStr )
{

//数据有效长度 不包含nSize本身占的字节
short nSize = sizeof(short) + sizeof(int) + dataStr.length()*sizeof(char) ;

int pos = 0;

nSize = htons(nSize);
memcpy(des + pos,&nSize,sizeof(short));
pos += sizeof(short);

nType = htons(nType);
memcpy(des + pos,&nType,sizeof(short));
pos += sizeof(short);

nIndex = htonl(nIndex);
memcpy(des + pos,&nIndex,sizeof(int));
pos += sizeof(int);

memcpy(des + pos,dataStr.c_str(),dataStr.length()*sizeof(char));
pos += dataStr.length()*sizeof(char);

//返回构建完成的消息的长度
return pos;

}

DLL_API int getSerializedName(char* des, char* name)
{

// 声明一个MsgName
MsgName msgname;

// 为其传入name字段
msgname.set_name(name);

std::string s_data;

// 序列化给s_data
msgname.SerializeToString(&s_data);

// 构建消息并传给des
int len = constructMessage(des, 0, 0, s_data);

// 返回构建成的消息的长度
return len;
}

DLL_API int getParsedName(char* name, char* src, int len)
{

// 去掉消息前部的 nType 和 nIndex 的数据
int offset = sizeof(short) + sizeof(int);

if(len<=offset) return -1;

std::string data;
data.assign(src + offset , len - offset);

// 声明一个MsgName
MsgName msgname;

// 从data反序列化
if (!msgname.ParseFromString(data)) return -1;

// 得到消息中的name并赋给name
strcpy(name, msgname.name().c_str());

return 0 ;
}

完成之后编译并生成pb_dll.dll

在LoadRunner中使用导出的dll文件

加载文件

将上一步生成的pb_dll.dll放入虚拟用户脚本的文件夹内,使用之前先加载dll,例如可以在vuser_init.c中加入以下代码:

1
lr_load_dll("pb_dll.dll");

如返回值为0,则表示加载成功,可以在Action.c中使用dll内封装的函数。

调用函数

Action.c内直接调用函数即可,详见以下代码,封装name生成消息并序列化,发送给服务器后得到返回的消息,再将返回的消息反序列化出返回的name

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Action()
{

char* sendStr;
char* rece;
int receLen = 0;
char* name;

lrs_create_socket("socket0", "TCP", "LocalHost=0", "RemoteHost=192.168.1.200:2000", LrsLastArg);

rece = (char*)malloc(1024 * sizeof(char));
sendStr = (char*)malloc(1024 * sizeof(char));
name = (char*)malloc(128 * sizeof(char));

receLen = getSerializedName(rece, "TestingName");

dataToHex(rece, sendStr, receLen );

lrs_set_send_buffer("socket0", sendStr , strlen(sendStr) );
lrs_send("socket0", "buf0", LrsLastArg);

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

lrs_get_last_received_buffer("socket0", &rece, &receLen);

getParsedName(name, rece, receLen);

lr_output_message("name from sever is %s ",name);

free(rece);
free(sendStr);
free(name);

lrs_close_socket("socket0");

return 0;
}

REFERENCE

https://github.com/google/protobuf/blob/master/cmake/README.md
http://www.cnblogs.com/tangxin-blog/p/5698137.html
http://blog.csdn.net/jiang1986829/article/details/48467269