本文记录的是在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.h
和MsgName.cpp
。
构建dll工程
建立工程并包含依赖文件
在Visual Studio中构建Win32工程,并设置为DLL类型。
完成工程建立之后,在工程中导入protobuf相关的文件,更新附加包含目录。导入之前生成的MsgName.h
和MsgName.cpp
。
接口编写
新建一组h和cpp文件,如pb.h
和pb.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>
int constructMessage(char* des , short nType, int nIndex, const std::string &dataStr);
extern "C" DLL_API int getSerializedName(char* des, char* 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 ) {
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.set_name(name);
std::string s_data; msgname.SerializeToString(&s_data);
int len = constructMessage(des, 0, 0, s_data); return len; }
DLL_API int getParsedName(char* name, char* src, int len) {
int offset = sizeof(short) + sizeof(int);
if(len<=offset) return -1;
std::string data; data.assign(src + offset , len - offset);
MsgName msgname; if (!msgname.ParseFromString(data)) return -1;
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