memched1.0源码阅读(4)——事件的处理流程

来源:互联网 发布:cloud tv mac 编辑:程序博客网 时间:2024/06/07 06:28
在讲解事件处理之前,先讲解套接字会话的创建:
我们可以看到在套接字会话创建的过程中,调用了一个叫event_set/event_add的函数,这是libevent的接口,它就把套接字感兴趣的事件(例如读写之类的事件)以及相应的事件回调函数(event_handler)添加到libevent中,这样每当这些事件发生的时候libevent就会调用这个事件回调函数。memched做的所有工作几乎都在event_handler这个函数中。
// 新建一个套接字会话conn *conn_new(int sfd, int init_state, int event_flags) {    conn *c;    /* do we have a free conn structure from a previous close? */    // 从空闲的connect数组中查找一个conn    if (freecurr > 0) {        c = freeconns[--freecurr];    }    // 如果在空闲数组中没有找到    else { /* allocate a new one */        // 申请内存        if (!(c = (conn *)malloc(sizeof(conn)))) {            perror("malloc()");            return 0;        }        // 读写缓存的初始化        c->rbuf = c->wbuf = 0;        c->ilist = 0;        // 创建读写缓存        c->rbuf = (char *) malloc(DATA_BUFFER_SIZE);        c->wbuf = (char *) malloc(DATA_BUFFER_SIZE);        // 创建该连接对应的item数组,该数组的每一个元素都存放了一个item的指针        c->ilist = (item **) malloc(sizeof(item *)*200);        if (c->rbuf == 0 || c->wbuf == 0 || c->ilist == 0) {            if (c->rbuf != 0) free(c->rbuf);            if (c->wbuf != 0) free(c->wbuf);            if (c->ilist !=0) free(c->ilist);            free(c);            perror("malloc()");            return 0;        }        c->rsize = c->wsize = DATA_BUFFER_SIZE;        c->isize = 200;        // 增加系统中连接的计数        stats.conn_structs++;    }    c->sfd = sfd;    c->state = init_state;    c->rlbytes = 0;    c->rbytes = c->wbytes = 0;    c->wcurr = c->wbuf;    c->rcurr = c->rbuf;    c->icurr = c->ilist;     c->ileft = 0;    c->iptr = c->ibuf;    c->ibytes = 0;    // 下一个准备进行的操作?    c->write_and_go = conn_read;    c->write_and_free = 0;    c->item = 0;    // 设置对应的事件处理器,事件处理的回调函数是event_handler,这个函数会调用核心函数drive_machine    // drive_machine中处理各种事件(accpet,read、write)    event_set(&c->event, sfd, event_flags, event_handler, (void *)c);    c->ev_flags = event_flags;    // 把它的事件处理器添加到libevent中    if (event_add(&c->event, 0) == -1) {        free(c);        return 0;    }    stats.curr_conns++;    stats.total_conns++;    return c;}
事件的处理流程——核心函数drive_machine(在event_handler 中被调用) :进入一个大循环中,根据套接字会话的状态进行不同的处理:
1、如果是conn_listening状态。表示套接字会话要处理客户的连接,那么调用accpet函数,接收一个套接字,然后把套接字设置为非阻塞,然后根据这个套接字创建套接字会话(conn_new函数),并同时把该套接字感兴趣事件以及相应的事件回调函数添加到libevent中。
2、如果是conn_read状态。表示套接字准备处理读事件,那么调用try_read_command读取客户发送过来的命令,然后调用try_read_network读取客户发送过来数据块,接着调用update_event更新套接字感兴趣的事件。
3、如果是conn_nread状态。表示套接字准备接收指定长度的数据,如果还没有接收完成,那么就继续接收。如果接收完成,那么调用complete_nread函数,这个函数主要的作用就是往memched添加或者更新一个对象。
4、如果是conn_swallow状态。在某些情况下,memched已经满了(内存不足),那么对于客户端发送过来的数据进行读取然后忽略掉。
5、如果是conn_write状态。表示套接字准备向客户发送数据。
6、如果是conn_mwrite状态。同上,不过情况稍微复杂点。
7、如果是conn_closing状态。表示客户端关闭了。
// 核心函数void drive_machine(conn *c) {    int exit = 0;    int sfd, flags = 1;    socklen_t addrlen;    struct sockaddr addr;    conn *newc;    int res;    while (!exit) {      /*printf("state %d\n", c->state); */        switch(c->state) {        // 如果是监听事件        case conn_listening:            addrlen = sizeof(addr);            // accpet一个套接字            if ((sfd = accept(c->sfd, &addr, &addrlen)) == -1) {                if (errno == EAGAIN || errno == EWOULDBLOCK) {                    perror("accept() shouldn't block");                } else {                    perror("accept()");                }                return;            }            // 设置套接字为非阻塞            if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||                fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {                perror("setting O_NONBLOCK");                close(sfd);                return;            }            // 根据套接字以及相关信息创建会话对象            newc = conn_new(sfd, conn_read, EV_READ | EV_PERSIST);            if (!newc) {                if(settings.verbose)                    fprintf(stderr, "couldn't create new connection\n");                close(sfd);                return;            }            exit = 1;            break;        // 如果是读事件        case conn_read:            // 读取命令            if (try_read_command(c)) {                continue;            }            // 读取数据            if (try_read_network(c)) {                continue;            }            /* we have no command line and no data to read from network */            // 更新事件            if (!update_event(c, EV_READ | EV_PERSIST)) {                if(settings.verbose)                    fprintf(stderr, "Couldn't update event\n");                c->state = conn_closing;                break;            }            exit = 1;            break;        case conn_nread:            /* we are reading rlbytes into rcurr; */            // 读取指定数量的字节,rlbytes存放着还要读取的字节的数量            // 如果rlbytes等于0,那么表示已经读取完毕            if (c->rlbytes == 0) {                // 进行处理                complete_nread(c);                break;            }            /* first check if we have leftovers in the conn_read buffer */            // 如果读取的字节数量大于0            // 这里没看懂,为什么要把rbuf指向的数据复制复制到rcurr所指的位置(关键是它们指向同一个缓冲区)?            if (c->rbytes > 0) {                int tocopy = c->rbytes > c->rlbytes ? c->rlbytes : c->rbytes;                // 那么把读取到的数据复制到读缓存中                memcpy(c->rcurr, c->rbuf, tocopy);                c->rcurr += tocopy;                c->rlbytes -= tocopy;                if (c->rbytes > tocopy) {                    memmove(c->rbuf, c->rbuf+tocopy, c->rbytes - tocopy);                }                c->rbytes -= tocopy;                break;            }            /*  now try reading from the socket */            // 尝试读取数据(非阻塞的读)            res = read(c->sfd, c->rcurr, c->rlbytes);            // 成功读取到数据            if (res > 0) {                stats.bytes_read += res;                c->rcurr += res;                c->rlbytes -= res;                break;            }            // 返回0,表示对方关闭了连接            if (res == 0) { /* end of stream */                c->state = conn_closing;                break;            }            // 出错,但是出错码是EAGAIN或者EWOULDBLOCK,表示系统正在等待数据的到来            if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {                if (!update_event(c, EV_READ | EV_PERSIST)) {                    if(settings.verbose)                         fprintf(stderr, "Couldn't update event\n");                    c->state = conn_closing;                    break;                }                exit = 1;                break;            }            /* otherwise we have a real error, on which we close the connection */            if(settings.verbose)                fprintf(stderr, "Failed to read, and not due to blocking\n");            c->state = conn_closing;            break;        // conn_swallow状态是当内存不足并且调用add/set/replace命令的时候才会出现, 此操作主要是读取客户端发送过来的数据, 并且忽略掉        case conn_swallow:            /* we are reading sbytes and throwing them away */            if (c->sbytes == 0) {                c->state = conn_read;                break;            }            /* first check if we have leftovers in the conn_read buffer */            if (c->rbytes > 0) {                int tocopy = c->rbytes > c->sbytes ? c->sbytes : c->rbytes;                c->sbytes -= tocopy;                if (c->rbytes > tocopy) {                    memmove(c->rbuf, c->rbuf+tocopy, c->rbytes - tocopy);                }                c->rbytes -= tocopy;                break;            }            /*  now try reading from the socket */            res = read(c->sfd, c->rbuf, c->rsize > c->sbytes ? c->sbytes : c->rsize);            if (res > 0) {                stats.bytes_read += res;                c->sbytes -= res;                break;            }            if (res == 0) { /* end of stream */                c->state = conn_closing;                break;            }            if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {                if (!update_event(c, EV_READ | EV_PERSIST)) {                    if(settings.verbose)                        fprintf(stderr, "Couldn't update event\n");                    c->state = conn_closing;                    break;                }                exit = 1;                break;            }            /* otherwise we have a real error, on which we close the connection */            if(settings.verbose)                fprintf(stderr, "Failed to read, and not due to blocking\n");            c->state = conn_closing;            break;        // 写        case conn_write:            /* we are writing wbytes bytes starting from wcurr */            if (c->wbytes == 0) {                if (c->write_and_free) {                    free(c->write_and_free);                    c->write_and_free = 0;                }                c->state = c->write_and_go;                break;            }            // 非阻塞写            res = write(c->sfd, c->wcurr, c->wbytes);            if (res > 0) {                stats.bytes_written += res;                c->wcurr  += res;                c->wbytes -= res;                break;            }            if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {                if (!update_event(c, EV_WRITE | EV_PERSIST)) {                    if(settings.verbose)                        fprintf(stderr, "Couldn't update event\n");                    c->state = conn_closing;                    break;                }                                exit = 1;                break;            }            /* if res==0 or res==-1 and error is not EAGAIN or EWOULDBLOCK,               we have a real error, on which we close the connection */            if(settings.verbose)                fprintf(stderr, "Failed to write, and not due to blocking\n");            c->state = conn_closing;            break;        // 发送(写)指定长度的字节        case conn_mwrite:            /*              * we're writing ibytes bytes from iptr. iptr alternates between             * ibuf, where we build a string "VALUE...", and it->data for the              * current item. When we finish a chunk, we choose the next one using              * ipart, which has the following semantics: 0 - start the loop, 1 -              * we finished ibuf, go to current it->data; 2 - we finished it->data,             * move to the next item and build its ibuf; 3 - we finished all items,              * write "END".             */            if (c->ibytes > 0) {                res = write(c->sfd, c->iptr, c->ibytes);                if (res > 0) {                    stats.bytes_written += res;                    c->iptr += res;                    c->ibytes -= res;                    break;                }                if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {                    if (!update_event(c, EV_WRITE | EV_PERSIST)) {                        if(settings.verbose)                            fprintf(stderr, "Couldn't update event\n");                        c->state = conn_closing;                        break;                    }                    exit = 1;                    break;                }                /* if res==0 or res==-1 and error is not EAGAIN or EWOULDBLOCK,                   we have a real error, on which we close the connection */                if(settings.verbose)                    fprintf(stderr, "Failed to write, and not due to blocking\n");                c->state = conn_closing;                break;            } else {                item *it;                /* we finished a chunk, decide what to do next */                switch (c->ipart) {                case 1:                    it = *(c->icurr);                    c->iptr = it->data;                    c->ibytes = it->nbytes;                    c->ipart = 2;                    break;                case 2:                    it = *(c->icurr);                    item_remove(it);                    if (c->ileft <= 1) {                        c->ipart = 3;                        break;                    } else {                        c->ileft--;                        c->icurr++;                    }                    /* FALL THROUGH */                case 0:                    it = *(c->icurr);                    sprintf(c->ibuf, "VALUE %s %u %u\r\n", it->key, it->flags, it->nbytes - 2);                    c->iptr = c->ibuf;                    c->ibytes = strlen(c->iptr);                    c->ipart = 1;                    break;                case 3:                    out_string(c, "END");                    break;                }            }            break;        case conn_closing: // 关闭套接字            conn_close(c);            exit = 1;            break;        }    }    return;}


0 0
原创粉丝点击