一、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结构指针

    1. redisContext *redisConnect(const char *ip, int port);
    2. 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;
}