zoukankan      html  css  js  c++  java
  • redis客户端连接到服务器的步骤

    和大多数客户端连接到服务器一样,redis-cli连接到服务器也主要分为两个阶段,请求连接阶段和数据传送阶段。具体来讲redis-cli做的事情有:

    1、以socket方式建立连接;

    2,选择相应的数据库;

    3,对客户端发送的命令进行编码;

    4,发送客户端编码的数据(write);

    5,接收服务器回应的数据(read);

    6,解析接收的数据。

    以下根据源码对客户端所做的事情进行分析。

    /* Start interactive mode when no command is provided */
    if (argc == 0 && !config.eval) {
    /* Ignore SIGPIPE in interactive mode to force a reconnect */
    signal(SIGPIPE, SIG_IGN);

    /* Note that in repl mode we don't abort on connection error.
    * A new attempt will be performed for every command send. */
    cliConnect(0);
    repl();
    }
    以上代码为redis-cli主函数中关于交互模式的部分,主要由cliConnect和repl构成,其中前者负责连接服务器,后者负责进行数据传输。

    建立连接
    关于客户端与服务器建立连接,我们知道主要的步骤分为、

    1,创建socket(有一套固定的模式);

    2,根据设定的ip及端口号,与服务器进行connet;

    好的,这本来就是传统的做法,咱们看看redis它怎么做了呢?

    它首先出定义了一个redis上下文结构,包含一次请求及回应的数据信息以及状态信息如下:

    typedef struct redisContext {
    int err;
    char errstr[128];
    int fd;
    int flags;
    char *obuf;
    redisReader *reader;
    enum redisConnectionType connection_type;
    struct timeval *timeout;
    struct {
    char *host;
    char *source_addr;
    int port;
    } tcp;

    struct {
    char *path;
    } unix_sock;
    } redisContext;
    err、errstr为接收数据不正常时定义的变量,fd为客户端创建的sockfd,obuf为客户端的编码命令,reader为服务器返回的数据。并且要通过cliConnect进行初始化,定义tcp,fd等信息。

    先看看cliConnect的操作

    * Connect to the server. It is possible to pass certain flags to the function:
    * CC_FORCE: The connection is performed even if there is already
    * a connected socket.
    * CC_QUIET: Don't print errors if connection fails. */
    static int cliConnect(int flags) {
    if (context == NULL || flags & CC_FORCE) {
    if (context != NULL) {
    redisFree(context);
    }

    if (config.hostsocket == NULL) {
    context = redisConnect(config.hostip,config.hostport);
    } else {
    context = redisConnectUnix(config.hostsocket);
    }
    ...

    anetKeepAlive(NULL, context->fd, REDIS_CLI_KEEPALIVE_INTERVAL);

    /* Do AUTH and select the right DB. */
    if (cliAuth() != REDIS_OK)
    return REDIS_ERR;
    if (cliSelect() != REDIS_OK)
    return REDIS_ERR;
    }
    return REDIS_OK;
    }
    flags用于表示客户端在已经连接到了服务器的情况下,是否还能在连接,0表示不允许连接,1表示允许连接(在改变客户端登录的服务器时会用到)。

    redisConnect定义了context的ip,port;调用redisContextInit初始化字符串,调用redisContextConnectTcp(c,ip,port,NULL)完成了socket连接。

    由于已经连接完成,cliSelect调用命令

    reply = redisCommand(context,"SELECT %d",config.dbnum);
    选0号数据库(默认)。

    数据传输
    在repl()中,使用了linenoise工具处理输入的行,字符串分割部分采用了cliSplitArgs,参数处理部分采用issueCommandRepeat。它调用redisAppendCommandArgv将参数编码,传递给context的obu;调用cliReadReply负责解码客户端接收到的数据;调用redisGetReply(在cliReadReply中)负责底层I/O数据的传输。

    int redisGetReply(redisContext *c, void **reply) {
    int wdone = 0;
    void *aux = NULL;

    /* Try to read pending replies */
    if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
    return REDIS_ERR;

    /* For the blocking context, flush output buffer and read reply */
    if (aux == NULL && c->flags & REDIS_BLOCK) {
    /* Write until done */
    do {
    if (redisBufferWrite(c,&wdone) == REDIS_ERR)
    return REDIS_ERR;
    } while (!wdone);

    /* Read until there is a reply */
    do {
    if (redisBufferRead(c) == REDIS_ERR)
    return REDIS_ERR;
    if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
    return REDIS_ERR;
    } while (aux == NULL);
    }

    /* Set reply object */
    if (reply != NULL) *reply = aux;
    return REDIS_OK;
    }
    主要是redisBufferWrite和redisBufferRead,分别向I/Obuf中写入和读取数据。redisGetReplyFromReader负责解码接收数据。

    总结:
    1,redis-cli在基本的客户端编程的基础上,增加了Context定义,可以知道数据的类型,数据的好坏。

    2,增加了编码解码功能,一般编码解码功能可以使数据更安全,不知是不是因为这样才进行编码解码。

    3,具体的实现部分还是需要进一步学习。

     
    --------------------- 

  • 相关阅读:
    Redis
    cut
    grep
    MySQL中EXPLAIN的解释
    MySQL数据类型
    有用的MySQL语句
    mysql函数
    memcache
    存储过程 游标的使用
    存储过程批量删除
  • 原文地址:https://www.cnblogs.com/ly570/p/10961763.html
Copyright © 2011-2022 走看看