和大多数客户端连接到服务器一样,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. 使用外部LDAP用户认证后,DJANGO用户如何作登陆的操作?

    公司的要求,使用公司的UM帐号验证密码,然后,在DJANGO里登陆. 因为没有authenticate函数,只有login函数,所以不能简单的使用用户来login,而需要加一个backend参数... ...

  2. nyoj_17_单调递增最长子序列_201403121516

    单调递增最长子序列 时间限制:3000 ms  |  内存限制:65535 KB 难度:4   描述 求一个字符串的最长递增子序列的长度如:dabdbf最长递增子序列就是abdf,长度为4   输入 ...

  3. mongodb之用户/认证/角色/权限管理

    前言 用户权限管理很重要,只给需要的权限,防止应用系统漏洞导致脱库 认证和授权 Authentication 认证识别,解决我是谁 Authorization 操作授权,我能做什么 认证机制 MONG ...

  4. JAVA 大数据

  5. linux sh 脚本调用外部命令

    参考:http://blog.csdn.net/lhb_blog/article/details/22083649 ------------------------------------------ ...

  6. MFC小程序003------MFC使用WebBrowser组件,在对话框中创建滚动视图,动态创建一个静态文本控件并设置鼠标单击的消息响应

    MFC小程序截图: 一.在MFC中简单使用WebBrowser的ActiveX插件的方法: 见博文:  http://blog.csdn.net/supermanking/article/detail ...

  7. 泛型和面向对象C++

    1. 在类内部定义的函数默觉得inline,内联函数应该在头文件里定义,由于其定义对编译器必须是可见的,以便编译器可以在调用点内联展开该函数的代码. 此时,仅有函数原型是不够的. 2.assert 3 ...

  8. Codeforces Round #276 (Div. 1) A. Bits 贪心

    A. Bits   Let's denote as  the number of bits set ('1' bits) in the binary representation of the non ...

  9. luogu3155 [CQOI2009]叶子的染色

    题目大意 给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根.内部结点和叶子均可)着以黑色或白色.你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点( ...

  10. mysql数据库操作(1)

    1.mysql 数据库备份: 语法: MYSQLDUMP -uUSER -pPASS DataBase > Path 其中:USER 是用户名,PASS 是密码,DataBase 是数据库名, ...