一、Redis数据库简介
Redis(REmote DIctionary Server)是一个高性能的key-value数据库。
Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
Redis的使用,请参考如下网址:
https://www.runoob.com/redis/redis-tutorial.html
Redis
Redis 特点
- 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
- 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子性 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
二、Redia的基本操作
三、C语言常用API
Hiredis是Redis数据库的一个极简C客户端库,只是对Redis协议的最小支持。源码地址:https://github.com/redis/hiredis。前面已经详介绍如何安装和配置,这里就不在描述。
1、常用API
建立连接:与Redis server建立连接,返回一个redisContext结构指针
redisContext *redisConnect(const char *ip, int port);
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv)
发送命令:向Redis server发送命令;成功:返回redisReply结构指针,失败:返回NULL
void *redisCommand(redisContext *c, const char *format, ...); // 同步执行redis命令,和printf()用法类似 // argc:argv数组元素个数;argv:参数数组(指针数组);argvlen:数组首地址,每个元素是argv数组中相应参数的长度。 // 传入命令是字符串形式时,argvlen可以指定为NULL,这个时候使用strlen()计算argv中每个字符串长度;传入命令是二进制形式时,argvlen必须指定,用于指示argv中每个元素的长度void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); 使用hiredis连接带密码的redis服务 c = redisConnect((char*)redis_host, redis_port); if (c->err) { /* Error flags, 0 when there is no error */ printf("连接Redis失败: %s\n", c->errstr); exit(1); } else { printf("连接Redis成功!\n"); } reply = (redisReply *)redisCommand(c, "AUTH %s", redis_password); if (reply->type == REDIS_REPLY_ERROR) { printf("Redis认证失败!\n"); } else { printf("Redis认证成功!\n"); } freeReplyObject(reply);
释放redisReply结构指针
void freeReplyObject(void *reply);
释放redisContext结构指针
void redisFree(redisContext *c);
2、主要数据结构
redisContext:管理连接上下文的结构体,由redisConnect()函数创建并返回。
//* Context for a connection to Redis */ typedef struct redisContext { int err; /* Error flags, 0 when there is no error */ char errstr[128]; /* String representation of error when applicable */ int fd; int flags; char *obuf; /* Write buffer */ redisReader *reader; /* Protocol reader */ enum redisConnectionType connection_type; struct timeval *timeout; struct { char *host; char *source_addr; int port; } tcp; struct { char *path; } unix_sock; /* For non-blocking connect */ struct sockadr *saddr; size_t addrlen; } redisContext;
redisReply:响应结构体,由redisCommand()函数创建并返回。
/* This is the reply object returned by redisCommand() */ typedef struct redisReply { int type; /* REDIS_REPLY_* */ long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ size_t len; /* Length of string */ char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */ size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ } redisReply;
3、Redis二进制操作(安全操作)
参考资料:https://github.com/redis/hiredis#sending-commands-contd ```bash int toredis(int runmode, redisContext conn_redis,char ReidsKey,unsigned char* sendbuf,int SendBufElemnets)//数组传给函数作参数后,在函数中运算不包含数组长度 { if (runmode)
printf("Toredis...\n"); /* Set a key using binary safe API */
char* RedisCommand=”LPUSH”;
redisReply *reply=NULL; const char* command_argv_set[3] = {RedisCommand, ReidsKey, (char*)sendbuf};//指针数组,存放Command所使用的字节的地址; const size_t command_argv_set_len[3]={strlen(RedisCommand),strlen(ReidsKey),SendBufElemnets};// int CommandElements = sizeof(command_argv_set_len)/sizeof(size_t);//元素个数由数组长度除以每个元素的长度 if(runmode) { printf("SendBuf Elements:%d\n",SendBufElemnets); printf("CommandElements Size: %d \n",CommandElements);//打印命令中的元素个数 } //执行命令 reply = (redisReply *)redisCommandArgv(conn_redis,CommandElements, (const char**)&command_argv_set, command_argv_set_len); if (reply->type == REDIS_REPLY_ERROR) //操作失败 { if(runmode) { printf("Toredis: Message to Redis Fail!\n"); printf("Toredis: %s\n",reply->str); } freeReplyObject(reply); return -1; }
else //操作成功 {
if(runmode) { printf( "reply->type:%d\n",reply->type);//确定是list类型 printf("Toredis:"); for(int i=0;i<SendBufElemnets; i++) printf("%02x ",sendbuf[i]); printf("\n"); printf("This List has %lld elements!\n", reply->integer);//查看列表中有多少元素 }
}
freeReplyObject(reply); return 0;
}
size_t fromredis(int runmode, redisContext conn_redis,char ReidsKey,unsigned char* recvbuf,int RecvBufElements,int timeout) { if (runmode)
printf("Fromredis...\n");
redisReply reply=NULL; size_t ReceiveLen=0; char RedisCommand=”BRPOP”;//取最旧的一个数据 reply = (redisReply *)redisCommand(conn_redis,”%s %s %d”,RedisCommand,ReidsKey,timeout);//获取最下面的元素,旧的 if (runmode)
printf( "reply->type:%d\n",reply->type);
if(reply->type == REDIS_REPLY_ARRAY) //操作成功 {
ReceiveLen=reply->element[1]->len; memcpy(recvbuf,reply->element[1]->str,ReceiveLen); //把读取的消息放入recvbuf if(runmode) { printf( "reply->elements:%lu\n",reply->elements); printf( "reply->element[0]->len: %lu\n",(unsigned long)reply->element[0]->len); printf( "reply->element[0]->str: %s\n",reply->element[0]->str); printf( "reply->element[1]->len: %lu\n",ReceiveLen); printf( "reply->element[1]: "); for(int i=0;i<reply->element[1]->len;i++)//element[0]的内容是ListBit { printf("%dth:%02x ",i,recvbuf[i]); } printf("\n"); }
} else if (reply->type == REDIS_REPLY_ERROR)//操作redis失败 {
printf("Toredis: %s\n",reply->str); freeReplyObject(reply); return -1;
} freeReplyObject(reply); return ReceiveLen; }
<a name="2jAPd"></a>
# 四、C语言访问与操作Redis数据库
<a name="9migN"></a>
##### Hiredis源码中提供的一个例子,包含了常用API和Redis的基本操作
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hiredis.h> //这个头文件一般是#include <hiredis/hiredis.h> 前面已经说明了
int main(int argc, char **argv)
{
unsigned int j, isunix = 0;
redisContext *c;
redisReply *reply;
const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1";
if (argc > 2) {
if (*argv[2] == 'u' || *argv[2] == 'U') {
isunix = 1;
/* in this case, host is the path to the unix socket */
printf("Will connect to unix socket @%s\n", hostname);
}
}
int port = (argc > 2) ? atoi(argv[2]) : 6379;
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
if (isunix) {
c = redisConnectUnixWithTimeout(hostname, timeout);
} else {
c = redisConnectWithTimeout(hostname, port, timeout);
}
if (c == NULL || c->err) {
if (c) {
printf("Connection error: %s\n", c->errstr);
redisFree(c);
} else {
printf("Connection error: can't allocate redis context\n");
}
exit(1);
}
/* PING server */
reply = redisCommand(c,"PING");
printf("PING: %s\n", reply->str);
freeReplyObject(reply);
/* Set a key */
reply = redisCommand(c,"SET %s %s", "foo", "hello world");
printf("SET: %s\n", reply->str);
freeReplyObject(reply);
/* Set a key using binary safe API */
reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5);
printf("SET (binary API): %s\n", reply->str);
freeReplyObject(reply);
/* Try a GET and two INCR */
reply = redisCommand(c,"GET foo");
printf("GET foo: %s\n", reply->str);
freeReplyObject(reply);
reply = redisCommand(c,"INCR counter");
printf("INCR counter: %lld\n", reply->integer);
freeReplyObject(reply);
/* again ... */
reply = redisCommand(c,"INCR counter");
printf("INCR counter: %lld\n", reply->integer);
freeReplyObject(reply);
/* Create a list of numbers, from 0 to 9 */
reply = redisCommand(c,"DEL mylist");
freeReplyObject(reply);
for (j = 0; j < 10; j++) {
char buf[64];
sprintf(buf,64,"%u",j);
reply = redisCommand(c,"LPUSH mylist element-%s", buf);
freeReplyObject(reply);
}
/* Let's check what we have inside the list */
reply = redisCommand(c,"LRANGE mylist 0 -1");
if (reply->type == REDIS_REPLY_ARRAY) {
for (j = 0; j < reply->elements; j++) {
printf("%u) %s\n", j, reply->element[j]->str);
}
}
freeReplyObject(reply);//释放返回结构体
/* Disconnects and frees the context */
redisFree(c);//释放redis连接
return 0;
}