结合redis设计与实现的redis源码学习-18-网络连接库(networking.c)

networking.cRedis 的网络连接库,负责发送命令回复和接受命令请求, 同时也负责创建/销毁客户端, 以及通信协议分析等工作.
下面是部分使用的结构体,定义在server.h中

//命令结构体
struct redisCommand {
    char *name;
    redisCommandProc *proc;//命令处理器
    int arity;//元数
    char *sflags; /* Flags as string representation, one char per flag. 用字符串表示标志,*/
    int flags;    /* The actual flags, obtained from the 'sflags' field. 实际标志,从sflags中获得*/
    /* Use a function to determine keys arguments in a command line. Used for Redis Cluster redirect. 使用函数来确定命令行中的参数,用作Redis集群重定向*/
    redisGetKeysProc *getkeys_proc;
    /* What keys should be loaded in background when calling this command? 调用此命令时应该在后台加载那些秘钥*/
    int firstkey; /* The first argument that's a key (0 = no keys) 第一个参数是键*/
    int lastkey;  /* The last argument that's a key 最后一个参数是键*/
    int keystep;  /* The step between first and last key 中间的步数*/
    long long microseconds, calls;
};

/* With multiplexing we need to take per-client state. Clients are taken in a linked list. 在多路复用下我们需要记录每个客户端的状态,放在列表中*/
typedef struct client {
    uint64_t id;            /* Client incremental unique ID. 唯一标识id*/
    int fd;                 /* Client socket. socket描述符*/
    redisDb *db;            /* Pointer to currently SELECTed DB. 选择/指向的数据库*/
    int dictid;             /* ID of the currently SELECTed DB. 当前选择的数据库字典的id*/
    robj *name;             /* As set by CLIENT SETNAME. 对象名称*/
    sds querybuf;           /* Buffer we use to accumulate client queries. 客户端的查询环城区*/
    size_t querybuf_peak;   /* Recent (100ms or more) peak of querybuf size. 最近100ms的缓冲区大小峰值*/
    int argc;               /* Num of arguments of current command. 当前命令参数的个数*/
    robj **argv;            /* Arguments of current command. 当前命令参数的数组*/
    struct redisCommand *cmd, *lastcmd;  /* Last command executed. 最后执行的命令*/
    int reqtype;            /* Request protocol type: PROTO_REQ_* 请求的协议类型*/
    int multibulklen;       /* Number of multi bulk arguments left to read. 尧都区多个批量参数的数量*/
    long bulklen;           /* Length of bulk argument in multi bulk request. 多批量请求中批量参数的长度*/
    list *reply;            /* List of reply objects to send to the client. 返回给客户端的reply对象列表*/
    unsigned long long reply_bytes; /* Tot bytes of objects in reply list. reply对象列表的字节数*/
    size_t sentlen;         /* Amount of bytes already sent in the current buffer or object being sent. 在当前正在发送的缓冲区或对象中已经发送的字节数*/
    time_t ctime;           /* Client creation time. 客户端创建时间*/
    time_t lastinteraction; /* Time of the last interaction, used for timeout 最后一次交互的时间,用于超时*/
    time_t obuf_soft_limit_reached_time;
    int flags;              /* Client flags: CLIENT_* macros. 客户端标志*/
    int authenticated;      /* When requirepass is non-NULL. 认证号*/
    int replstate;          /* Replication state if this is a slave. 复制状态,如果这是一个从服务器*/
    int repl_put_online_on_ack; /* Install slave write handler on ACK. 安装从机写句柄在ack*/
    int repldbfd;           /* Replication DB file descriptor. 复制数据库文件描述符*/
    off_t repldboff;        /* Replication DB file offset. db文件描述符偏移量*/
    off_t repldbsize;       /* Replication DB file size. 复制的大小*/
    sds replpreamble;       /* Replication DB preamble.复制db前言 */
    long long reploff;      /* Replication offset if this is our master. 复制抵消,如果这是主机的话*/
    long long repl_ack_off; /* Replication ack offset, if this is a slave. 复制请抵消,如果这是从机*/
    long long repl_ack_time;/* Replication ack time, if this is a slave. 复制请求时间,从机*/
    long long psync_initial_offset; /* FULLRESYNC reply offset other slaves copying this slave output buffer should use. 回复其他从机复制这个从机输出缓冲区使用*/
    char replrunid[CONFIG_RUN_ID_SIZE+1]; /* Master run id if is a master. 主机的运行id*/
    int slave_listening_port; /* As configured with: REPLCONF listening-port 从机端口*/
    char slave_ip[NET_IP_STR_LEN]; /* Optionally given by REPLCONF ip-address 从机可选ip*/
    int slave_capa;         /* Slave capabilities: SLAVE_CAPA_* bitwise OR. 从属功能*/
    multiState mstate;      /* MULTI/EXEC state 事务状态*/
    int btype;              /* Type of blocking op if CLIENT_BLOCKED. 阻塞类型*/
    blockingState bpop;     /* blocking state 阻塞状态*/
    long long woff;         /* Last write global replication offset. 最后写全局复制偏移量*/
    list *watched_keys;     /* Keys WATCHED for MULTI/EXEC CAS 在事务过程中观察的key链表*/
    dict *pubsub_channels;  /* channels a client is interested in (SUBSCRIBE) 客户端订阅的通道*/
    list *pubsub_patterns;  /* patterns a client is interested in (SUBSCRIBE) 客户端订阅的模式*/
    sds peerid;             /* Cached peer ID. 缓存对等id*/

    /* Response buffer 返回缓冲区*/
    int bufpos;
    char buf[PROTO_REPLY_CHUNK_BYTES];
} client;

networking.c

#include "server.h"
#include 
#include 
static void setProtocolError(client *c, int pos);

/* Return the size consumed from the allocator, for the specified SDS string, including internal fragmentation. This function is used in order to compute the client output buffer size. 从指定的sds返回分配器消耗的大小,包括内部碎片,这个函数用来计算客户端输出缓冲区大小*/
size_t sdsZmallocSize(sds s) {
    void *sh = sdsAllocPtr(s);
    return zmalloc_size(sh);
}
/* Return the amount of memory used by the sds string at object->ptr for a string object. 返回字符串对象中sds使用的内存量*/
size_t getStringObjectSdsUsedMemory(robj *o) {
    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
    switch(o->encoding) {
    case OBJ_ENCODING_RAW: return sdsZmallocSize(o->ptr);
    case OBJ_ENCODING_EMBSTR: return zmalloc_size(o)-sizeof(robj);
    default: return 0; /* Just integer encoding for now. */
    }
}
//客户端响应值引用计数+1
void *dupClientReplyValue(void *o) {
    incrRefCount((robj*)o);
    return o;
}
//链表比较对象
int listMatchObjects(void *a, void *b) {
    return equalStringObjects(a,b);
}
//创建客户端
client *createClient(int fd) {
    client *c = zmalloc(sizeof(client));

    /* passing -1 as fd it is possible to create a non connected client. This is useful since all the commands needs to be executed in the context of a client. When commands are executed in other contexts (for instance a Lua script) we need a non connected client. 传递-1作为FD可以创建一个非连接的客户端,这是很有用的,因为所有的命令都需要在客户端的上下文中执行。挡在其他上下文中执行命令时,我们需要一个未连接的客户端*/
    if (fd != -1) {
        anetNonBlock(NULL,fd);//将文件描述符设置为非阻塞
        anetEnableTcpNoDelay(NULL,fd);//支持非延迟发送
        if (server.tcpkeepalive)
            anetKeepAlive(NULL,fd,server.tcpkeepalive);//让连接保持声明周期
        if (aeCreateFileEvent(server.el,fd,AE_READABLE,//创建文件事件
            readQueryFromClient, c) == AE_ERR)
        {
            close(fd);
            zfree(c);
            return NULL;
        }
    }

    selectDb(c,0);//选择数据库
    c->id = server.next_client_id++;
    c->fd = fd;
    c->name = NULL;
    c->bufpos = 0;
    c->querybuf = sdsempty();
    c->querybuf_peak = 0;
    c->reqtype = 0;
    c->argc = 0;
    c->argv = NULL;
    c->cmd = c->lastcmd = NULL;
    c->multibulklen = 0;
    c->bulklen = -1;
    c->sentlen = 0;
    c->flags = 0;
    c->ctime = c->lastinteraction = server.unixtime;
    c->authenticated = 0;
    c->replstate = REPL_STATE_NONE;
    c->repl_put_online_on_ack = 0;
    c->reploff = 0;
    c->repl_ack_off = 0;
    c->repl_ack_time = 0;
    c->slave_listening_port = 0;
    c->slave_ip[0] = '\0';
    c->slave_capa = SLAVE_CAPA_NONE;
    c->reply = listCreate();
    c->reply_bytes = 0;
    c->obuf_soft_limit_reached_time = 0;
    listSetFreeMethod(c->reply,decrRefCountVoid);//设置释放函数
    listSetDupMethod(c->reply,dupClientReplyValue);//设置复制函数
    c->btype = BLOCKED_NONE;
    c->bpop.timeout = 0;
    c->bpop.keys = dictCreate(&setDictType,NULL);//创建客户端键空间
    c->bpop.target = NULL;
    c->bpop.numreplicas = 0;
    c->bpop.reploffset = 0;
    c->woff = 0;
    c->watched_keys = listCreate();
    c->pubsub_channels = dictCreate(&setDictType,NULL);//创建
    c->pubsub_patterns = listCreate();
    c->peerid = NULL;
    listSetFreeMethod(c->pubsub_patterns,decrRefCountVoid);
    listSetMatchMethod(c->pubsub_patterns,listMatchObjects);
    if (fd != -1) listAddNodeTail(server.clients,c);//将客户端加入链表
    initClientMultiState(c);//初始化事务状态
    return c;
}

/* This function is called every time we are going to transmit new data to the client. The behavior is the following: If the client should receive new data (normal clients will) the function returns C_OK, and make sure to install the write handler in our event loop so that when the socket is writable new data gets written. If the client should not receive new data, because it is a fake client (used to load AOF in memory), a master or because the setup of the write handler failed, the function returns C_ERR. The function may return C_OK without actually installing the write event handler in the following cases:每当我们要出书新的数据给客户端时,都会调用这个函数,其行为如下:如果客户端应该接受新数据,函数返回C_OK,并确保在我们的事件循环中安装写入处理程序,一遍在套接字可写入时写入新的数据。如果客户端并应该接受新数据,一位它是假客户端,主服务器或由于写处理程序的设置失败,函数返回C_ERR。在以下情况,该函数可能会返回C_OK而不是时间安装写入事件处理程序:
 * 1) The event handler should already be installed since the output buffer already contained something.事件处理程序已经安装,因为输出缓冲区已经包含了一些东西。
 * 2) The client is a slave but not yet online, so we want to just accumulate writes in the buffer but not actually sending them yet. Typically gets called every time a reply is built, before adding more data to the clients output buffers. If the function returns C_ERR no data should be appended to the output buffers. 客户端是一个奴隶,但还没有在线,所以我们只想累计写入缓冲区,但实际上并没有发送他们,通常在每次构建回复时都会调用,然后再向客户端输出缓冲区添加更多数据。如果返回C_ERR,则不应将数据附加到输出缓冲区*/
