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,具體的實現部分還是需要進一步學習。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM