通过前面一篇文章分析得知,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; }
Memcached源码分析之状态机(二),布布扣,bubuko.com
原文:http://blog.csdn.net/lcli2009/article/details/21949991