int prepareClientToWrite(client *c) {
    /* If it's the Lua client we always return ok without installing any handler since there is no socket at all. 如果是lua客户端,总是返回ok并且不安装任何处理程序,因为没有套接字*/
    if (c->flags & CLIENT_LUA) return C_OK;

    /* CLIENT REPLY OFF / SKIP handling: don't send replies. 客户端回复关闭/跳过处理:不发送回复*/
    if (c->flags & (CLIENT_REPLY_OFF|CLIENT_REPLY_SKIP)) return C_ERR;

    /* Masters don't receive replies, unless CLIENT_MASTER_FORCE_REPLY flag is set. 主机没有收到回复,flag设置*/
    if ((c->flags & CLIENT_MASTER) &&
        !(c->flags & CLIENT_MASTER_FORCE_REPLY)) return C_ERR;

    if (c->fd <= 0) return C_ERR; /* Fake client for AOF loading.假装AOF加载客户端 */

    /* Schedule the client to write the output buffers to the socket only if not already done (there were no pending writes already and the client was yet not flagged), and, for slaves, if the slave can actually receive writes at this stage. 安排客户端将输出缓冲区写入套接字,只有在尚未完成的情况下,以及对于从服务器,如果从服务器在此阶段能够世界接收写入数据请求*/
    if (!clientHasPendingReplies(c) &&
        !(c->flags & CLIENT_PENDING_WRITE) &&
        (c->replstate == REPL_STATE_NONE ||
         (c->replstate == SLAVE_STATE_ONLINE && !c->repl_put_online_on_ack)))
    {
        /* Here instead of installing the write handler, we just flag the client and put it into a list of clients that have something to write to the socket. This way before re-entering the event loop, we can try to directly write to the client sockets avoiding a system call. We'll only really install the write handler if we'll not be able to write the whole reply at once. 在这里不是安装写处理程序,而是将客户端标记,并将其放入要写入套接字的客户端列表中,重新进入时间循环之前,我们可以尝试直接写入客户端套接字,避免系统调用,如果我们不能一次写完整个回复,我们只会真正安装写处理程序*/
        c->flags |= CLIENT_PENDING_WRITE;
        listAddNodeHead(server.clients_pending_write,c);
    }

    /* Authorize the caller to queue in the output buffer of this client. 授权调用者在此客户端的输出缓冲区中排队*/
    return C_OK;
}
/* Create a duplicate of the last object in the reply list when it is not exclusively owned by the reply list. 如果答复列表中的最后一个对象不属于答复列表的所有者,则创建最后一个对象的副本*/
robj *dupLastObjectIfNeeded(list *reply) {
    robj *new, *cur;
    listNode *ln;
    serverAssert(listLength(reply) > 0);
    ln = listLast(reply);
    cur = listNodeValue(ln);
    if (cur->refcount > 1) {
        new = dupStringObject(cur);
        decrRefCount(cur);
        listNodeValue(ln) = new;
    }
    return listNodeValue(ln);
}
/* -----------------------------------------------------------------------------
 * Low level functions to add more data to output buffers.
 * -------------------------------------------------------------------------- */
//将reply数据写入缓冲区
int _addReplyToBuffer(client *c, const char *s, size_t len) {
    size_t available = sizeof(c->buf)-c->bufpos;

    if (c->flags & CLIENT_CLOSE_AFTER_REPLY) return C_OK;

    /* If there already are entries in the reply list, we cannot add anything more to the static buffer. 如果条目已经在链表中,不会再增加任何东西到buffer中*/
    if (listLength(c->reply) > 0) return C_ERR;

    /* Check that the buffer has enough space available for this string. 检查buffer是否有足够空间放这个string*/
    if (len > available) return C_ERR;

    memcpy(c->buf+c->bufpos,s,len);
    c->bufpos+=len;
    return C_OK;
}
//将对象放入链表
void _addReplyObjectToList(client *c, robj *o) {
    robj *tail;

    if (c->flags & CLIENT_CLOSE_AFTER_REPLY) return;

    if (listLength(c->reply) == 0) {
        incrRefCount(o);
        listAddNodeTail(c->reply,o);//将对象放入链表
        c->reply_bytes += getStringObjectSdsUsedMemory(o);
    } else {
        tail = listNodeValue(listLast(c->reply));//获取链表尾节点

        /* Append to this object when possible. 可以的话追加到这个对象*/
        if (tail->ptr != NULL &&
            tail->encoding == OBJ_ENCODING_RAW &&
            sdslen(tail->ptr)+sdslen(o->ptr) <= PROTO_REPLY_CHUNK_BYTES)
        {
            c->reply_bytes -= sdsZmallocSize(tail->ptr);
            tail = dupLastObjectIfNeeded(c->reply);
            tail->ptr = sdscatlen(tail->ptr,o->ptr,sdslen(o->ptr));
            c->reply_bytes += sdsZmallocSize(tail->ptr);
        } else {//不行就在家一个节点
            incrRefCount(o);
            listAddNodeTail(c->reply,o);
            c->reply_bytes += getStringObjectSdsUsedMemory(o);
        }
    }
    asyncCloseClientOnOutputBufferLimitReached(c);
}
/* This method takes responsibility over the sds. When it is no longer needed it will be free'd, otherwise it ends up in a robj. 这个方法对sds负责,当它不再需要时,它将被释放,否自将以robj结束*/
void _addReplySdsToList(client *c, sds s) {
    robj *tail;

    if (c->flags & CLIENT_CLOSE_AFTER_REPLY) {
        sdsfree(s);
        return;
    }

    if (listLength(c->reply) == 0) {//链表为空,直接加
        listAddNodeTail(c->reply,createObject(OBJ_STRING,s));
        c->reply_bytes += sdsZmallocSize(s);
    } else {
        tail = listNodeValue(listLast(c->reply));

        /* Append to this object when possible. 尝试追加到尾部节点*/
        if (tail->ptr != NULL && tail->encoding == OBJ_ENCODING_RAW &&
            sdslen(tail->ptr)+sdslen(s) <= PROTO_REPLY_CHUNK_BYTES)
        {
            c->reply_bytes -= sdsZmallocSize(tail->ptr);
            tail = dupLastObjectIfNeeded(c->reply);
            tail->ptr = sdscatlen(tail->ptr,s,sdslen(s));
            c->reply_bytes += sdsZmallocSize(tail->ptr);
            sdsfree(s);
        } else {//新加入一个节点
            listAddNodeTail(c->reply,createObject(OBJ_STRING,s));
            c->reply_bytes += sdsZmallocSize(s);
        }
    }
    asyncCloseClientOnOutputBufferLimitReached(c);
}
//将字符串追加到链表
void _addReplyStringToList(client *c, const char *s, size_t len) {
    robj *tail;

    if (c->flags & CLIENT_CLOSE_AFTER_REPLY) return;

    if (listLength(c->reply) == 0) {
        robj *o = createStringObject(s,len);

        listAddNodeTail(c->reply,o);
        c->reply_bytes += getStringObjectSdsUsedMemory(o);
    } else {
        tail = listNodeValue(listLast(c->reply));

        /* Append to this object when possible. */
        if (tail->ptr != NULL && tail->encoding == OBJ_ENCODING_RAW &&
            sdslen(tail->ptr)+len <= PROTO_REPLY_CHUNK_BYTES)
        {
            c->reply_bytes -= sdsZmallocSize(tail->ptr);
            tail = dupLastObjectIfNeeded(c->reply);
            tail->ptr = sdscatlen(tail->ptr,s,len);
            c->reply_bytes += sdsZmallocSize(tail->ptr);
        } else {
            robj *o = createStringObject(s,len);

            listAddNodeTail(c->reply,o);
            c->reply_bytes += getStringObjectSdsUsedMemory(o);
        }
    }
    asyncCloseClientOnOutputBufferLimitReached(c);
}
/* -----------------------------------------------------------------------------
 * Higher level functions to queue data on the client output buffer. The following functions are the ones that commands implementations will call.
 * -------------------------------------------------------------------------- */
