和大多数客户端连接到服务器一样,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客户端连接到服务器的步骤的更多相关文章

  1. 使用redis客户端连接windows和linux下的redis并解决无法连接redis的问题

    搭建环境:linux是centos7.4(请注意centos7以下版本的防火墙跟centos7以上的不同,使用redis客户端连接redis时会有区别,建议使用centos7以上版本) 一.下载red ...

  2. Redis客户端连接以及持久化数据(三)

    0.Redis目录结构 1)Redis介绍及部署在CentOS7上(一) 2)Redis指令与数据结构(二) 3)Redis客户端连接以及持久化数据(三) 4)Redis高可用之主从复制实践(四) 5 ...

  3. C#两大知名Redis客户端连接哨兵集群的姿势

    前言 前面利用<Docker-Compose搭建Redis高可用哨兵集群>, 我们的思路是将Redis.Sentinel.Redis Client App链接到同一个网桥网络,这个网桥内的 ...

  4. redis客户端连接异常

    本文参考:http://mdba.cn/2015/04/02/redistwemproxy-%e5%ae%a2%e6%88%b7%e7%ab%af%e8%bf%9e%e6%8e%a5%e5%bc%82 ...

  5. Redis客户端连接

    Redis接受上配置监听TCP端口和Unix套接字客户端的连接,如果启用.当一个新的客户端连接被接受,如有以下操作进行: 客户端套接字置于非阻塞状态,因为Redis的使用复用和非阻塞I/O操作. TC ...

  6. Redis客户端连接池

    使用场景 对于一些大对象,或者初始化过程较长的可复用的对象,我们如果每次都new对象出来,那么意味着会耗费大量的时间. 我们可以将这些对象缓存起来,当接口调用完毕后,不是销毁对象,当下次使用的时候,直 ...

  7. Redis 客户端连接

      Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作: 首先,客户端 socket 会被设置为非阻 ...

  8. 使用 WebSocket 客户端连接 MQTT 服务器

    简介 近年来随着 Web 前端的快速发展,浏览器新特性层出不穷,越来越多的应用可以在浏览器端或通过浏览器渲染引擎实现,Web 应用的即时通信方式 WebSocket 得到了广泛的应用. WebSock ...

  9. ESA2GJK1DH1K升级篇: 远程升级准备工作: 使用TCP客户端连接Web服务器实现http下载数据

    一,根目录建一个文件 二,使用浏览器访问 http://47.92.31.46:80/1.txt     或者  http://47.92.31.46/1.txt 三,使用TCP客户端访问文件内容 3 ...

随机推荐

  1. hdu_1013_Digital Roots_201310121652

    Digital Roots Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...

  2. MyBatis 3实现时间段精确的查询(转)

    效果如下: 说明: 时间范围的查询会存在以下问题: 1.如果单纯采用年月日的形式会出现缺少最后一点的数据,比如要查询2015-09-16到2015-09-17,那么2015-09-17 01:00:0 ...

  3. WGS84坐标系图层转火星坐标系(是整个图层转哟,不是转单点坐标)

    开篇唠叨(着急的略过) 大天朝“火星坐标系”小科普:是一种国家保密插件,对真实坐标系统进行人为的加偏处理,将真实的坐标加密成虚假的坐标,加密后的坐标被称为火星坐标系统.所有的电子地图所有的导航设备,都 ...

  4. linux面试之--堆、栈、自由存储区、全局/静态存储区和常量存储区

    栈,就是那些由编译器在须要的时候分配,在不须要的时候自己主动清除的变量的存储区.里面的变量一般是局部变量.函数參数等.在一个进程中.位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数的调用.和堆 ...

  5. selenium清空默认文字

    默认输入框 鼠标点击上去还有文案 直接用 clear不可以

  6. light oj1074

    Description The people of Mohammadpur have decided to paint each of their houses red, green, or blue ...

  7. C++组合通信

    #include <iostream> #include<vector> #include<string> using namespace std; class A ...

  8. Android实战简易教程-第四十一枪(显示倒计时的Button-适用于获取验证码)

    近期在做获取验证码的功能.考虑到优良的用户体验,决定制作一个拥有倒计时提示的Button按钮,在网上查了一些资料,非常是简单的就能实现.我写了一个小Demo,大家能够应用到自己的项目中. 一.代码 1 ...

  9. C#备份及还原数据库的实现代码(粗略) // 利用C#还原数据库(SQL SERVER)备份文件到指定路径

    C#数据库备份及还原 1.在用户的配置时,我们需要列出当前局域网内所有的数据库服务器,并且要列出指定服务器的所有数据库,实现代码如下: 取得数据库服务器列表: public ArrayList Get ...

  10. rpmbuild

    rpm2cpio xxx.rpm | cpio -div