项目中,有些数据由于环境限制无法实时传输,因此需要将这些数据保存下来,等待条件允许时再传输。redis数据库是一个key-value型的数据库,并且其数据实时保存在内存中,读写速度快,还可以自行配置将内存的数据写入到存储介质的时间间隔,满足实际使用要求。
由于项目采用的是TI am57xx芯片,因此,需要将redis源码交叉编译移植到板卡上。redis源码交叉编译与其他第三库移植,同样遵循make,make install两个步骤。redis源码采用的是redis-6.2.6.tar.bz2。这个编译redis不需要configure配置。
redis交叉编译移植
解压,执行下面语句即可。
make MALLOC=libc CC=arm-linux-gnueabihf-gcc
编译后如下所示:
在redis/src路径下,将会生成redis-server, redis-client 等可执行文件
其中,redis.conf在redis的源码目录下,读者需要将这些文件拷贝到板卡的/bin目录下,将redis.conf拷贝到/etc目录下即可。
然后在板卡上启动redis服务:
redis-server /etc/redis.conf &
启动redis客户端:
redis-client
redis的命令可以参考链接http://doc.redisfans.com/
redis的配置文件redis.conf
这里仅介绍常用的一些配置项:
60s至少改动一处即可保存(这里的保存是指真正写入存储介质,而不是停留在内存区)
配置数据库默认名称为dump.rdb
配置默认工作路径:/meida/mmc1p3
配置数据库默认容量:100MB
配置数据库的保存算法:
以上是此次项目中的常用配置,其他配置项读者可以自行查阅官方文档。
redis.conf更新后,需要将redis.conf重新拷贝到/etc目录下。
redis的c++封装
这里所说的c++封装,是指作者出于项目的需要,额外重新封装一些接口,内部依旧调用redis的c接口。redis源码自带了redis的c接口,因此,第一需要将这些c接口分离出来。这些c接口函数位于redis/deps/hiredis/目录下:
先建立一个redis_test目录,在redis_test路径下,再建立include以及src两个子目录;
将hiredis目录下的头文件拷贝到redis_test/include下:
将hiredis目录下的源文件拷贝到redis_test/src下:
注意:example.c、example.cpp以及build_redis_test.sh是自行编写的,后面会提到。拷贝过来的是redis的c接口函数,无需改动。
编写Redis类,Redis类根际实际使用对外只开放7个接口,分别为判断is_key_exists,设置单个key-valu值set_single_key,获取单个key-value值get_single_key,获取单个key-value并删除getdel_single_key,设置多个key-value值mset_multi_key,获取多个key-value值mget_multi_key,删除多个key值del_list。
每个接口内部,实际调用的依旧是hiredis的c接口,之所以封装,是为了隐去hiredis的实现步骤,读者可专注于使用逻辑。
完整的Redis类实现代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hiredis.h>
#include <win32.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include <list>
using namespace std;
#define VALUE_BUFF_SIZE (512 * 20)
struct st_key_val
{
string key;
uint8_t buff[VALUE_BUFF_SIZE];
size_t buff_len;
};
typedef list<struct st_key_val> ST_KEY_VAL_LIST;
typedef list<string> ST_KEY_LIST;
class Redis
{
private:
string hostname;
int hostport;
redisContext *context;
struct timeval timeout;
int hex2char(uint8_t *buff, int32_t len, string *out_str);
int char2hex(char *in_str, int32_t in_str_len, uint8_t *out, int32_t out_len);
public:
long long is_key_exists(const string key); /* is_key_exists ? */
int set_single_key(const string key, uint8_t *buff, int32_t len); /* Set single key--value */
int get_single_key(const string key, uint8_t *buff, int32_t len); /* Get single key--value */
int getdel_single_key(const string key, uint8_t *buff, int32_t len); /* Get single key--value and delete it */
int mset_multi_key(ST_KEY_VAL_LIST key_val_list); /* Set multi key--value */
//int mget_multi_key(const string key_list[], int32_t key_num, ST_KEY_VAL_LIST *val_list); /* Get multi key--value */
int mget_multi_key(ST_KEY_LIST key_list, ST_KEY_VAL_LIST *val_list); /* Get multi key--value */
//int del_list(const string key_list[], const int32_t key_num); /* Del a list */
int del_list(ST_KEY_LIST del_key_list);
public:
Redis(string name = "127.0.0.1", int port = 6379, int32_t sec = 1, int32_t microsec = 500000)
{
redisReply *reply;
hostname = name;
hostport = port;
timeout = {sec, microsec};
context = redisConnectWithTimeout(hostname.c_str(), port, timeout);
if ((context == NULL) || (context->err))
{
if (context)
{
printf("Connection error: %s\n", context->errstr);
redisFree(context);
}
else
{
printf("Connection error: can't allocate redis context\n");
}
exit(1);
}
/* PING server */
reply = (redisReply *)redisCommand(context,"PING");
printf("PING: %s\n", reply->str);
freeReplyObject(reply);
}
};
long long Redis::is_key_exists(const string key)
{
redisReply *reply = NULL;
long long integer = 0;
const char *c_key = key.c_str();
reply = (redisReply *)redisCommand(context,"EXISTS %s", c_key);
integer = reply->integer;
freeReplyObject(reply);
return integer;
}
int Redis::set_single_key(const string key, uint8_t *buff, int32_t len)
{
int32_t i = 0;
redisReply *reply;
const char *c_key = key.c_str();
string s_val;
char aa[8];
//for (i = 0; i < len; i++)
//{
// sprintf(aa, "%.2x", (uint8_t)buff[i]);
// s_val += aa;
//}
hex2char(buff, len, &s_val);
reply = (redisReply *)redisCommand(context,"SET %s %s", c_key, s_val.c_str());
printf("SET: %s\n", reply->str);
freeReplyObject(reply);
return 1;
}
int Redis::get_single_key(const string key, uint8_t *buff, int32_t len)
{
redisReply *reply = NULL;
char recv[len] = {'\0'};
int32_t recv_len = 0;
const char *c_key = key.c_str();
memset(recv, 0x00, sizeof(recv));
if (is_key_exists(key) == 1)
{
reply = (redisReply *)redisCommand(context,"GET %s", c_key);
if (reply != NULL)
{
strcpy(recv, reply->str);
recv_len = char2hex(recv, reply->len, buff, len);
freeReplyObject(reply);
}
}
return recv_len;
}
int Redis::getdel_single_key(const string key, uint8_t *buff, int32_t len)
{
redisReply *reply = NULL;
char recv[len] = {'\0'};
int32_t recv_len = 0;
const char *c_key = key.c_str();
memset(recv, 0x00, sizeof(recv));
if (is_key_exists(key) == 1)
{
reply = (redisReply *)redisCommand(context,"GETDEL %s", c_key);
if (reply != NULL)
{
strcpy(recv, reply->str);
recv_len = char2hex(recv, reply->len, buff, len);
freeReplyObject(reply);
}
}
return recv_len;
}
int Redis::mset_multi_key(ST_KEY_VAL_LIST key_val_list)
{
redisReply *reply;
ST_KEY_VAL_LIST::iterator st_key_val;
string mset_str = "MSET";
for (st_key_val = key_val_list.begin(); st_key_val != key_val_list.end(); st_key_val++)
{
mset_str.append(" ");
mset_str.append(st_key_val->key);
mset_str.append(" ");
string s_val;
hex2char(st_key_val->buff, st_key_val->buff_len, &s_val);
mset_str.append(s_val);
}
reply = (redisReply *)redisCommand(context, mset_str.c_str(), NULL);
//printf("SET: %s\n", reply->str);
freeReplyObject(reply);
}
#if 0
int Redis::mget_multi_key(const string key_list[], const int32_t key_num, ST_KEY_VAL_LIST *val_list)
{
redisReply *reply;
int32_t i = 0;
struct st_key_val val;
int32_t val_cn = 0;
string mget_str = "MGET";
const char *c_mget_str = NULL;
for (i = 0; i < key_num; i++)
{
mget_str.append(" ");
mget_str.append(key_list[i]);
}
c_mget_str = mget_str.c_str();
reply = (redisReply *)redisCommand(context, c_mget_str, NULL);
if (reply != NULL)
{
if (reply->elements > 0)
{
//printf("reply->elements = %d\n", reply->elements);
for (i = 0; i < reply->elements; i++)
{
val.key = key_list[i];
val.buff_len = char2hex(reply->element[i]->str, reply->element[i]->len, val.buff, VALUE_BUFF_SIZE);
val_list->push_back(val);
val_cn++;
}
}
}
freeReplyObject(reply);
return val_cn;
}
#endif
int Redis::mget_multi_key(ST_KEY_LIST key_list, ST_KEY_VAL_LIST *val_list)
{
redisReply *reply;
int32_t i = 0;
struct st_key_val val;
int32_t val_cn = 0;
string mget_str = "MGET";
const char *c_mget_str = NULL;
ST_KEY_LIST::iterator mget_key;
for (mget_key = key_list.begin(); mget_key != key_list.end(); mget_key++)
{
mget_str.append(" ");
mget_str.append(*mget_key);
}
c_mget_str = mget_str.c_str();
reply = (redisReply *)redisCommand(context, c_mget_str, NULL);
if (reply != NULL)
{
if (reply->elements > 0)
{
//printf("reply->elements = %d\n", reply->elements);
mget_key = key_list.begin();
for (i = 0; i < reply->elements; i++)
{
val.key = *mget_key;
val.buff_len = char2hex(reply->element[i]->str, reply->element[i]->len, val.buff, VALUE_BUFF_SIZE);
val_list->push_back(val);
val_cn++;
mget_key++;
}
}
}
freeReplyObject(reply);
return val_cn;
}
#if 0
int Redis::del_list(const string key_list[], const int32_t key_num)
{
redisReply *reply;
int32_t i = 0;
const char *c_list_name;
string del_str = "DEL";
for (i = 0; i < key_num; i++)
{
if (is_key_exists(key_list[i]))
{
del_str.append(" ");
del_str.append(key_list[i]);
}
}
c_list_name = del_str.c_str();
printf("c_list_name = %s\n", c_list_name);
reply = (redisReply *)redisCommand(context, c_list_name, NULL);
printf("reply->integer = %lld\n", reply->integer);
freeReplyObject(reply);
return 0;
}
#endif
int Redis::del_list(ST_KEY_LIST del_key_list)
{
long long ret = 0;
redisReply *reply;
const char *c_list_name;
string del_str = "DEL";
ST_KEY_LIST::iterator del_key;
for (del_key = del_key_list.begin(); del_key != del_key_list.end(); del_key++)
{
if (is_key_exists(*del_key))
{
del_str.append(" ");
del_str.append(*del_key);
}
}
c_list_name = del_str.c_str();
//printf("c_list_name = %s\n", c_list_name);
reply = (redisReply *)redisCommand(context, c_list_name, NULL);
//printf("reply->integer = %lld\n", reply->integer);
ret = reply->integer;
freeReplyObject(reply);
return ret;
}
int Redis::hex2char(uint8_t *buff, int32_t len, string *out_str)
{
//uint8_t tmp = 0x00;
//int32_t i = 0;
//int32_t j = 0;
//for (i = 0; i < len; i++)
//{
// for (j = 0; j < 2; j++)
// {
// tmp = (*(buff + i) >> 4) * (1 - j) + (*(buff + i) & 0x0f) * j;
// if ((tmp >= 0) && (tmp <= 9))
// {
// val[2 * i + j] = tmp + '0';
// }
// else if ((tmp >= 0x0A) && (tmp <= 0x0F))
// {
// val[2 * i + j] = tmp - 0x0A + 'A';
// }
// }
//}
//string s;
//char aa[8];
//for(uint16_t i = 0; i < len; i++)
//{
// sprintf(aa, "%.2x", (uint8_t)buff[i]);
// s += aa;
//}
char aa[8];
for(uint16_t i = 0; i < len; i++)
{
sprintf(aa, "%.2x", (uint8_t)buff[i]);
out_str->append(aa);
}
//printf("s = %s\n", s.c_str());
return 0;
}
int Redis::char2hex(char *in_str, int32_t in_str_len, uint8_t *out, int32_t out_len)
{
char *p = in_str;
uint8_t high = 0;
uint8_t low = 0;
int cnt = 0;
int32_t ret_len = 0;
while (cnt < in_str_len/2)
{
high = ((*p > '9') && ((*p <= 'F') || (*p <= 'f'))) ? *p - 48 - 7 : *p - 48;
low = (*(++p) > '9' && ((*p <= 'F') || (*p <= 'f'))) ? *p - 48 - 7 : *p - 48;
out[cnt] = (high & 0x0f) << 4 | (low & 0x0f);
p++;
cnt++;
}
if (in_str_len % 2 != 0)
{
out[cnt] = ((*p > '9') && ((*p <= 'F') || (*p <= 'f'))) ? *p - 48 - 7 : *p - 48;
}
ret_len = in_str_len / 2 + in_str_len % 2;
return ret_len;
}
int main(int argc, char *argv[])
{
Redis xz_redis;
uint8_t data[] = {0x01, 0x11, 0xa0, 0xff, 0x0f, 0x90};
uint8_t recv[1024];;
int32_t recv_len = 0;
int32_t i = 0;
string del_key[] = {"xu"};
char val[8];
ST_KEY_VAL_LIST val_list;
string mget_key[] = {"yc1", "yc2", "yc4", "yc3", "yc7", "yc6", "yc5"};
ST_KEY_VAL_LIST::iterator st_val;
int32_t st_val_cn = 0;
ST_KEY_VAL_LIST mset_list;
struct st_key_val mset_val;
mset_val.key = "yc5";
mset_val.buff[0] = 0x58;
mset_val.buff[1] = 0x58;
mset_val.buff[2] = 0x58;
mset_val.buff[3] = 0x58;
mset_val.buff_len = 4;
mset_list.push_back(mset_val);
mset_val.key = "yc6";
mset_val.buff[0] = 0x61;
mset_val.buff[1] = 0x62;
mset_val.buff[2] = 0x63;
mset_val.buff[3] = 0x64;
mset_val.buff_len = 4;
mset_list.push_back(mset_val);
mset_val.key = "yc7";
mset_val.buff[0] = 0x79;
mset_val.buff[1] = 0x79;
mset_val.buff[2] = 0x79;
mset_val.buff[3] = 0x79;
mset_val.buff_len = 4;
mset_list.push_back(mset_val);
ST_KEY_LIST del_key_list;
for (i = 0; i < 7; i++)
{
del_key_list.push_back(mget_key[i]);
}
while(1)
{
xz_redis.mset_multi_key(mset_list);
st_val_cn = xz_redis.mget_multi_key(del_key_list, &val_list);
printf("st_val_cn = %d\n", st_val_cn);
for (st_val = val_list.begin(); st_val != val_list.end(); st_val++)
{
printf("st_val.key = %s\n", st_val->key.c_str());
printf("st_val.buff_len = %d\n", st_val->buff_len);
for (i = 0; i < st_val->buff_len; i++)
printf("st_val.buff[%d] = %x\n", i, st_val->buff[i]);
}
xz_redis.del_list(del_key_list);
//xz_redis.del_list(del_key, 1);
//xz_redis.set_single_key("xu", data, sizeof(data));
//recv_len = xz_redis.get_single_key("xu", recv, sizeof(recv));
//if (recv_len > 0)
//{
// printf("recv_len = %d\n", recv_len);
// for (i = 0; i < recv_len; i++)
// printf("0x%.2x ", recv[i]);
// printf("\n");
//}
//else
//{
// printf("not exists\n");
//}
break;
}
}
读者在用上述例子测试时,可以按下面的方法交互测试:
用代码写入key-value,再用redis命令读取,验证是否正确;
用命令写入key-value,再用代码读取,验证是否正确;
注意:上述代码实现的例子,写入的数据均会转成字符串写入到redis中。但因为作者实际使用的数据均为uint8_t数组,因此还做了转换函数,但转换函数不对外开放。
完整的代码实现以及redis的编译资源见链接:https://download.csdn.net/download/qq_36731830/60825216