//增加回复对象
void addReply(client *c, robj *obj) {
    if (prepareClientToWrite(c) != C_OK) return;

    /* This is an important place where we can avoid copy-on-write when there is a saving child running, avoiding touching the refcount field of the object if it's not needed. If the encoding is RAW and there is room in the static buffer we'll be able to send the object to the client without messing with its page.这是一个重要的地方,我们可以避免在保存子运行时写入复制,避免触摸对象的引用次数字段,如果编码是raw,并且在静态缓冲区中有空间,我们就可以把对象发送给客户端,而不会弄乱页面 */
    if (sdsEncodedObject(obj)) {
        if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != C_OK)
            _addReplyObjectToList(c,obj);
    } else if (obj->encoding == OBJ_ENCODING_INT) {
        /* Optimization: if there is room in the static buffer for 32 bytes (more than the max chars a 64 bit integer can take as string) we avoid decoding the object and go for the lower level approach. 优化:如果在静态缓冲区中有32个字节的空间,我们避免解码对象,并采取较低级的方法*/
        if (listLength(c->reply) == 0 && (sizeof(c->buf) - c->bufpos) >= 32) {
            char buf[32];
            int len;

            len = ll2string(buf,sizeof(buf),(long)obj->ptr);
            if (_addReplyToBuffer(c,buf,len) == C_OK)
                return;
            /* else... continue with the normal code path, but should never happen actually since we verified there is room. 否则继续正常的代码路径,但不应该实际发生,因为我们验证有空间*/
        }
        obj = getDecodedObject(obj);
        if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != C_OK)
            _addReplyObjectToList(c,obj);
        decrRefCount(obj);
    } else {
        serverPanic("Wrong obj->encoding in addReply()");
    }
}
//增加sds
void addReplySds(client *c, sds s) {
    if (prepareClientToWrite(c) != C_OK) {
        /* The caller expects the sds to be free'd. 调用者希望sds会被释放*/
        sdsfree(s);
        return;
    }
    if (_addReplyToBuffer(c,s,sdslen(s)) == C_OK) {
        sdsfree(s);
    } else {
        /* This method free's the sds when it is no longer needed. 当不再需要的时候,这个方法会释放sds*/
        _addReplySdsToList(c,s);
    }
}
//追加string返回
void addReplyString(client *c, const char *s, size_t len) {
    if (prepareClientToWrite(c) != C_OK) return;
    if (_addReplyToBuffer(c,s,len) != C_OK)
        _addReplyStringToList(c,s,len);
}
//按长度追加错误
void addReplyErrorLength(client *c, const char *s, size_t len) {
    addReplyString(c,"-ERR ",5);
    addReplyString(c,s,len);
    addReplyString(c,"\r\n",2);
}
//追加错误
void addReplyError(client *c, const char *err) {
    addReplyErrorLength(c,err,strlen(err));
}
//
void addReplyErrorFormat(client *c, const char *fmt, ...) {
    size_t l, j;
    va_list ap;
    va_start(ap,fmt);
    sds s = sdscatvprintf(sdsempty(),fmt,ap);//将输入追加到一个sds
    va_end(ap);
    /* Make sure there are no newlines in the string, otherwise invalid protocol is emitted. 确保字符串中没有换行符,否则发出无效的协议*/
    l = sdslen(s);
    for (j = 0; j < l; j++) {
        if (s[j] == '\r' || s[j] == '\n') s[j] = ' ';//将\r或\n变成空格
    }
    addReplyErrorLength(c,s,sdslen(s));
    sdsfree(s);
}
//增加状态长度
void addReplyStatusLength(client *c, const char *s, size_t len) {
    addReplyString(c,"+",1);
    addReplyString(c,s,len);
    addReplyString(c,"\r\n",2);
}
//增加返回状态
void addReplyStatus(client *c, const char *status) {
    addReplyStatusLength(c,status,strlen(status));
}
//按格式化追加到sds
void addReplyStatusFormat(client *c, const char *fmt, ...) {
    va_list ap;
    va_start(ap,fmt);
    sds s = sdscatvprintf(sdsempty(),fmt,ap);
    va_end(ap);
    addReplyStatusLength(c,s,sdslen(s));
    sdsfree(s);
}
/* Adds an empty object to the reply list that will contain the multi bulk length, which is not known when this function is called. 将一个空对象添加到回复列表中,该列表将包含多个批量长度,在调用此函数时这是不可知的*/
void *addDeferredMultiBulkLength(client *c) {
    /* Note that we install the write event here even if the object is not ready to be sent, since we are sure that before returning to the event loop setDeferredMultiBulkLength() will be called. 注意:即使对象尚未准备好发送,我们也会在这里安装写入事件,因为我们确信在返回事件循环之前将会调用setDeferredMultiBulkLength*/
    if (prepareClientToWrite(c) != C_OK) return NULL;
    listAddNodeTail(c->reply,createObject(OBJ_STRING,NULL));
    return listLast(c->reply);
}
/* Populate the length object and try gluing it to the next chunk. 填充长度对象,并尝试黏贴到下一块*/
void setDeferredMultiBulkLength(client *c, void *node, long length) {
    listNode *ln = (listNode*)node;
    robj *len, *next;

    /* Abort when *node is NULL (see addDeferredMultiBulkLength). 节点为NULL时终止*/
    if (node == NULL) return;

    len = listNodeValue(ln);
    len->ptr = sdscatprintf(sdsempty(),"*%ld\r\n",length);
    len->encoding = OBJ_ENCODING_RAW; /* in case it was an EMBSTR. 如果是EMBSTR的话*/
    c->reply_bytes += sdsZmallocSize(len->ptr);
    if (ln->next != NULL) {
        next = listNodeValue(ln->next);

        /* Only glue when the next node is non-NULL (an sds in this case) 只有当下一个节点是非NULL*/
        if (next->ptr != NULL) {
            c->reply_bytes -= sdsZmallocSize(len->ptr);
            c->reply_bytes -= getStringObjectSdsUsedMemory(next);
            len->ptr = sdscatlen(len->ptr,next->ptr,sdslen(next->ptr));
            c->reply_bytes += sdsZmallocSize(len->ptr);
            listDelNode(c->reply,ln->next);
        }
    }
    asyncCloseClientOnOutputBufferLimitReached(c);
}

/* Add a double as a bulk reply 加入double到块*/
void addReplyDouble(client *c, double d) {
    char dbuf[128], sbuf[128];
    int dlen, slen;
    if (isinf(d)) {
        /* Libc in odd systems (Hi Solaris!) will format infinite in a different way, so better to handle it in an explicit way. 奇数系统中的libc将以不同的方式对无限进行格式化,所以更好的以明确的方式处理它*/
        addReplyBulkCString(c, d > 0 ? "inf" : "-inf");
    } else {
        dlen = snprintf(dbuf,sizeof(dbuf),"%.17g",d);
        slen = snprintf(sbuf,sizeof(sbuf),"$%d\r\n%s\r\n",dlen,dbuf);
        addReplyString(c,sbuf,slen);
    }
}

/* Add a long double as a bulk reply, but uses a human readable formatting of the double instead of exposing the crude behavior of doubles to the dear user. 添加一个厂整数作为批量回复,但是用双精度的人类可读格式,而不是将寸草行为的双精度值暴露给用户*/
void addReplyHumanLongDouble(client *c, long double d) {
    robj *o = createStringObjectFromLongDouble(d,1);
    addReplyBulk(c,o);
    decrRefCount(o);
}
/* Add a long long as integer reply or bulk len / multi bulk count. Basically this is used to output . 添加长整型应答或批量长/多批量计数,基本上这是用来输出前缀ll*/
void addReplyLongLongWithPrefix(client *c, long long ll, char prefix) {
    char buf[128];
    int len;

    /* Things like $3\r\n or *2\r\n are emitted very often by the protocol so we have a few shared objects to use if the integer is small like it is most of the times. 像特殊符号经常被协议发出,所以如果整数很小,就像大部分时间一样,我们有一些共享对象可以使用*/
    if (prefix == '*' && ll < OBJ_SHARED_BULKHDR_LEN && ll >= 0) {
        addReply(c,shared.mbulkhdr[ll]);
        return;
    } else if (prefix == '$' && ll < OBJ_SHARED_BULKHDR_LEN && ll >= 0) {
        addReply(c,shared.bulkhdr[ll]);
        return;
    }

    buf[0] = prefix;
    len = ll2string(buf+1,sizeof(buf)-1,ll);
    buf[len+1] = '\r';
    buf[len+2] = '\n';
    addReplyString(c,buf,len+3);
}
//将ll加入返回buffer
void addReplyLongLong(client *c, long long ll) {
    if (ll == 0)
        addReply(c,shared.czero);
    else if (ll == 1)
        addReply(c,shared.cone);
    else
        addReplyLongLongWithPrefix(c,ll,':');
}
//增加多块长度
void addReplyMultiBulkLen(client *c, long length) {
    if (length < OBJ_SHARED_BULKHDR_LEN)
        addReply(c,shared.mbulkhdr[length]);
    else
        addReplyLongLongWithPrefix(c,length,'*');
}
/* Create the length prefix of a bulk reply, example: $2234 对块响应创建长度前缀*/
void addReplyBulkLen(client *c, robj *obj) {
    size_t len;

    if (sdsEncodedObject(obj)) {
        len = sdslen(obj->ptr);
    } else {
        long n = (long)obj->ptr;

        /* Compute how many bytes will take this integer as a radix 10 string 计算多少个字节将把这个整数作为一个基数为10的字符串*/
        len = 1;
        if (n < 0) {
            len++;
            n = -n;
        }
        while((n = n/10) != 0) {
            len++;
        }
    }

    if (len < OBJ_SHARED_BULKHDR_LEN)
        addReply(c,shared.bulkhdr[len]);
    else
        addReplyLongLongWithPrefix(c,len,'$');
}
/* Add a Redis Object as a bulk reply 将redis对象追加到块返回*/
void addReplyBulk(client *c, robj *obj) {
    addReplyBulkLen(c,obj);
    addReply(c,obj);
    addReply(c,shared.crlf);
}

/* Add a C buffer as bulk reply 将一个cbuffer加入块返回*/
void addReplyBulkCBuffer(client *c, const void *p, size_t len) {
    addReplyLongLongWithPrefix(c,len,'$');
    addReplyString(c,p,len);
    addReply(c,shared.crlf);
}

/* Add sds to reply (takes ownership of sds and frees it) 将sds加入到响应区*/
void addReplyBulkSds(client *c, sds s)  {
    addReplySds(c,sdscatfmt(sdsempty(),"$%u\r\n",
        (unsigned long)sdslen(s)));
    addReplySds(c,s);
    addReply(c,shared.crlf);
}

/* Add a C nul term string as bulk reply 将空指针加入到块响应*/
void addReplyBulkCString(client *c, const char *s) {
    if (s == NULL) {
        addReply(c,shared.nullbulk);
    } else {
        addReplyBulkCBuffer(c,s,strlen(s));
    }
}

