按前面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;
状态机的整个处理过程就介绍到这里,其他的状态我们就不介绍了,后续的文章主要是分析一些数据的操作和内存结构,了解了这些之后,其实其他状态就相对容易很多。