按前面2篇文章的分析可以知道,从网络读取了数据之后,将会进入conn_parse_cmd状态,该状态是按协议来解析读取到的网络数据。
case conn_parse_cmd: //解析数据 if (try_read_command(c) == 0) { //如果读取到的数据不够,我们继续等待,等读取到的数据够了,再进行解析 conn_set_state(c, conn_waiting); } break; //memcached支持二进制协议和文本协议 static int try_read_command(conn *c) { assert(c != NULL); assert(c->rcurr <= (c->rbuf + c->rsize)); assert(c->rbytes > 0); if (c->protocol == negotiating_prot || c->transport == udp_transport) { //二进制协议有标志,按标志进行区分 if ((unsigned char) c->rbuf[0] == (unsigned char) PROTOCOL_BINARY_REQ) { c->protocol = binary_prot;//二进制协议 } else { c->protocol = ascii_prot;//文本协议 } if (settings.verbose > 1) { fprintf(stderr, "%d: Client using the %s protocol\n", c->sfd, prot_text(c->protocol)); } } //如果是二进制协议 if (c->protocol == binary_prot) { //二进制协议读取到的数据小于二进制协议的头部长度 if (c->rbytes < sizeof(c->binary_header)) { //返回继续读数据 return 0; } else { #ifdef NEED_ALIGN //如果需要对齐,则按8字节对齐,对齐能提高CPU读取的效率 if (((long)(c->rcurr)) % 8 != 0) { //调整缓冲区 memmove(c->rbuf, c->rcurr, c->rbytes); c->rcurr = c->rbuf; if (settings.verbose > 1) { fprintf(stderr, "%d: Realign input buffer\n", c->sfd); } } #endif protocol_binary_request_header* req;//二进制协议头 req = (protocol_binary_request_header*) c->rcurr; //调试信息 if (settings.verbose > 1) { /* Dump the packet before we convert it to host order */ int ii; fprintf(stderr, "<%d Read binary protocol data:", c->sfd); for (ii = 0; ii < sizeof(req->bytes); ++ii) { if (ii % 4 == 0) { fprintf(stderr, "\n<%d ", c->sfd); } fprintf(stderr, " 0x%02x", req->bytes[ii]); } fprintf(stderr, "\n"); } c->binary_header = *req; c->binary_header.request.keylen = ntohs(req->request.keylen);//二进制协议相关内容 c->binary_header.request.bodylen = ntohl(req->request.bodylen); c->binary_header.request.cas = ntohll(req->request.cas); //判断魔数是否合法,魔数用来防止TCP粘包 if (c->binary_header.request.magic != PROTOCOL_BINARY_REQ) { if (settings.verbose) { fprintf(stderr, "Invalid magic: %x\n", c->binary_header.request.magic); } conn_set_state(c, conn_closing); return -1; } c->msgcurr = 0; c->msgused = 0; c->iovused = 0; if (add_msghdr(c) != 0) { out_string(c, "SERVER_ERROR out of memory"); return 0; } c->cmd = c->binary_header.request.opcode; c->keylen = c->binary_header.request.keylen; c->opaque = c->binary_header.request.opaque; //清除客户端传递的cas值 c->cas = 0; dispatch_bin_command(c);//协议数据处理 c->rbytes -= sizeof(c->binary_header);//更新已经读取到的字节数据 c->rcurr += sizeof(c->binary_header);//更新缓冲区的路标信息 } }
文本协议的过程和二进制协议的过程类似,此处不分析,另外dispatch_bin_command是处理具体的(比如get,set等)操作的,和是二进制协议具体相关的,解析完一些数据之后,会进入到conn_nread的流程,也就是读取指定数目数据的过程,这个过程主要是做具体的操作了,比如get,add,set操作。
case bin_read_set_value: complete_update_bin(c);//执行Update操作 break; case bin_reading_get_key: process_bin_get(c);//执行get操作 break;状态机的整个处理过程就介绍到这里,其他的状态我们就不介绍了,后续的文章主要是分析一些数据的操作和内存结构,了解了这些之后,其实其他状态就相对容易很多。