/* Add a long long as a bulk reply 将ll加入到快响应*/
void addReplyBulkLongLong(client *c, long long ll) {
    char buf[64];
    int len;

    len = ll2string(buf,64,ll);
    addReplyBulkCBuffer(c,buf,len);
}
/* Copy 'src' client output buffers into 'dst' client output buffers. The function takes care of freeing the old output buffers of the destination client. 将src客户单输入缓冲区复制到dst客户端输出缓冲区,该功能负责释放目标客户端的旧输出缓冲区*/
void copyClientOutputBuffer(client *dst, client *src) {
    listRelease(dst->reply);
    dst->reply = listDup(src->reply);
    memcpy(dst->buf,src->buf,src->bufpos);
    dst->bufpos = src->bufpos;
    dst->reply_bytes = src->reply_bytes;
}
/* Return true if the specified client has pending reply buffers to write to the socket. 如果指定的客户端有待处理的应答缓冲区写入套接字,返回true*/
int clientHasPendingReplies(client *c) {
    return c->bufpos || listLength(c->reply);
}
#define MAX_ACCEPTS_PER_CALL 1000
static void acceptCommonHandler(int fd, int flags, char *ip) {
    client *c;
    if ((c = createClient(fd)) == NULL) {
        serverLog(LL_WARNING,
            "Error registering fd event for the new client: %s (fd=%d)",
            strerror(errno),fd);
        close(fd); /* May be already closed, just ignore errors 可能已经关闭,只是忽略错误*/
        return;
    }
    /* If maxclient directive is set and this is one client more... close the connection. Note that we create the client instead to check before for this condition, since now the socket is already set in non-blocking mode and we can send an error for free using the Kernel I/O 如果maxclient指令被设置,并且这是一个更多客户端,关闭连接,我们创建客户端,而不是在此之前检查此条件,因为现在套接字已经设置为非阻塞模式,我们可以发送一个错误免费试用内核I/O*/
    if (listLength(server.clients) > server.maxclients) {
        char *err = "-ERR max number of clients reached\r\n";

        /* That's a best effort error message, don't check write errors 这是一个尽力而为的错误消息,不要检查写入错误*/
        if (write(c->fd,err,strlen(err)) == -1) {
            /* Nothing to do, Just to avoid the warning... 没啥事做,忽略警告*/
        }
        server.stat_rejected_conn++;
        freeClient(c);
        return;
    }

    /* If the server is running in protected mode (the default) and there is no password set, nor a specific interface is bound, we don't accept requests from non loopback interfaces. Instead we try to explain the user what to do to fix it if needed. 如果服务器运行在保护模式下,并且没有设置密码,也没有绑定特定的接口,我们不接受来自非回送接口的请求,相反,我们试图解释用户如何解决它,如果需要的话。*/
    if (server.protected_mode &&
        server.bindaddr_count == 0 &&
        server.requirepass == NULL &&
        !(flags & CLIENT_UNIX_SOCKET) &&
        ip != NULL)
    {
        if (strcmp(ip,"127.0.0.1") && strcmp(ip,"::1")) {
            char *err =
                "-DENIED Redis is running in protected mode because protected "
                "mode is enabled, no bind address was specified, no "
                "authentication password is requested to clients. In this mode "
                "connections are only accepted from the loopback interface. "
                "If you want to connect from external computers to Redis you "
                "may adopt one of the following solutions: "
                "1) Just disable protected mode sending the command "
                "'CONFIG SET protected-mode no' from the loopback interface "
                "by connecting to Redis from the same host the server is "
                "running, however MAKE SURE Redis is not publicly accessible "
                "from internet if you do so. Use CONFIG REWRITE to make this "
                "change permanent. "
                "2) Alternatively you can just disable the protected mode by "
                "editing the Redis configuration file, and setting the protected "
                "mode option to 'no', and then restarting the server. "
                "3) If you started the server manually just for testing, restart "
                "it with the '--protected-mode no' option. "
                "4) Setup a bind address or an authentication password. "
                "NOTE: You only need to do one of the above things in order for "
                "the server to start accepting connections from the outside.\r\n";
            if (write(c->fd,err,strlen(err)) == -1) {
                /* Nothing to do, Just to avoid the warning... */
            }
            server.stat_rejected_conn++;
            freeClient(c);//释放客户端
            return;
        }
    }

    server.stat_numconnections++;
    c->flags |= flags;
}
//连接tcp句柄
void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
    int cport, cfd, max = MAX_ACCEPTS_PER_CALL;
    char cip[NET_IP_STR_LEN];
    UNUSED(el);
    UNUSED(mask);
    UNUSED(privdata);

    while(max--) {
        cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
        if (cfd == ANET_ERR) {
            if (errno != EWOULDBLOCK)
                serverLog(LL_WARNING,
                    "Accepting client connection: %s", server.neterr);
            return;
        }
        serverLog(LL_VERBOSE,"Accepted %s:%d", cip, cport);
        acceptCommonHandler(cfd,0,cip);
    }
}
//接收unix句柄
void acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
    int cfd, max = MAX_ACCEPTS_PER_CALL;
    UNUSED(el);
    UNUSED(mask);
    UNUSED(privdata);

    while(max--) {
        cfd = anetUnixAccept(server.neterr, fd);
        if (cfd == ANET_ERR) {
            if (errno != EWOULDBLOCK)
                serverLog(LL_WARNING,
                    "Accepting client connection: %s", server.neterr);
            return;
        }
        serverLog(LL_VERBOSE,"Accepted connection to %s", server.unixsocket);
        acceptCommonHandler(cfd,CLIENT_UNIX_SOCKET,NULL);
    }
}
//释放客户端参数
static void freeClientArgv(client *c) {
    int j;
    for (j = 0; j < c->argc; j++)
        decrRefCount(c->argv[j]);
    c->argc = 0;
    c->cmd = NULL;
}

/* Close all the slaves connections. This is useful in chained replication when we resync with our own master and want to force all our slaves to resync with us as well. 关闭所有的从属连接,当我们与自己的主机重新同步并且想要强制所有的奴隶与我们重新同步时,这在链接复制中是有用的*/
void disconnectSlaves(void) {
    while (listLength(server.slaves)) {
        listNode *ln = listFirst(server.slaves);
        freeClient((client*)ln->value);
    }
}
/* Remove the specified client from global lists where the client could be referenced, not including the Pub/Sub channels. This is used by freeClient() and replicationCacheMaster(). 从可饮用客户端的全局列表中删除指定的客户端,不包括pubsub通道,*/
void unlinkClient(client *c) {
    listNode *ln;

    /* If this is marked as current client unset it. 如果这被标记为当前客户端未设置它*/
    if (server.current_client == c) server.current_client = NULL;

    /* Certain operations must be done only if the client has an active socket. If the client was already unlinked or if it's a "fake client" the fd is already set to -1. 某些操作只能在客户端拥有活动套接字的情况下完成,如果客户端已经被取消链接,或者是假客户端,则fd已经设为-1*/
    if (c->fd != -1) {
        /* Remove from the list of active clients. 从活动客户端中删除它*/
        ln = listSearchKey(server.clients,c);
        serverAssert(ln != NULL);
        listDelNode(server.clients,ln);

        /* Unregister async I/O handlers and close the socket. 取消注册异步I/O处理程序并关闭套接字*/
        aeDeleteFileEvent(server.el,c->fd,AE_READABLE);
        aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
        close(c->fd);
        c->fd = -1;
    }

    /* Remove from the list of pending writes if needed. 如果需要,从挂起的写入列表中删除*/
    if (c->flags & CLIENT_PENDING_WRITE) {
        ln = listSearchKey(server.clients_pending_write,c);
        serverAssert(ln != NULL);
        listDelNode(server.clients_pending_write,ln);
        c->flags &= ~CLIENT_PENDING_WRITE;
    }

    /* When client was just unblocked because of a blocking operation, remove it from the list of unblocked clients. 当客户端由于阻止操作而被解除阻塞时,请将其从未阻止的客户端列表中删除*/
    if (c->flags & CLIENT_UNBLOCKED) {
        ln = listSearchKey(server.unblocked_clients,c);
        serverAssert(ln != NULL);
        listDelNode(server.unblocked_clients,ln);
        c->flags &= ~CLIENT_UNBLOCKED;
    }
}
//释放客户端
void freeClient(client *c) {
    listNode *ln;

    /* If it is our master that's beging disconnected we should make sure to cache the state to try a partial resynchronization later. Note that before doing this we make sure that the client is not in some unexpected state, by checking its flags. 如果这时我们的主机是断开连接,我们应该确保缓存状态,以稍后尝试部分重新同步。请注意,在执行此操作之前,我们检查其标志来确保客户端处于意外状态。*/
    if (server.master && c->flags & CLIENT_MASTER) {
        serverLog(LL_WARNING,"Connection with master lost.");
        if (!(c->flags & (CLIENT_CLOSE_AFTER_REPLY|
                          CLIENT_CLOSE_ASAP|
                          CLIENT_BLOCKED|
                          CLIENT_UNBLOCKED)))
        {
            replicationCacheMaster(c);
            return;
        }
    }

    /* Log link disconnection with slave 从属连接断开写日志*/
    if ((c->flags & CLIENT_SLAVE) && !(c->flags & CLIENT_MONITOR)) {
        serverLog(LL_WARNING,"Connection with slave %s lost.",
            replicationGetSlaveName(c));
    }

    /* Free the query buffer 释放请求buffer*/
    sdsfree(c->querybuf);
    c->querybuf = NULL;

    /* Deallocate structures used to block on blocking ops. 取消分配用于阻塞阻塞操作的结构*/
    if (c->flags & CLIENT_BLOCKED) unblockClient(c);
    dictRelease(c->bpop.keys);

    /* UNWATCH all the keys 取消watch所有key*/
    unwatchAllKeys(c);
    listRelease(c->watched_keys);

    /* Unsubscribe from all the pubsub channels 取消订阅所有通道*/
    pubsubUnsubscribeAllChannels(c,0);
    pubsubUnsubscribeAllPatterns(c,0);
    dictRelease(c->pubsub_channels);
    listRelease(c->pubsub_patterns);

    /* Free data structures. 释放数据结构*/
    listRelease(c->reply);
    freeClientArgv(c);

    /* Unlink the client: this will close the socket, remove the I/O handlers, and remove references of the client from different places where active clients may be referenced. 取消链接客户端:这将关闭套接字,杀出I/O处理程序,并从可能引用活动客户端的不同位置删除对客户端的引用*/
    unlinkClient(c);

    /* Master/slave cleanup Case 1: we lost the connection with a slave. 主从清理情况:我们丢失了一个从属的连接*/
    if (c->flags & CLIENT_SLAVE) {
        if (c->replstate == SLAVE_STATE_SEND_BULK) {
            if (c->repldbfd != -1) close(c->repldbfd);
            if (c->replpreamble) sdsfree(c->replpreamble);
        }
        list *l = (c->flags & CLIENT_MONITOR) ? server.monitors : server.slaves;
        ln = listSearchKey(l,c);
        serverAssert(ln != NULL);
        listDelNode(l,ln);
        /* We need to remember the time when we started to have zero attached slaves, as after some time we'll free the replication backlog. 我们需要记住我们开始有零附加从属的时间,因为过了一段时间我们将释放复制积累*/
        if (c->flags & CLIENT_SLAVE && listLength(server.slaves) == 0)
            server.repl_no_slaves_since = server.unixtime;
        refreshGoodSlavesCount();
    }

    /* Master/slave cleanup Case 2: we lost the connection with the master. 情况2:丢失了主机的连接*/
    if (c->flags & CLIENT_MASTER) replicationHandleMasterDisconnection();

    /* If this client was scheduled for async freeing we need to remove it from the queue. 如果这个客户单被安排为异步释放,我们需要从队列中删除它*/
    if (c->flags & CLIENT_CLOSE_ASAP) {
        ln = listSearchKey(server.clients_to_close,c);
        serverAssert(ln != NULL);
        listDelNode(server.clients_to_close,ln);
    }

    /* Release other dynamically allocated client structure fields, and finally release the client structure itself. 释放其他动态分配的客户端结构字段,最后释放客户单结构本身*/
    if (c->name) decrRefCount(c->name);
    zfree(c->argv);
    freeClientMultiState(c);
    sdsfree(c->peerid);
    zfree(c);
}
/* Schedule a client to free it at a safe time in the serverCron() function. This function is useful when we need to terminate a client but we are in a context where calling freeClient() is not possible, because the client should be valid for the continuation of the flow of the program. 安排一个客户端在serverCron函数的安全时间去释放它,当我们需要终止一个客户端时,这个函数是有用的,但是我们处于一个不可能调用freeClient的上下文中,因为客户端对于程序流的继续是有效的*/
void freeClientAsync(client *c) {
    if (c->flags & CLIENT_CLOSE_ASAP || c->flags & CLIENT_LUA) return;
    c->flags |= CLIENT_CLOSE_ASAP;
    listAddNodeTail(server.clients_to_close,c);
}
//在异步队列中释放客户端
void freeClientsInAsyncFreeQueue(void) {
    while (listLength(server.clients_to_close)) {
        listNode *ln = listFirst(server.clients_to_close);
        client *c = listNodeValue(ln);

        c->flags &= ~CLIENT_CLOSE_ASAP;
        freeClient(c);
        listDelNode(server.clients_to_close,ln);
    }
}
/* Write data in output buffers to client. Return C_OK if the client is still valid after the call, C_ERR if it was freed. 将输出缓冲区中的数据写入客户端,如果客户端在调用之后仍然有效,则返回ok,如果被释放,则返回err*/
int writeToClient(int fd, client *c, int handler_installed) {
    ssize_t nwritten = 0, totwritten = 0;
    size_t objlen;
    size_t objmem;
    robj *o;

    while(clientHasPendingReplies(c)) {
        if (c->bufpos > 0) {
            nwritten = write(fd,c->buf+c->sentlen,c->bufpos-c->sentlen);//写到tcp
            if (nwritten <= 0) break;
            c->sentlen += nwritten;
            totwritten += nwritten;

            /* If the buffer was sent, set bufpos to zero to continue with the remainder of the reply. 如果已经发送了,将bufpos设置为0*/
            if ((int)c->sentlen == c->bufpos) {
                c->bufpos = 0;
                c->sentlen = 0;
            }
        } else {
            o = listNodeValue(listFirst(c->reply));//获取每个reply去写到缓冲区
            objlen = sdslen(o->ptr);
            objmem = getStringObjectSdsUsedMemory(o);

            if (objlen == 0) {
                listDelNode(c->reply,listFirst(c->reply));
                c->reply_bytes -= objmem;
                continue;
            }

            nwritten = write(fd, ((char*)o->ptr)+c->sentlen,objlen-c->sentlen);
            if (nwritten <= 0) break;
            c->sentlen += nwritten;
            totwritten += nwritten;

            /* If we fully sent the object on head go to the next one 如果我们把头的东西完全发送到下一个*/
            if (c->sentlen == objlen) {
                listDelNode(c->reply,listFirst(c->reply));
                c->sentlen = 0;
                c->reply_bytes -= objmem;
            }
        }
        /* Note that we avoid to send more than NET_MAX_WRITES_PER_EVENT bytes, in a single threaded server it's a good idea to serve other clients as well, even if a very large request comes from super fast link that is always able to accept data (in real world scenario think about 'KEYS *' against the loopback interface). However if we are over the maxmemory limit we ignore that and just deliver as much data as it is possible to deliver. 我们避免发送超过最大字节数,在单线程服务器中,为其他客户端提供服务也是一个好主意,及时非常大的请求来自时钟能够接受数据的超级快速链接,但是如果我们超过了最大内存限制,我们将忽略这一点,只交付尽可能多的数据*/
        if (totwritten > NET_MAX_WRITES_PER_EVENT &&
            (server.maxmemory == 0 ||
             zmalloc_used_memory() < server.maxmemory)) break;
    }
    server.stat_net_output_bytes += totwritten;
    if (nwritten == -1) {
        if (errno == EAGAIN) {
            nwritten = 0;
        } else {
            serverLog(LL_VERBOSE,
                "Error writing to client: %s", strerror(errno));
            freeClient(c);
            return C_ERR;
        }
    }
    if (totwritten > 0) {
        /* For clients representing masters we don't count sending data as an interaction, since we always send REPLCONF ACK commands that take some time to just fill the socket output buffer. We just rely on data / pings received for timeout detection. 对于表示主设备的客户端,我们不会将数据作为交互发送,因为我们总是发送REPLCON ACK命令,需要一些时间来填充套接字输出缓冲区,我们只依靠接收到的数据/查询来检测超时*/
        if (!(c->flags & CLIENT_MASTER)) c->lastinteraction = server.unixtime;
    }
    if (!clientHasPendingReplies(c)) {
        c->sentlen = 0;
        if (handler_installed) aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);

        /* Close connection after entire reply has been sent. 关闭连接,在响应发送之后*/
        if (c->flags & CLIENT_CLOSE_AFTER_REPLY) {
            freeClient(c);
            return C_ERR;
        }
    }
    return C_OK;
}
/* Write event handler. Just send data to the client. 些时间句柄,发送数据给客户端*/
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
    UNUSED(el);
    UNUSED(mask);
    writeToClient(fd,privdata,1);
}

