项目中,有多台机器频繁读写、同步一些参数。起初的方案是通过MySQL的临时表实现,对效率有一些影响,故改为redis方案。项目中redis和web、mysql是linux平台,客户端是windows 7平台C++应用进程、操作LoRa等硬件设备。
按网络上多篇博文的描述,C++环境下访问redis需要使用hiredis库。从下载的redis3.0源代码中发现,hiredis已集成在了项目中,如下是redis3.0的目录结构:
├─deps
├─msvs
│ ├─hiredis
│ ├─lua
│ ├ ……
│ ├─Samples
│ ├─setups
│ └─tools
├─src
│ └─Win32_Interop
├─tests
└─utils
其中,msvc目录下有个hiredis.vcxproj文件,src目录下有Win32_Interop.vcxproj文件,这两个是C++客户端需要调用的库文件。
注意,这两个.vcxproj工程是vc2012版本的,源码使用了一些Cxx11的语法,如自动推导、变长参数模板、常量成员赋值等,因此,需要对源代码做一些小改动。
添加到解决方案中,然后,在“属性->平台属性->常规”中将“平台工具集”由v120变更为v100,“配置类型”为“静态库(.lib)”;
编译,报告大量的编译错误。别紧张,这些错误其实都有共性,用vim正则表达式替换下就好。
Win32_CommandLine.cpp中,有如下代码:
static RedisParamterMapper g_redisArgMap = {...};
解决:
写个loader类,在其构造函数中对该变量赋值,然后,定义一个全局的loader变量,例如;
class g_redisArgMap_loader {
public:
g_redisArgMap_loader()
{
// QFork flags
g_redisArgMap.insert(std::pair(cQFork, &fp2)); // qfork [QForkControlMemoryMap handle] [parent process id]
...
}
...
};
static g_redisArgMap_loader g_redisArgMap_loader_object = g_redisArgMap_loader();
for (auto p : sentinelSubCommands) {...}
解决:
for (vector::iterator p = sentinelSubCommands.begin(); p != sentinelSubCommands.end(); p++) {...}
这个解决起来也不麻烦,在win32_variadicfunctor.h中注释掉dllfunctor_stdcall的定义,然后,由vim写正则式,将代码作如下的替换:
原始代码:
auto f_WSACleanup = dllfunctor_stdcall("ws2_32.dll", "WSACleanup");
替换后的代码:
int (WSAAPI * f_WSACleanup)( void ) = (int(\_\_stdcall * )())DLLMap::getInstance().getProcAddress("ws2\_32.dll", "WSACleanup");
vim正则式:
:%s/auto\s*\(\w\+\)\s* = dllfunctor_\w\+<\([^>,]\+\),*\s*\([^>]\+\)>/\2 (WSAAPI * \1)(\3) = (\2(__stdcall *)(\3))DLLMap::getInstance().getProcAddress/gc
其它小问题,自行解决即可。
编译成功,生成Win32_Interop.lib文件。
添加到解决方案中,在“属性->平台属性->常规”中将“平台工具集”由v120变更为v100,“配置类型”变更为“静态库(.lib)”;
在“附加包含目录”中添加“$(ProjectDir)…\src\Win32_Interop”;
编译,报告找不到winapifamily.h文件。
如前所述,redis3.0中的hiredis.vcxproj是vc2012以上版本支持windows 8.x的,而winapifamily.h是WDK 8(Windows driver Kit)中的头文件。这里,我们需要对源代码做一些小改动。
在文件“redis-3.0\src\win32_interop\ws2tcpip.h”中,添加如下代码:
#define PICEA_12345_INCLUDED 1
#if (PICEA_12345_INCLUDED)
#ifndef _Outptr_ // SAL2.0中的宏,vc2010的SAL1.0中未定义
#define _Outptr_
#endif
#ifndef _In_reads_bytes_ // SAL2.0中的宏,vc2010的SAL1.0中未定义,写个假的
#define _In_reads_bytes_(a)
#endif
#ifndef _Out_writes_opt_ // SAL2.0中的宏,vc2010的SAL1.0中未定义,写个假的
#define _Out_writes_opt_(a)
#endif
#endif
#if (PICEA_12345_INCLUDED || WINVER <= _WIN32_WINNT_WS03)
#include "win32_winapifamily.h"
#else
#include
#endif
编译成功,生成hiredis.lib文件。
这些库都是.c文件。
若出现“宏定义”错误,在文件开头添加#include
若出现变量声明错误,是因为vc2010的编译器不允许c函数语句中间有变量的声明,例如:
void f(void) {
if (...) { /*statement*/ ... }
int i; //这里vc2010会报告错误
i=0;
}
解决方法: 将变量声明在函数开头即可
特别地,Cxx11/VC2013中才有的isnan和isinf等函数,在网上自己找一个,添加到工程中就可以了(我是写了个vc2010-porting.cpp文件,添加到工程中)。
这几个工程是RedisServer不依赖的,编译不编译没有影响。
处理方法同1.3。
对源代码的改动同1.1和1.3,并添加vc2010-porting.cpp处理isnan等函数。
然后,编译通过。
按F5调试,出现redis启动窗口
新建一个win32项目,然后,右键项目名,属性->配置属性->VC++ 目录->包含目录,将hiredis的路径加入进去
$(SolutionDir)redis-3.0\deps\hiredis
右键项目名->属性->配置属性->VC++目录->库目录,将hiredis.lib和Win32_Interop.lib的路径加入进去
( S o l u t i o n D i r ) (SolutionDir) (SolutionDir)(Configuration)\
右键项目名->属性->链接器->输入->附加依赖项,添加hiredis.lib、Win32_Interop和ws2_32.lib
#include
#include
#include
#include
extern "C"
{
//#include
}
#include
#include
#define REDIS_SERVER_IP "192.168.x.x"
#define REDIS_SERVER_PORT 6379
#define REDIS_SERVER_PASS "xxxxxx"
#pragma comment(lib, "ws2_32.lib")
using namespace std;
void redis_test_proc()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 1), &wsaData);
redisContext * redis_ctx_;
redisReply *reply;
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
redis_ctx_ = redisConnectWithTimeout((char*)REDIS_SERVER_IP, REDIS_SERVER_PORT, timeout);
if (NULL != redis_ctx_ && redis_ctx_->err) {
printf("Connection error: %s\n", redis_ctx_->errstr);
exit(1);
}
reply = (redisReply *)redisCommand(redis_ctx_, "auth "REDIS_SERVER_PASS);
if (REDIS_REPLY_ERROR == reply->type) {
printf("Authentication failed: %s\n", reply->str);
}
freeReplyObject(reply);
/* Set a key */
reply = (redisReply *)redisCommand(redis_ctx_, "SET %s %s", "foo", "kivifriut is delicious ...");
if (REDIS_REPLY_ERROR == reply->type) {
printf("SET failed (%s:%d): %s\n", __FUNCTION__, __LINE__, reply->str);
// TODO: 延迟x秒重连
} else {
printf("SET foo: %s\n", reply->str);
}
freeReplyObject(reply);
/* Try a GET and two INCR */
reply = (redisReply *)redisCommand(redis_ctx_, "GET foo");
printf("GET foo: %s\n", reply->str);
freeReplyObject(reply);
redisFree(redis_ctx_);
}
int main(int argc, _TCHAR* argv[])
{
redis_test_proc();
system("pause");
return 0;
}
SET foo: OK
GET foo: kivifriut is delicious ...
请按任意键继续. . .
源代码:
https://download.csdn.net/download/hylaking/10729376