Memcached源码分析之状态机(二)

通过前面一篇文章分析得知,conn_wating状态是在等待读取数据,conn_wating通过修改libevent事件(修改为读事件)之后就进入了conn_read状态,该状态就是从网络中读取数据,下面我们详细分析conn_read状态。

case conn_read:
		res = IS_UDP(c->transport) ? try_read_udp(c) : try_read_network(c);//判断采用UDP协议还是TCP协议

		switch (res)
		{
		case READ_NO_DATA_RECEIVED://未读取到数据
			conn_set_state(c, conn_waiting);//继续等待
			break;
		case READ_DATA_RECEIVED://读取数据
			conn_set_state(c, conn_parse_cmd);//开始解析数据
			break;
		case READ_ERROR://读取发生错误
			conn_set_state(c, conn_closing);//关闭连接
			break;
		case READ_MEMORY_ERROR: //申请内存空间错误,继续尝试
			break;
		}
		break;
//采用TCP协议,从网络读取数据
static enum try_read_result try_read_network(conn *c)
{
enum try_read_result gotdata = READ_NO_DATA_RECEIVED;
int res;
int num_allocs = 0;
assert(c != NULL);
//rcurr标记读缓冲区的开始位置,如果不在,通过memmove调整
if (c->rcurr != c->rbuf)
{
    if (c->rbytes != 0)
        memmove(c->rbuf, c->rcurr, c->rbytes);
    c->rcurr = c->rbuf;//rcurr指向读缓冲区起始位置
}

while (1)//循环读取
{
    if (c->rbytes >= c->rsize)//已经读取到的数据大于读缓冲区的大小
    {
        if (num_allocs == 4)
        {
            return gotdata;
        }
        ++num_allocs;
        char *new_rbuf = realloc(c->rbuf, c->rsize * 2);//按2倍扩容空间
        if (!new_rbuf)//realloc发生错误,也就是申请内存失败
        {
            if (settings.verbose > 0)
                fprintf(stderr, "Couldn't realloc input buffer\n");
            c->rbytes = 0; //忽略已经读取到的数据
            out_string(c, "SERVER_ERROR out of memory reading request");
            c->write_and_go = conn_closing;//下一个状态就是conn_closing状态
            return READ_MEMORY_ERROR;//返回错误
        }
        c->rcurr = c->rbuf = new_rbuf;//读缓冲区指向新的缓冲区
        c->rsize *= 2;//读缓冲区的大小扩大2倍
    }

    int avail = c->rsize - c->rbytes;//读缓冲区剩余空间
    res = read(c->sfd, c->rbuf + c->rbytes, avail);//执行网络读取,这个是非阻塞的读
    if (res > 0)//如果读取到了数据
    {
        pthread_mutex_lock(&c->thread->stats.mutex);
        c->thread->stats.bytes_read += res;//更新线程的统计数据
        pthread_mutex_unlock(&c->thread->stats.mutex);
        gotdata = READ_DATA_RECEIVED;//返回读取到数据的状态
        c->rbytes += res;//读取到的数据个数增加res
        if (res == avail)//最多读取到avail个,如果已经读到了,则可以尝试继续读取
        {
            continue;
        }
        else//否则,小于avail,表示已经没数据了,退出循环。
        {
            break;
        }
    }
    if (res == 0)//表示已经断开网络连接了
    {
        return READ_ERROR;
    }
    if (res == -1)//因为是非阻塞的,所以会返回下面的两个错误码
    {
        if (errno == EAGAIN || errno == EWOULDBLOCK)
        {
            break;
        }
        return READ_ERROR;//如果返回为负数,且不是上面两个数,则表示发生了其他错误,返回READ_ERROR
    }
}
return gotdata;
}

上面描述的是TCP的数据读取,下面我们分析下UDP的数据读取,UDP是数据报的形式,读取到一个,就是一个完整的数据报,所以其处理过程简单。

//UDP读取网络数据
static enum try_read_result try_read_udp(conn *c)
{
int res;

assert(c != NULL);

c->request_addr_size = sizeof(c->request_addr);
res = recvfrom(c->sfd, c->rbuf, c->rsize, 0, &c->request_addr,
		&c->request_addr_size);//执行UDP的网络读取
if (res > 8)//UDP数据包大小大于8,已经有可能是业务数据包
{
	unsigned char *buf = (unsigned char *) c->rbuf;
	pthread_mutex_lock(&c->thread->stats.mutex);
	c->thread->stats.bytes_read += res;//更新每个线程的统计数据
	pthread_mutex_unlock(&c->thread->stats.mutex);

	/* Beginning of UDP packet is the request ID; save it. */
	c->request_id = buf[0] * 256 + buf[1];//UDP为了防止丢包,增加了确认字段

	/* If this is a multi-packet request, drop it. */
	if (buf[4] != 0 || buf[5] != 1)//一些业务的特征信息判断
	{
		out_string(c, "SERVER_ERROR multi-packet request not supported");
		return READ_NO_DATA_RECEIVED;
	}

	/* Don't care about any of the rest of the header. */
	res -= 8;
	memmove(c->rbuf, c->rbuf + 8, res);//调整缓冲区

	c->rbytes = res;//更新信息
	c->rcurr = c->rbuf;
	return READ_DATA_RECEIVED;
}
return READ_NO_DATA_RECEIVED;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值