/* This function is called just before entering the event loop, in the hope we can just write the replies to the client output buffer without any need to use a syscall in order to install the writable event handler, get it called, and so forth. 这个函数在进入事件循环之前被调用,希望我们可以直接写回应到客户端输出缓冲区,而不需要使用系统调用来安装可写事件处理程序,调用它*/
int handleClientsWithPendingWrites(void) {
    listIter li;
    listNode *ln;
    int processed = listLength(server.clients_pending_write);

    listRewind(server.clients_pending_write,&li);
    while((ln = listNext(&li))) {
        client *c = listNodeValue(ln);
        c->flags &= ~CLIENT_PENDING_WRITE;
        listDelNode(server.clients_pending_write,ln);

        /* Try to write buffers to the client socket. 尝试写入客户端socket缓冲区*/
        if (writeToClient(c->fd,c,0) == C_ERR) continue;

        /* If there is nothing left, do nothing. Otherwise install the write handler.如果没问题,安装写事件*/
        if (clientHasPendingReplies(c) &&
            aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,
                sendReplyToClient, c) == AE_ERR)
        {
            freeClientAsync(c);
        }
    }
    return processed;
}

/* resetClient prepare the client to process the next command 准备客户单来处理下一个命令*/
void resetClient(client *c) {
    redisCommandProc *prevcmd = c->cmd ? c->cmd->proc : NULL;

    freeClientArgv(c);
    c->reqtype = 0;
    c->multibulklen = 0;
    c->bulklen = -1;

    /* We clear the ASKING flag as well if we are not inside a MULTI, and if what we just executed is not the ASKING command itself. 如果我们不在十五中,清除ASKING标志,如果我们刚执行的不是ASKING命令本身*/
    if (!(c->flags & CLIENT_MULTI) && prevcmd != askingCommand)
        c->flags &= ~CLIENT_ASKING;

    /* Remove the CLIENT_REPLY_SKIP flag if any so that the reply to the next command will be sent, but set the flag if the command we just processed was "CLIENT REPLY SKIP". 删除标志,一遍发送下一个命令的答复,但是如果我们刚刚处理的命令是它,则设置标志*/
    c->flags &= ~CLIENT_REPLY_SKIP;
    if (c->flags & CLIENT_REPLY_SKIP_NEXT) {
        c->flags |= CLIENT_REPLY_SKIP;
        c->flags &= ~CLIENT_REPLY_SKIP_NEXT;
    }
}
//线程buffer
int processInlineBuffer(client *c) {
    char *newline;
    int argc, j;
    sds *argv, aux;
    size_t querylen;

    /* Search for end of line 找到行的结尾*/
    newline = strchr(c->querybuf,'\n');

    /* Nothing to do without a \r\n  \r\n什么都不做*/
    if (newline == NULL) {
        if (sdslen(c->querybuf) > PROTO_INLINE_MAX_SIZE) {
            addReplyError(c,"Protocol error: too big inline request");
            setProtocolError(c,0);
        }
        return C_ERR;
    }

    /* Handle the \r\n case. 处理\r\n*/
    if (newline && newline != c->querybuf && *(newline-1) == '\r')
        newline--;

    /* Split the input buffer up to the \r\n 切割buffer*/
    querylen = newline-(c->querybuf);
    aux = sdsnewlen(c->querybuf,querylen);
    argv = sdssplitargs(aux,&argc);
    sdsfree(aux);
    if (argv == NULL) {
        addReplyError(c,"Protocol error: unbalanced quotes in request");
        setProtocolError(c,0);
        return C_ERR;
    }

    /* Newline from slaves can be used to refresh the last ACK time. This is useful for a slave to ping back while loading a big RDB file. 来自从机的换行可以用来刷新最后的ACK时间,当加载一个大的RDB文件的时候,这对于一个从设备来说是很有用的*/
    if (querylen == 0 && c->flags & CLIENT_SLAVE)
        c->repl_ack_time = server.unixtime;

    /* Leave data after the first line of the query in the buffer 将数据保留在缓冲区中查询的第一行之后*/
    sdsrange(c->querybuf,querylen+2,-1);

    /* Setup argv array on client structure 在客户端结构上安装argv数组*/
    if (argc) {
        if (c->argv) zfree(c->argv);
        c->argv = zmalloc(sizeof(robj*)*argc);
    }

    /* Create redis objects for all arguments. 把所有参数变成redis对象*/
    for (c->argc = 0, j = 0; j < argc; j++) {
        if (sdslen(argv[j])) {
            c->argv[c->argc] = createObject(OBJ_STRING,argv[j]);
            c->argc++;
        } else {
            sdsfree(argv[j]);
        }
    }
    zfree(argv);
    return C_OK;
}
/* Helper function. Trims query buffer to make the function that processes multi bulk requests idempotent. 助手功能,修建查询缓冲区以使处理多个批量请求的函数具有幂等性*/
static void setProtocolError(client *c, int pos) {
    if (server.verbosity <= LL_VERBOSE) {
        sds client = catClientInfoString(sdsempty(),c);
        serverLog(LL_VERBOSE,
            "Protocol error from client: %s", client);
        sdsfree(client);
    }
    c->flags |= CLIENT_CLOSE_AFTER_REPLY;
    sdsrange(c->querybuf,pos,-1);
}
//进行事务缓冲区
int processMultibulkBuffer(client *c) {
    char *newline = NULL;
    int pos = 0, ok;
    long long ll;

    if (c->multibulklen == 0) {
        /* The client should have been reset 重置客户端*/
        serverAssertWithInfo(c,NULL,c->argc == 0);

        /* Multi bulk length cannot be read without a \r\n 事务块长度不能不读\r\n*/
        newline = strchr(c->querybuf,'\r');
        if (newline == NULL) {
            if (sdslen(c->querybuf) > PROTO_INLINE_MAX_SIZE) {
                addReplyError(c,"Protocol error: too big mbulk count string");
                setProtocolError(c,0);
            }
            return C_ERR;
        }

        /* Buffer should also contain \n 缓冲区应该包含*/
        if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))
            return C_ERR;

        /* We know for sure there is a whole line since newline != NULL, so go ahead and find out the multi bulk length. 我们知道自从newline != NUL以后肯定有一整行,所以继续找出多散列长度*/
        serverAssertWithInfo(c,NULL,c->querybuf[0] == '*');
        ok = string2ll(c->querybuf+1,newline-(c->querybuf+1),&ll);
        if (!ok || ll > 1024*1024) {
            addReplyError(c,"Protocol error: invalid multibulk length");
            setProtocolError(c,pos);
            return C_ERR;
        }

        pos = (newline-c->querybuf)+2;
        if (ll <= 0) {
            sdsrange(c->querybuf,pos,-1);
            return C_OK;
        }

        c->multibulklen = ll;

        /* Setup argv array on client structure 在客户端结构上安装argv数组*/
        if (c->argv) zfree(c->argv);
        c->argv = zmalloc(sizeof(robj*)*c->multibulklen);
    }

    serverAssertWithInfo(c,NULL,c->multibulklen > 0);
    while(c->multibulklen) {
        /* Read bulk length if unknown 如果不知道块长度,就读*/
        if (c->bulklen == -1) {
            newline = strchr(c->querybuf+pos,'\r');
            if (newline == NULL) {
                if (sdslen(c->querybuf) > PROTO_INLINE_MAX_SIZE) {
                    addReplyError(c,
                        "Protocol error: too big bulk count string");
                    setProtocolError(c,0);
                    return C_ERR;
                }
                break;
            }

            /* Buffer should also contain \n */
            if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))
                break;

            if (c->querybuf[pos] != '$') {
                addReplyErrorFormat(c,
                    "Protocol error: expected '$', got '%c'",
                    c->querybuf[pos]);
                setProtocolError(c,pos);
                return C_ERR;
            }

            ok = string2ll(c->querybuf+pos+1,newline-(c->querybuf+pos+1),&ll);
            if (!ok || ll < 0 || ll > 512*1024*1024) {
                addReplyError(c,"Protocol error: invalid bulk length");
                setProtocolError(c,pos);
                return C_ERR;
            }

            pos += newline-(c->querybuf+pos)+2;
            if (ll >= PROTO_MBULK_BIG_ARG) {
                size_t qblen;

                /* If we are going to read a large object from network try to make it likely that it will start at c->querybuf boundary so that we can optimize object creation avoiding a large copy of data. 如果我们要从网络中读取一个大对象,可以尝试从querybuf边界开始,这样我们可以优化对象的创建,避免大量的数据拷贝*/
                sdsrange(c->querybuf,pos,-1);
                pos = 0;
                qblen = sdslen(c->querybuf);
                /* Hint the sds library about the amount of bytes this string is going to contain. 向sds库提示该字符串将要包含的字节数量*/
                if (qblen < (size_t)ll+2)
                    c->querybuf = sdsMakeRoomFor(c->querybuf,ll+2-qblen);
            }
            c->bulklen = ll;
        }

        /* Read bulk argument 读取块参数*/
        if (sdslen(c->querybuf)-pos < (unsigned)(c->bulklen+2)) {
            /* Not enough data (+2 == trailing \r\n) */
            break;
        } else {
            /* Optimization: if the buffer contains JUST our bulk element instead of creating a new object by *copying* the sds we just use the current sds string. 优化:如果缓冲区包含just我们的批量元素,而不是通过复制sds创建一个新的对象,我们只使用当前的sds字符串*/
            if (pos == 0 &&
                c->bulklen >= PROTO_MBULK_BIG_ARG &&
                (signed) sdslen(c->querybuf) == c->bulklen+2)
            {
                c->argv[c->argc++] = createObject(OBJ_STRING,c->querybuf);
                sdsIncrLen(c->querybuf,-2); /* remove CRLF */
                /* Assume that if we saw a fat argument we'll see another one likely... 假设我们看到一个胖胖的论点,我们会看到另一个可能*/
                c->querybuf = sdsnewlen(NULL,c->bulklen+2);
                sdsclear(c->querybuf);
                pos = 0;
            } else {
                c->argv[c->argc++] =
                    createStringObject(c->querybuf+pos,c->bulklen);
                pos += c->bulklen+2;
            }
            c->bulklen = -1;
            c->multibulklen--;
        }
    }

    /* Trim to pos 修剪到位置*/
    if (pos) sdsrange(c->querybuf,pos,-1);

    /* We're done when c->multibulk == 0 当事务块等于0时,返回ok*/
    if (c->multibulklen == 0) return C_OK;

    /* Still not read to process the command 仍然没有阅读来处理命令*/
    return C_ERR;
}
//使用输入缓冲
void processInputBuffer(client *c) {
    server.current_client = c;
    /* Keep processing while there is something in the input buffer 保持进程当输入缓冲区有东西*/
    while(sdslen(c->querybuf)) {
        /* Return if clients are paused. 如果客户端处在暂停状态,返回*/
        if (!(c->flags & CLIENT_SLAVE) && clientsArePaused()) break;

        /* Immediately abort if the client is in the middle of something. 如果客户端处于某种事情的中间,立即终止*/
        if (c->flags & CLIENT_BLOCKED) break;

        /* CLIENT_CLOSE_AFTER_REPLY closes the connection once the reply is written to the client. Make sure to not let the reply grow after this flag has been set (i.e. don't process more commands). The same applies for clients we want to terminate ASAP. 一旦将答复写入客户端,将关闭连接,在设置了这个标志之后,确保不要让应答增长,这同样适用于我们想要尽快终止的客户端*/
        if (c->flags & (CLIENT_CLOSE_AFTER_REPLY|CLIENT_CLOSE_ASAP)) break;

        /* Determine request type when unknown. 终止我们不知道的请求类型*/
        if (!c->reqtype) {
            if (c->querybuf[0] == '*') {
                c->reqtype = PROTO_REQ_MULTIBULK;
            } else {
                c->reqtype = PROTO_REQ_INLINE;
            }
        }

        if (c->reqtype == PROTO_REQ_INLINE) {
            if (processInlineBuffer(c) != C_OK) break;
        } else if (c->reqtype == PROTO_REQ_MULTIBULK) {
            if (processMultibulkBuffer(c) != C_OK) break;
        } else {
            serverPanic("Unknown request type");
        }

        /* Multibulk processing could see a <= 0 length. 查看事务块长度*/
        if (c->argc == 0) {
            resetClient(c);
        } else {
            /* Only reset the client when the command was executed. 执行该命令时只能重置客户端*/
            if (processCommand(c) == C_OK)
                resetClient(c);
            /* freeMemoryIfNeeded may flush slave output buffers. This may result into a slave, that may be the active client, to be freed. 可能会清除从记得输出缓冲区,这可能会导致一个奴隶,可能是活跃的客户被释放*/
            if (server.current_client == NULL) break;
        }
    }
    server.current_client = NULL;
}
//从客户端中读取请求
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
    client *c = (client*) privdata;
    int nread, readlen;
    size_t qblen;
    UNUSED(el);
    UNUSED(mask);

    readlen = PROTO_IOBUF_LEN;
    /* If this is a multi bulk request, and we are processing a bulk reply that is large enough, try to maximize the probability that the query buffer contains exactly the SDS string representing the object, even at the risk of requiring more read(2) calls. This way the function processMultiBulkBuffer() can avoid copying buffers to create the Redis Object representing the argument. 如果这是一个多重批量请求,并且我们正在处理一个足够大的批量回复,那么尽量使用查询缓冲区中包含的表示该对象的sds字符串的概率最大化,即使存在需要更多读调用的风险,这样函数可以避免复制缓冲区来创建表示参数的Redis对象*/
    if (c->reqtype == PROTO_REQ_MULTIBULK && c->multibulklen && c->bulklen != -1
        && c->bulklen >= PROTO_MBULK_BIG_ARG)
    {
        int remaining = (unsigned)(c->bulklen+2)-sdslen(c->querybuf);

        if (remaining < readlen) readlen = remaining;
    }

    qblen = sdslen(c->querybuf);
    if (c->querybuf_peak < qblen) c->querybuf_peak = qblen;
    c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);
    nread = read(fd, c->querybuf+qblen, readlen);
    if (nread == -1) {
        if (errno == EAGAIN) {
            return;
        } else {
            serverLog(LL_VERBOSE, "Reading from client: %s",strerror(errno));
            freeClient(c);
            return;
        }
    } else if (nread == 0) {
        serverLog(LL_VERBOSE, "Client closed connection");
        freeClient(c);
        return;
    }

    sdsIncrLen(c->querybuf,nread);
    c->lastinteraction = server.unixtime;
    if (c->flags & CLIENT_MASTER) c->reploff += nread;
    server.stat_net_input_bytes += nread;
    if (sdslen(c->querybuf) > server.client_max_querybuf_len) {
        sds ci = catClientInfoString(sdsempty(),c), bytes = sdsempty();

        bytes = sdscatrepr(bytes,c->querybuf,64);
        serverLog(LL_WARNING,"Closing client that reached max query buffer length: %s (qbuf initial bytes: %s)", ci, bytes);
        sdsfree(ci);
        sdsfree(bytes);
        freeClient(c);
        return;
    }
    processInputBuffer(c);
}
//获取客户端最大缓冲区
void getClientsMaxBuffers(unsigned long *longest_output_list,
                          unsigned long *biggest_input_buffer) {
    client *c;
    listNode *ln;
    listIter li;
    unsigned long lol = 0, bib = 0;

    listRewind(server.clients,&li);
    while ((ln = listNext(&li)) != NULL) {
        c = listNodeValue(ln);

        if (listLength(c->reply) > lol) lol = listLength(c->reply);
        if (sdslen(c->querybuf) > bib) bib = sdslen(c->querybuf);
    }
    *longest_output_list = lol;
    *biggest_input_buffer = bib;
}

