• libevent源码解析2


    官方示例

    /* For sockaddr_in */
    #include <netinet/in.h>
    /* For socket functions */
    #include <sys/socket.h>
    /* For fcntl */
    #include <fcntl.h>
    
    #include <event2/event.h>
    
    #include <assert.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    
    #define MAX_LINE 16384
    
    void do_read(evutil_socket_t fd, short events, void *arg);
    void do_write(evutil_socket_t fd, short events, void *arg);
    
    char
    rot13_char(char c)
    {
        /* We don't want to use isalpha here; setting the locale would change
         * which characters are considered alphabetical. */
        if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
            return c + 13;
        else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
            return c - 13;
        else
            return c;
    }
    
    struct fd_state {
        char buffer[MAX_LINE];
        size_t buffer_used;
    
        size_t n_written;
        size_t write_upto;
    
        struct event *read_event;
        struct event *write_event;
    };
    
    struct fd_state *
    alloc_fd_state(struct event_base *base, evutil_socket_t fd)
    {
        struct fd_state *state = malloc(sizeof(struct fd_state));
        if (!state)
            return NULL;
        state->read_event = event_new(base, fd, EV_READ|EV_PERSIST, do_read, state);
        if (!state->read_event) {
            free(state);
            return NULL;
        }
        state->write_event =
            event_new(base, fd, EV_WRITE|EV_PERSIST, do_write, state);
    
        if (!state->write_event) {
            event_free(state->read_event);
            free(state);
            return NULL;
        }
    
        state->buffer_used = state->n_written = state->write_upto = 0;
    
        assert(state->write_event);
        return state;
    }
    
    void
    free_fd_state(struct fd_state *state)
    {
        event_free(state->read_event);
        event_free(state->write_event);
        free(state);
    }
    
    void
    do_read(evutil_socket_t fd, short events, void *arg)
    {
        struct fd_state *state = arg;
        char buf[1024];
        int i;
        ssize_t result;
        while (1) {
            assert(state->write_event);
            result = recv(fd, buf, sizeof(buf), 0);
            if (result <= 0)
                break;
    
            for (i=0; i < result; ++i)  {
                if (state->buffer_used < sizeof(state->buffer))
                    state->buffer[state->buffer_used++] = rot13_char(buf[i]);
                if (buf[i] == '
    ') {
                    assert(state->write_event);
                    event_add(state->write_event, NULL);
                    state->write_upto = state->buffer_used;
                }
            }
        }
    
        if (result == 0) {
            free_fd_state(state);
        } else if (result < 0) {
            if (errno == EAGAIN) // XXXX use evutil macro
                return;
            perror("recv");
            free_fd_state(state);
        }
    }
    
    void
    do_write(evutil_socket_t fd, short events, void *arg)
    {
        struct fd_state *state = arg;
    
        while (state->n_written < state->write_upto) {
            ssize_t result = send(fd, state->buffer + state->n_written,
                                  state->write_upto - state->n_written, 0);
            if (result < 0) {
                if (errno == EAGAIN) // XXX use evutil macro
                    return;
                free_fd_state(state);
                return;
            }
            assert(result != 0);
    
            state->n_written += result;
        }
    
        if (state->n_written == state->buffer_used)
            state->n_written = state->write_upto = state->buffer_used = 1;
    
        event_del(state->write_event);
    }
    
    void
    do_accept(evutil_socket_t listener, short event, void *arg)
    {
        struct event_base *base = arg;
        struct sockaddr_storage ss;
        socklen_t slen = sizeof(ss);
        int fd = accept(listener, (struct sockaddr*)&ss, &slen);
        if (fd < 0) { // XXXX eagain??
            perror("accept");
        } else if (fd > FD_SETSIZE) {
            close(fd); // XXX replace all closes with EVUTIL_CLOSESOCKET */
        } else {
            struct fd_state *state;
            evutil_make_socket_nonblocking(fd);
            state = alloc_fd_state(base, fd);
            assert(state); /*XXX err*/
            assert(state->write_event);
            event_add(state->read_event, NULL);
        }
    }
    
    void
    run(void)
    {
        evutil_socket_t listener;
        struct sockaddr_in sin;
        struct event_base *base;
        struct event *listener_event;
    
        base = event_base_new();
        if (!base)
            return; /*XXXerr*/
    
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = 0;
        sin.sin_port = htons(40713);
    
        listener = socket(AF_INET, SOCK_STREAM, 0);
        evutil_make_socket_nonblocking(listener);
    
    #ifndef WIN32
        {
            int one = 1;
            setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
        }
    #endif
    
        if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
            perror("bind");
            return;
        }
    
        if (listen(listener, 16)<0) {
            perror("listen");
            return;
        }
    
        listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base);
        /*XXX check it */
        event_add(listener_event, NULL);
    
        event_base_dispatch(base);
    }
    
    int
    main(int c, char **v)
    {
        setvbuf(stdout, NULL, _IONBF, 0);
    
        run();
        return 0;
    }
    

    上一节,我们介绍了从开始,创建event base,到最后分发消息。这一节我们介绍一下,具体的,收到监听,发送消息和接收消息。

    回调流程

    写服务端程序,肯定是先创建监听socket,然后等待链接进来,链接进来后,我们保存起来,进行收发消息。对于libevent封装后,我们看一下,链接是怎么进来的。

    首先在上面的程序中,还是先创建socket,绑定端口,开启监听,然后创建event,与socket绑定,添加到event base中,开启分发。

    创建event的时候,在event_new中,绑定了event base,event,socket,监听事件,回调接口和回调参数。

    调用event_base_dispatch分发事件。然后就没有额外的代码了,这就说明event_base_dispatch是一个循环,等待事件的到来,我们跟踪一下如何在调用了event_base_dispatch之后,到达事件后,可以回调到传入的回调函数,比如上面的do_accept do_read do_write。

    event_base_dispatch->event_base_loop->event_process_active->event_process_active_single_queue

    上面就是事件回调调用堆栈,先是event_base_dispatch,然后具体调用在event_base_loop,获得事件后,调用event_process_active,做了一些处理,最终调用event_process_active_single_queue,在这个函数中,根据不同的事件,调用保存的对应的回调函数。

    整体流程就是先创建一个event base,event base根据系统的不同选择不同的IO模型,linux下选择epoll,也就是调用epoll_wait。然后创建监听socket,绑定一个event,添加到主循环,调用epoll_ctl。在event_base_dispatch中,调用epoll_wait等待事件到达。到达后,根据传入的回调函数,执行回调。

  • 相关阅读:
    [数据知识]DAMA数据管理—引论
    How to clear/delete all the partition table from a disk or partition in Linux
    Rust Safe Coding Notes
    量化交易平台
    斯坦福大学——人工智能本科4年课程清单
    去中心化数字身份DID简介——五、DID的应用
    linux c 打印时间最简单的实例
    sqlalchemy中Column的默认值属性
    Ubuntu安装jdk8的两种方式
    面试官:手撕十大排序算法,你会几种?(转)
  • 原文地址:https://www.cnblogs.com/studywithallofyou/p/13554779.html
Copyright © 2020-2023  润新知