通过前面一篇文章分析得知,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;
}