/* A Redis "Peer ID" is a colon separated ip:port pair. For IPv4 it's in the form x.y.z.k:port, example: "127.0.0.1:1234". For IPv6 addresses we use [] around the IP part, like in "[::1]:1234". For Unix sockets we use path:0, like in "/tmp/redis:0". A Peer ID always fits inside a buffer of NET_PEER_ID_LEN bytes, including the null term. On failure the function still populates 'peerid' with the "?:0" string in case you want to relax error checking or need to display something anyway (see anetPeerToString implementation for more info). 这是冒号分割的IP:PORT对,对于IPV4,它的形式是x.y.z.k:port,对于ipv6,使用[::]:port,。。。*/
void genClientPeerId(client *client, char *peerid,
                            size_t peerid_len) {
    if (client->flags & CLIENT_UNIX_SOCKET) {
        /* Unix socket client. */
        snprintf(peerid,peerid_len,"%s:0",server.unixsocket);
    } else {
        /* TCP client. */
        anetFormatPeer(client->fd,peerid,peerid_len);
    }
}
/* This function returns the client peer id, by creating and caching it if client->peerid is NULL, otherwise returning the cached value.The Peer ID never changes during the life of the client, however it is expensive to compute. 这个函数返回客户端对端ID,通过创建并缓存它,如果client->peerid为空,否则返回缓存的值,对端ID在客户端的生命周期内不会改变,但是计算其阿里很贵*/
char *getClientPeerId(client *c) {
    char peerid[NET_PEER_ID_LEN];

    if (c->peerid == NULL) {
        genClientPeerId(c,peerid,sizeof(peerid));
        c->peerid = sdsnew(peerid);
    }
    return c->peerid;
}

/* Concatenate a string representing the state of a client in an human readable format, into the sds string 's'. 将表示客户单状态的字符串连接到sds字符串的s字符串中*/
sds catClientInfoString(sds s, client *client) {
    char flags[16], events[3], *p;
    int emask;

    p = flags;
    if (client->flags & CLIENT_SLAVE) {
        if (client->flags & CLIENT_MONITOR)
            *p++ = 'O';
        else
            *p++ = 'S';
    }
    if (client->flags & CLIENT_MASTER) *p++ = 'M';
    if (client->flags & CLIENT_MULTI) *p++ = 'x';
    if (client->flags & CLIENT_BLOCKED) *p++ = 'b';
    if (client->flags & CLIENT_DIRTY_CAS) *p++ = 'd';
    if (client->flags & CLIENT_CLOSE_AFTER_REPLY) *p++ = 'c';
    if (client->flags & CLIENT_UNBLOCKED) *p++ = 'u';
    if (client->flags & CLIENT_CLOSE_ASAP) *p++ = 'A';
    if (client->flags & CLIENT_UNIX_SOCKET) *p++ = 'U';
    if (client->flags & CLIENT_READONLY) *p++ = 'r';
    if (p == flags) *p++ = 'N';
    *p++ = '\0';

    emask = client->fd == -1 ? 0 : aeGetFileEvents(server.el,client->fd);
    p = events;
    if (emask & AE_READABLE) *p++ = 'r';
    if (emask & AE_WRITABLE) *p++ = 'w';
    *p = '\0';
    return sdscatfmt(s,
        "id=%U addr=%s fd=%i name=%s age=%I idle=%I flags=%s db=%i sub=%i psub=%i multi=%i qbuf=%U qbuf-free=%U obl=%U oll=%U omem=%U events=%s cmd=%s",
        (unsigned long long) client->id,
        getClientPeerId(client),
        client->fd,
        client->name ? (char*)client->name->ptr : "",
        (long long)(server.unixtime - client->ctime),
        (long long)(server.unixtime - client->lastinteraction),
        flags,
        client->db->id,
        (int) dictSize(client->pubsub_channels),
        (int) listLength(client->pubsub_patterns),
        (client->flags & CLIENT_MULTI) ? client->mstate.count : -1,
        (unsigned long long) sdslen(client->querybuf),
        (unsigned long long) sdsavail(client->querybuf),
        (unsigned long long) client->bufpos,
        (unsigned long long) listLength(client->reply),
        (unsigned long long) getClientOutputBufferMemoryUsage(client),
        events,
        client->lastcmd ? client->lastcmd->name : "NULL");
}
//获取所有客户端的信息字符串
sds getAllClientsInfoString(void) {
    listNode *ln;
    listIter li;
    client *client;
    sds o = sdsnewlen(NULL,200*listLength(server.clients));
    sdsclear(o);
    listRewind(server.clients,&li);
    while ((ln = listNext(&li)) != NULL) {
        client = listNodeValue(ln);
        o = catClientInfoString(o,client);
        o = sdscatlen(o,"\n",1);
    }
    return o;
}
//client命令
void clientCommand(client *c) {
    listNode *ln;
    listIter li;
    client *client;

    if (!strcasecmp(c->argv[1]->ptr,"list") && c->argc == 2) {
        /* CLIENT LIST 列出所有客户端命令*/
        sds o = getAllClientsInfoString();
        addReplyBulkCBuffer(c,o,sdslen(o));
        sdsfree(o);
    } else if (!strcasecmp(c->argv[1]->ptr,"reply") && c->argc == 3) {
        /* CLIENT REPLY ON|OFF|SKIP */
        if (!strcasecmp(c->argv[2]->ptr,"on")) {
            c->flags &= ~(CLIENT_REPLY_SKIP|CLIENT_REPLY_OFF);
            addReply(c,shared.ok);
        } else if (!strcasecmp(c->argv[2]->ptr,"off")) {
            c->flags |= CLIENT_REPLY_OFF;
        } else if (!strcasecmp(c->argv[2]->ptr,"skip")) {
            if (!(c->flags & CLIENT_REPLY_OFF))
                c->flags |= CLIENT_REPLY_SKIP_NEXT;
        } else {
            addReply(c,shared.syntaxerr);
            return;
        }
    } else if (!strcasecmp(c->argv[1]->ptr,"kill")) {
        /* CLIENT KILL  杀死某一客户端命令
         * CLIENT KILL 
        char *addr = NULL;
        int type = -1;
        uint64_t id = 0;
        int skipme = 1;
        int killed = 0, close_this_client = 0;

        if (c->argc == 3) {
            /* Old style syntax: CLIENT KILL  */
            addr = c->argv[2]->ptr;
            skipme = 0; /* With the old form, you can kill yourself. */
        } else if (c->argc > 3) {
            int i = 2; /* Next option index. */

            /* New style syntax: parse options. */
            while(i < c->argc) {
                int moreargs = c->argc > i+1;

                if (!strcasecmp(c->argv[i]->ptr,"id") && moreargs) {
                    long long tmp;

                    if (getLongLongFromObjectOrReply(c,c->argv[i+1],&tmp,NULL)
                        != C_OK) return;
                    id = tmp;
                } else if (!strcasecmp(c->argv[i]->ptr,"type") && moreargs) {
                    type = getClientTypeByName(c->argv[i+1]->ptr);
                    if (type == -1) {
                        addReplyErrorFormat(c,"Unknown client type '%s'",
                            (char*) c->argv[i+1]->ptr);
                        return;
                    }
                } else if (!strcasecmp(c->argv[i]->ptr,"addr") && moreargs) {
                    addr = c->argv[i+1]->ptr;
                } else if (!strcasecmp(c->argv[i]->ptr,"skipme") && moreargs) {
                    if (!strcasecmp(c->argv[i+1]->ptr,"yes")) {
                        skipme = 1;
                    } else if (!strcasecmp(c->argv[i+1]->ptr,"no")) {
                        skipme = 0;
                    } else {
                        addReply(c,shared.syntaxerr);
                        return;
                    }
                } else {
                    addReply(c,shared.syntaxerr);
                    return;
                }
                i += 2;
            }
        } else {
            addReply(c,shared.syntaxerr);
            return;
        }

        /* Iterate clients killing all the matching clients. */
        listRewind(server.clients,&li);
        while ((ln = listNext(&li)) != NULL) {
            client = listNodeValue(ln);
            if (addr && strcmp(getClientPeerId(client),addr) != 0) continue;
            if (type != -1 && getClientType(client) != type) continue;
            if (id != 0 && client->id != id) continue;
            if (c == client && skipme) continue;

            /* Kill it. */
            if (c == client) {
                close_this_client = 1;
            } else {
                freeClient(client);
            }
            killed++;
        }

        /* Reply according to old/new format. */
        if (c->argc == 3) {
            if (killed == 0)
                addReplyError(c,"No such client");
            else
                addReply(c,shared.ok);
        } else {
            addReplyLongLong(c,killed);
        }

        /* If this client has to be closed, flag it as CLOSE_AFTER_REPLY only after we queued the reply to its output buffers. */
        if (close_this_client) c->flags |= CLIENT_CLOSE_AFTER_REPLY;
    } else if (!strcasecmp(c->argv[1]->ptr,"setname") && c->argc == 3) {
        int j, len = sdslen(c->argv[2]->ptr);
        char *p = c->argv[2]->ptr;

        /* Setting the client name to an empty string actually removes the current name. 将客户单名称设置为空字符串是江上会删除当前的名称*/
        if (len == 0) {
            if (c->name) decrRefCount(c->name);
            c->name = NULL;
            addReply(c,shared.ok);
            return;
        }

        /* Otherwise check if the charset is ok. We need to do this otherwise
         * CLIENT LIST format will break. You should always be able to
         * split by space to get the different fields. */
        for (j = 0; j < len; j++) {
            if (p[j] < '!' || p[j] > '~') { /* ASCII is assumed. */
                addReplyError(c,
                    "Client names cannot contain spaces, "
                    "newlines or special characters.");
                return;
            }
        }
        if (c->name) decrRefCount(c->name);
        c->name = c->argv[2];
        incrRefCount(c->name);
        addReply(c,shared.ok);
    } else if (!strcasecmp(c->argv[1]->ptr,"getname") && c->argc == 2) {
        if (c->name)
            addReplyBulk(c,c->name);
        else
            addReply(c,shared.nullbulk);
    } else if (!strcasecmp(c->argv[1]->ptr,"pause") && c->argc == 3) {
        long long duration;

        if (getTimeoutFromObjectOrReply(c,c->argv[2],&duration,UNIT_MILLISECONDS)
                                        != C_OK) return;
        pauseClients(duration);
        addReply(c,shared.ok);
    } else {
        addReplyError(c, "Syntax error, try CLIENT (LIST | KILL | GETNAME | SETNAME | PAUSE | REPLY)");
    }
}
/* This callback is bound to POST and "Host:" command names. Those are not really commands, but are used in security attacks in order to talk to Redis instances via HTTP, with a technique called "cross protocol scripting" which exploits the fact that services like Redis will discard invalid HTTP headers and will process what follows. As a protection against this attack, Redis will terminate the connection when a POST or "Host:" header is seen, and will log the event from time to time (to avoid creating a DOS as a result of too many logs). 词汇调绑定到post和主机:命令名称,这些并不是真正的命令,而是被用在安全攻击中,一遍通过http与redis实例进行交互,这种技术被称为跨协议脚本,它利用了像redis这样的服务会丢弃无效的http头并处理后续事件的事实,为了方式这种攻击,redis将在post或host头部出现时终止连接,并且会不时记录事件*/
void securityWarningCommand(client *c) {
    static time_t logged_time;
    time_t now = time(NULL);

    if (labs(now-logged_time) > 60) {
        serverLog(LL_WARNING,"Possible SECURITY ATTACK detected. It looks like somebody is sending POST or Host: commands to Redis. This is likely due to an attacker attempting to use Cross Protocol Scripting to compromise your Redis instance. Connection aborted.");
        logged_time = now;
    }
    freeClientAsync(c);
}

/* Rewrite the command vector of the client. All the new objects ref count is incremented. The old command vector is freed, and the old objects ref count is decremented. 重写客户端的命令向量,所有新的对象引用计数递增,旧的命令向量被释放,旧的对象引用计数递减*/
void rewriteClientCommandVector(client *c, int argc, ...) {
    va_list ap;
    int j;
    robj **argv; /* The new argument vector 新参数向量*/

    argv = zmalloc(sizeof(robj*)*argc);
    va_start(ap,argc);
    for (j = 0; j < argc; j++) {
        robj *a;

        a = va_arg(ap, robj*);
        argv[j] = a;
        incrRefCount(a);
    }
    /* We free the objects in the original vector at the end, so we are sure that if the same objects are reused in the new vector the refcount gets incremented before it gets decremented. 我们在最后释放原始想两种的对象,因此我们确信,如果在新想两种重复使用相同的对象,则在递减之前,引用计数会递增*/
    for (j = 0; j < c->argc; j++) decrRefCount(c->argv[j]);
    zfree(c->argv);
    /* Replace argv and argc with our new versions. 覆盖参数使用我们的新版本*/
    c->argv = argv;
    c->argc = argc;
    c->cmd = lookupCommandOrOriginal(c->argv[0]->ptr);
    serverAssertWithInfo(c,NULL,c->cmd != NULL);
    va_end(ap);
}
/* Completely replace the client command vector with the provided one. 用提供的一个完成覆盖客户端命令向量*/
void replaceClientCommandVector(client *c, int argc, robj **argv) {
    freeClientArgv(c);
    zfree(c->argv);
    c->argv = argv;
    c->argc = argc;
    c->cmd = lookupCommandOrOriginal(c->argv[0]->ptr);
    serverAssertWithInfo(c,NULL,c->cmd != NULL);
}

/* Rewrite a single item in the command vector. The new val ref count is incremented, and the old decremented. It is possible to specify an argument over the current size of the argument vector: in this case the array of objects gets reallocated and c->argc set to the max value.在命令向量中重写单个项目,新的valref计数递增,旧的递减,可以在参数向量的当前大小上指定参数:在这种情况下,将重新分配对象数组,并且c->argc设置为最大值.然而这取决于调用者
 However it's up to the caller to
 1. Make sure there are no "holes" and all the arguments are set.确保没有漏洞,并设置所有的参数
 * 2. If the original argument vector was longer than the one we want to end with, it's up to the caller to set c->argc an free the no longer used objects on c->argv. 如果原始参数向量比我们想要结束的那个更长,则由调用者设置来释放不在使用的对象*/
void rewriteClientCommandArgument(client *c, int i, robj *newval) {
    robj *oldval;

    if (i >= c->argc) {
        c->argv = zrealloc(c->argv,sizeof(robj*)*(i+1));
        c->argc = i+1;
        c->argv[i] = NULL;
    }
    oldval = c->argv[i];
    c->argv[i] = newval;
    incrRefCount(newval);
    if (oldval) decrRefCount(oldval);

    /* If this is the command name make sure to fix c->cmd. 如果这是命令名称,请确保修复c->cmd*/
    if (i == 0) {
        c->cmd = lookupCommandOrOriginal(c->argv[0]->ptr);
        serverAssertWithInfo(c,NULL,c->cmd != NULL);
    }
}
/* This function returns the number of bytes that Redis is virtually using to store the reply still not read by the client. It is "virtual" since the reply output list may contain objects that are shared and are not really using additional memory. The function returns the total sum of the length of all the objects stored in the output list, plus the memory used to allocate every list node. The static reply buffer is not taken into account since it is allocated anyway.这个函数返回redis虚拟使用的字节数来存储仍然没有被客户端读取的答复,这是虚拟的,因为答复输出列表可能包含共享的对象,而实际上并没有使用额外的内存,该函数返回存储在列表输出中的所有的长度综合,以及用于分配每个列表节点的内存,静态回复缓冲区不会被考虑在内,因为无论如何都是分配的
 Note: this function is very fast so can be called as many time as the caller wishes. The main usage of this function currently is enforcing the client output length limits. 这个功能非常快,所以可以随着调用者的医院多次调用,这个函数的主要用法是强制执行客户端的输出长度限制*/
unsigned long getClientOutputBufferMemoryUsage(client *c) {
    unsigned long list_item_size = sizeof(listNode)+sizeof(robj);

    return c->reply_bytes + (list_item_size*listLength(c->reply));
}
//获取客户端类型:一般,从属,订阅,主机
int getClientType(client *c) {
    if (c->flags & CLIENT_MASTER) return CLIENT_TYPE_MASTER;
    if ((c->flags & CLIENT_SLAVE) && !(c->flags & CLIENT_MONITOR))
        return CLIENT_TYPE_SLAVE;
    if (c->flags & CLIENT_PUBSUB) return CLIENT_TYPE_PUBSUB;
    return CLIENT_TYPE_NORMAL;
}
//获取客户单类型的函数
int getClientTypeByName(char *name) {
    if (!strcasecmp(name,"normal")) return CLIENT_TYPE_NORMAL;
    else if (!strcasecmp(name,"slave")) return CLIENT_TYPE_SLAVE;
    else if (!strcasecmp(name,"pubsub")) return CLIENT_TYPE_PUBSUB;
    else if (!strcasecmp(name,"master")) return CLIENT_TYPE_MASTER;
    else return -1;
}
char *getClientTypeName(int class) {
    switch(class) {
    case CLIENT_TYPE_NORMAL: return "normal";
    case CLIENT_TYPE_SLAVE:  return "slave";
    case CLIENT_TYPE_PUBSUB: return "pubsub";
    case CLIENT_TYPE_MASTER: return "master";
    default:                       return NULL;
    }
}
/* The function checks if the client reached output buffer soft or hard limit, and also update the state needed to check the soft limit as a side effect.
 Return value: non-zero if the client reached the soft or the hard limit.  Otherwise zero is returned. 该功能检查客户端是否达到输出缓冲区软限制或硬限制,并更新检查软限制所需的状态作为副作用*/
int checkClientOutputBufferLimits(client *c) {
    int soft = 0, hard = 0, class;
    unsigned long used_mem = getClientOutputBufferMemoryUsage(c);

    class = getClientType(c);
    /* For the purpose of output buffer limiting, masters are handled like normal clients. 为了输出缓冲区限制,服务器像普通客户单一样处理*/
    if (class == CLIENT_TYPE_MASTER) class = CLIENT_TYPE_NORMAL;

    if (server.client_obuf_limits[class].hard_limit_bytes &&
        used_mem >= server.client_obuf_limits[class].hard_limit_bytes)
        hard = 1;
    if (server.client_obuf_limits[class].soft_limit_bytes &&
        used_mem >= server.client_obuf_limits[class].soft_limit_bytes)
        soft = 1;

    /* We need to check if the soft limit is reached continuously for the specified amount of seconds. 我们需要检查软限制是否持续达到指定的秒数*/
    if (soft) {
        if (c->obuf_soft_limit_reached_time == 0) {
            c->obuf_soft_limit_reached_time = server.unixtime;
            soft = 0; /* First time we see the soft limit reached 我们第一次看到达到的软限制*/
        } else {
            time_t elapsed = server.unixtime - c->obuf_soft_limit_reached_time;

            if (elapsed <=
                server.client_obuf_limits[class].soft_limit_seconds) {
                soft = 0; /* The client still did not reached the max number of seconds for the soft limit to be considered reached. 客户端任然没有达到软限制被认为达到最大秒数*/
            }
        }
    } else {
        c->obuf_soft_limit_reached_time = 0;
    }
    return soft || hard;
}
/* Asynchronously close a client if soft or hard limit is reached on the output buffer size. The caller can check if the client will be closed checking if the client CLIENT_CLOSE_ASAP flag is set.
 Note: we need to close the client asynchronously because this function is called from contexts where the client can't be freed safely, i.e. from the lower level functions pushing data inside the client output buffers. 如果在输出缓冲区大小上达到软限制或硬限制,则异步关闭客户机,调用者可以检查客户单是否将关闭,检查是否设置了客户单标志。我们需要异步关闭客户端,因为这个函数是无法安全释放客户端上下文中调用的,也就是从下层客户端输出缓冲区中的数据推送数据*/
void asyncCloseClientOnOutputBufferLimitReached(client *c) {
    serverAssert(c->reply_bytes < SIZE_MAX-(1024*64));
    if (c->reply_bytes == 0 || c->flags & CLIENT_CLOSE_ASAP) return;
    if (checkClientOutputBufferLimits(c)) {
        sds client = catClientInfoString(sdsempty(),c);

        freeClientAsync(c);
        serverLog(LL_WARNING,"Client %s scheduled to be closed ASAP for overcoming of output buffer limits.", client);
        sdsfree(client);
    }
}
/* Helper function used by freeMemoryIfNeeded() in order to flush slaves
 * output buffers without returning control to the event loop.
 * This is also called by SHUTDOWN for a best-effort attempt to send
 * slaves the latest writes. */
void flushSlavesOutputBuffers(void) {
    listIter li;
    listNode *ln;

    listRewind(server.slaves,&li);
    while((ln = listNext(&li))) {
        client *slave = listNodeValue(ln);
        int events;

        /* Note that the following will not flush output buffers of slaves in STATE_ONLINE but having put_online_on_ack set to true: in this case the writable event is never installed, since the purpose of put_online_on_ack is to postpone the moment it is installed. This is what we want since slaves in this state should not receive writes before the first ACK. */
        events = aeGetFileEvents(server.el,slave->fd);
        if (events & AE_WRITABLE &&
            slave->replstate == SLAVE_STATE_ONLINE &&
            clientHasPendingReplies(slave))
        {
            writeToClient(slave->fd,slave,0);
        }
    }
}
//暂停客户端
void pauseClients(mstime_t end) {
    if (!server.clients_paused || end > server.clients_pause_end_time)
        server.clients_pause_end_time = end;
    server.clients_paused = 1;
}
//返回客户端是否暂停
int clientsArePaused(void) {
    if (server.clients_paused &&
        server.clients_pause_end_time < server.mstime)
    {
        listNode *ln;
        listIter li;
        client *c;

        server.clients_paused = 0;

        /* Put all the clients in the unblocked clients queue in order to force the re-processing of the input buffer if any. 将所有客户端放在为阻塞的客户端队列中,以便强制重新处理输入缓冲区*/
        listRewind(server.clients,&li);
        while ((ln = listNext(&li)) != NULL) {
            c = listNodeValue(ln);

            /* Don't touch slaves and blocked clients. The latter pending requests be processed when unblocked. 不要触摸从属和被阻止的客户端,接触封锁后,处理后面的待处理请求*/
            if (c->flags & (CLIENT_SLAVE|CLIENT_BLOCKED)) continue;
            c->flags |= CLIENT_UNBLOCKED;
            listAddNodeTail(server.unblocked_clients,c);
        }
    }
    return server.clients_paused;
}
/* This function is called by Redis in order to process a few events from time to time while blocked into some not interruptible operation. This allows to reply to clients with the -LOADING error while loading the data set at startup or after a full resynchronization with the master and so forth.这个函数被redis调用来处理一些事件,同时被阻塞成一些不可中断的操作,这允许在加载启动时的数据集时或在与主机完全重新同步之后使用loading错误来回复客户单
 * It calls the event loop in order to process a few events. Specifically we try to call the event loop 4 times as long as we receive acknowledge that some event was processed, in order to go forward with the accept, read, write, close sequence needed to serve a client.它调用事件循环来处理一些事件,具体而言我们尝试电泳事件循环四次,只要我们收到某个事件已经被处理,一边继续为服务客户端所需的接收,读写,写入,关闭序列
 The function returns the total number of events processed. 返回处理的时间总数*/
int processEventsWhileBlocked(void) {
    int iterations = 4; /* See the function top-comment. */
    int count = 0;
    while (iterations--) {
        int events = 0;
        events += aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);
        events += handleClientsWithPendingWrites();
        if (!events) break;
        count += events;
    }
    return count;
}

你可能感兴趣的:(redis)