libevent 源码 hello world

1.说明

程序是个简单的服务端程序,用于监听9995端口上的tcp连接。当有客户端接入,服务端会向客户端发送一条消息。

 

2.主线程代码解析

    // 创建关于libevent变量
    struct event_base *base;
    struct evconnlistener *listener;
    struct event *signal_event;

    // 初始化服务端设置
    struct sockaddr_in sin = {0};
    sin.sin_family = AF_INET;
    sin.sin_port = htons(PORT);
#ifdef _WIN32
    WSADATA wsa_data;
    WSAStartup(0x0201, &wsa_data);
#endif

    // 创建 event_base
    base = event_base_new();
    if (!base) {
        fprintf(stderr, "Could not initialize libevent!\n");
        return 1;
    }

    // 创建套接字
    // 绑定
    // 接受连接请求
    listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
        LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
        (struct sockaddr*)&sin,
        sizeof(sin));

    if (!listener) {
        fprintf(stderr, "Could not create a listener!\n");
        return 1;
    }

    // 信号事件
    signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);

    if (!signal_event || event_add(signal_event, NULL)<0) {
        fprintf(stderr, "Could not create/add a signal event!\n");
        return 1;
    }

    // 启动循环监听
    event_base_dispatch(base);

    evconnlistener_free(listener);
    event_free(signal_event);
    event_base_free(base);

    printf("done\n");
    return 0;

通过上面的部分代码可以看出,在流程上,tcp通信的建立没有太多变化。

接口变化,新的接口如下:

evconnlistener_new_bind

这个接口通过名称,可以知道是用于监听及绑定的。

struct evconnlistener *
evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb,
    void *ptr, unsigned flags, int backlog, const struct sockaddr *sa,
    int socklen)
/**
   Allocate a new evconnlistener object to listen for incoming TCP connections
   on a given address.
在给定地址分配一个监听对象,用于监听接入的tcp连接。 @param base The event base to associate the listener with. 和监听对象绑定到一起 @param cb A callback to be invoked when a new connection arrives. If the callback is NULL, the listener will be treated as disabled until the callback is set.
当一个新连接接入,回调会被唤醒,如果回调为NULL,会被视作不支持监听,除非设置了回调。 @param ptr A user-supplied pointer to give to the callback. 提供给回调的指针(用户提供的指针) @param flags Any number of LEV_OPT_* flags. LEV_OPT_类型的指针 @param backlog Passed to the listen() call to determine the length of the acceptable connection backlog. Set to -1 for a reasonable default.
传递给listen()调用的,用于决定可接受连接的长度。默认为-1. @param sa The address to listen for connections on. 监听的地址 @param socklen The length of the address. 地址的长度
*/

 

调用

    // 创建套接字
    // 绑定
    // 接受连接请求
    listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
        LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
        (struct sockaddr*)&sin,
        sizeof(sin));

 

第二个参数是回调函数

/**@file event2/listener.h

   @brief A callback that we invoke when a listener has a new connection.

   @param listener The evconnlistener 监听
   @param fd The new file descriptor 文件描叙符
   @param addr The source address of the connection 接入的连接的源地址
   @param socklen The length of addr 地址的长度
   @param user_arg the pointer passed to evconnlistener_new() 传递给evconnlisterner_new()的指针

上面的5个参数全部是出参。
*/ typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);

 

为了进一步了解回调函数的出参是怎么一回事,来看一下对应接口evconnlistener_new_bind

struct evconnlistener *
evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb,
    void *ptr, unsigned flags, int backlog, const struct sockaddr *sa,
    int socklen)
{
    struct evconnlistener *listener;
    evutil_socket_t fd;
    int on = 1;
    int family = sa ? sa->sa_family : AF_UNSPEC;
    int socktype = SOCK_STREAM | EVUTIL_SOCK_NONBLOCK;
    int support_keepalive = 1;

    if (backlog == 0)
        return NULL; //这里默认值为-1.

    if (flags & LEV_OPT_CLOSE_ON_EXEC)
        socktype |= EVUTIL_SOCK_CLOEXEC;

   // 文件描叙符 fd
= evutil_socket_(family, socktype, 0); if (fd == -1) return NULL;
   // 检测
#if defined(_WIN32) && defined(EVENT__HAVE_AFUNIX_H) if (family == AF_UNIX && evutil_check_working_afunix_()) { /* AF_UNIX socket can't set SO_KEEPALIVE option on Win10. * Avoid 10042 error. */ support_keepalive = 0; } #endif if (support_keepalive) { if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on))<0) goto err; } if (flags & LEV_OPT_REUSEABLE) { if (evutil_make_listen_socket_reuseable(fd) < 0) goto err; } if (flags & LEV_OPT_REUSEABLE_PORT) { if (evutil_make_listen_socket_reuseable_port(fd) < 0) goto err; } if (flags & LEV_OPT_DEFERRED_ACCEPT) { if (evutil_make_tcp_listen_socket_deferred(fd) < 0) goto err; } if (flags & LEV_OPT_BIND_IPV6ONLY) { if (evutil_make_listen_socket_ipv6only(fd) < 0) goto err; }
   // 绑定套接字
if (sa) { if (bind(fd, sa, socklen)<0) goto err; }
   // 监听是否有客户端接入 listener
= evconnlistener_new(base, cb, ptr, flags, backlog, fd); if (!listener) goto err; return listener; err: evutil_closesocket(fd); return NULL; }

 

在上面的代码中可以看到文件描叙符的创建,对应的接口是:evuntil_socket_

/* Internal wrapper around 'socket' to provide Linux-style support for
 * syscall-saving methods where available.
* 内部包装socket,以提供linux风格支持 * * In addition to regular socket behavior, you can use a bitwise or to set the * flags EVUTIL_SOCK_NONBLOCK and EVUTIL_SOCK_CLOEXEC in the 'type' argument, * to make the socket nonblocking or close-on-exec with as few syscalls as * possible.
* 除了正常的socket操作,对于'type'参数,也可以按位使用或者将它设置为EVUTIL_SOCK_NONBLOCK 和 EVuTIL_SOCK_CLOEXEC,这样可以尽可能减少对应的系统调用
*/ evutil_socket_t evutil_socket_(int domain, int type, int protocol) { evutil_socket_t r; #if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) r = socket(domain, type, protocol); //可以看到,这里是我们熟悉的创建套接字的接口 if (r >= 0) return r; else if ((type & (SOCK_NONBLOCK|SOCK_CLOEXEC)) == 0) return -1; #endif #define SOCKET_TYPE_MASK (~(EVUTIL_SOCK_NONBLOCK|EVUTIL_SOCK_CLOEXEC)) r = socket(domain, type & SOCKET_TYPE_MASK, protocol); if (r < 0) return -1; if (type & EVUTIL_SOCK_NONBLOCK) { if (evutil_fast_socket_nonblocking(r) < 0) { evutil_closesocket(r); return -1; } } if (type & EVUTIL_SOCK_CLOEXEC) { if (evutil_fast_socket_closeonexec(r) < 0) { evutil_closesocket(r); return -1; } } return r; }

 

同时,还有一个用于监听是否有客户端接入的接口:evconnlistener_new

struct evconnlistener *
evconnlistener_new(struct event_base *base,
    evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
    evutil_socket_t fd)
{
    struct evconnlistener_event *lev;

#ifdef _WIN32
    if (base && event_base_get_iocp_(base)) {
        const struct win32_extension_fns *ext =
            event_get_win32_extension_fns_();
        if (ext->AcceptEx && ext->GetAcceptExSockaddrs)
            return evconnlistener_new_async(base, cb, ptr, flags,
                backlog, fd);
    }
#endif

   // 非win32 if (backlog > 0) { if (listen(fd, backlog) < 0) return NULL; } else if (backlog < 0) { if (listen(fd, 128) < 0) return NULL; } lev = mm_calloc(1, sizeof(struct evconnlistener_event)); if (!lev) return NULL; lev->base.ops = &evconnlistener_event_ops; lev->base.cb = cb; lev->base.user_data = ptr; lev->base.flags = flags; lev->base.refcnt = 1; lev->base.accept4_flags = 0; if (!(flags & LEV_OPT_LEAVE_SOCKETS_BLOCKING)) lev->base.accept4_flags |= EVUTIL_SOCK_NONBLOCK; if (flags & LEV_OPT_CLOSE_ON_EXEC) lev->base.accept4_flags |= EVUTIL_SOCK_CLOEXEC; if (flags & LEV_OPT_THREADSAFE) { EVTHREAD_ALLOC_LOCK(lev->base.lock, EVTHREAD_LOCKTYPE_RECURSIVE); } event_assign(&lev->listener, base, fd, EV_READ|EV_PERSIST, listener_read_cb, lev); if (!(flags & LEV_OPT_DISABLED)) evconnlistener_enable(&lev->base); return &lev->base; }

 

分析win32部分,可以看到一个新的接口:evconnlistener_new_async

struct evconnlistener *
evconnlistener_new_async(struct event_base *base,
    evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
    evutil_socket_t fd)
{
    struct sockaddr_storage ss;
    int socklen = sizeof(ss);
    struct evconnlistener_iocp *lev;
    int i;

    flags |= LEV_OPT_THREADSAFE;

    if (!base || !event_base_get_iocp_(base))
        goto err;

    /* XXXX duplicate code */
    if (backlog > 0) {
        if (listen(fd, backlog) < 0)
            goto err;
    } else if (backlog < 0) {
        if (listen(fd, 128) < 0)//这里可以看到熟悉的监听接口listen.fd是之前我们创建的文件描叙符.通过128知道等待连接的最大长度是128.
            goto err;
    }
    if (getsockname(fd, (struct sockaddr*)&ss, &socklen)) {
        event_sock_warn(fd, "getsockname"); // 获取已创建好的套接字的本地名称
        goto err;
    }
    lev = mm_calloc(1, sizeof(struct evconnlistener_iocp)); //申请lev内存
    if (!lev) {
        event_warn("calloc");
        goto err;
    }
    lev->base.ops = &evconnlistener_iocp_ops;
    lev->base.cb = cb; // 将cb,用户的定义的回调赋给了申请的lev成员
    lev->base.user_data = ptr;
    lev->base.flags = flags;
    lev->base.refcnt = 1;
    lev->base.enabled = 1;

    lev->port = event_base_get_iocp_(base);
    lev->fd = fd; //将之前创建的fd,赋给了lev的成员
    lev->event_base = base;


    if (event_iocp_port_associate_(lev->port, fd, 1) < 0)
        goto err_free_lev;

    EVTHREAD_ALLOC_LOCK(lev->base.lock, EVTHREAD_LOCKTYPE_RECURSIVE);

    lev->n_accepting = N_SOCKETS_PER_LISTENER;
    lev->accepting = mm_calloc(lev->n_accepting,
        sizeof(struct accepting_socket *));
    if (!lev->accepting) {
        event_warn("calloc");
        goto err_delete_lock;
    }

   // 创建用于接收或者发送消息的套接字
for (i = 0; i < lev->n_accepting; ++i) { lev->accepting[i] = new_accepting_socket(lev, ss.ss_family); if (!lev->accepting[i]) { event_warnx("Couldn't create accepting socket"); goto err_free_accepting; } if (cb && start_accepting(lev->accepting[i]) < 0) { event_warnx("Couldn't start accepting on socket"); EnterCriticalSection(&lev->accepting[i]->lock); free_and_unlock_accepting_socket(lev->accepting[i]); goto err_free_accepting; } ++lev->base.refcnt; } iocp_listener_event_add(lev); return &lev->base; err_free_accepting: mm_free(lev->accepting); /* XXXX free the other elements. */ err_delete_lock: EVTHREAD_FREE_LOCK(lev->base.lock, EVTHREAD_LOCKTYPE_RECURSIVE); err_free_lev: mm_free(lev); err: /* Don't close the fd, it is caller's responsibility. */ return NULL; }

可以看到这部分代码用新接口:new_accepting_socket

static struct accepting_socket *
new_accepting_socket(struct evconnlistener_iocp *lev, int family)
{
    struct accepting_socket *res;
    int addrlen;
    int buflen;

    if (family == AF_INET)
        addrlen = sizeof(struct sockaddr_in);
    else if (family == AF_INET6)
        addrlen = sizeof(struct sockaddr_in6);
#ifdef EVENT__HAVE_AFUNIX_H
    else if (family == AF_UNIX && evutil_check_working_afunix_())
        addrlen = sizeof(struct sockaddr_un);
#endif
    else
        return NULL;
    buflen = (addrlen+16)*2;

    res = mm_calloc(1,sizeof(struct accepting_socket)-1+buflen);
    if (!res)
        return NULL;

    event_overlapped_init_(&res->overlapped, accepted_socket_cb);
    res->s = EVUTIL_INVALID_SOCKET;
    res->lev = lev; //lev,赋给了res的成员
    res->buflen = buflen;
    res->family = family;

    event_deferred_cb_init_(&res->deferred,
        event_base_get_npriorities(lev->event_base) / 2,
        accepted_socket_invoke_user_cb, res);

    InitializeCriticalSectionAndSpinCount(&res->lock, 1000);

    return res;
}

这里会看到event_deferred_cb_init接口

/**
   Initialize an empty, non-pending event_callback.

   @param deferred The struct event_callback structure to initialize. 待初始化的event_callback 结构体
   @param priority The priority that the callback should run at. callback的优先级
   @param cb The function to run when the struct event_callback executes. 执行event_callback时,需要调用的函数
   @param arg The function's second argument. 函数的第二个参数
 */
EVENT2_EXPORT_SYMBOL
void event_deferred_cb_init_(struct event_callback *, ev_uint8_t, deferred_cb_fn, void *);

在调用event_deferred_cb_init接口中可以看到,回调函数accepted_socket_invoke_user_cb

static void
accepted_socket_invoke_user_cb(struct event_callback *dcb, void *arg)
{
    struct accepting_socket *as = arg;

    struct sockaddr *sa_local=NULL, *sa_remote=NULL;
    int socklen_local=0, socklen_remote=0;
    const struct win32_extension_fns *ext = event_get_win32_extension_fns_();
    struct evconnlistener *lev = &as->lev->base;
    evutil_socket_t sock=-1;
    void *data;
    evconnlistener_cb cb=NULL; // 定义了一个evconnlistener_cb对象
    evconnlistener_errorcb errorcb=NULL;
    int error;

    EVUTIL_ASSERT(ext->GetAcceptExSockaddrs);

    LOCK(lev);
    EnterCriticalSection(&as->lock);
    if (as->free_on_cb) {
        free_and_unlock_accepting_socket(as);
        listener_decref_and_unlock(lev);
        return;
    }

    ++lev->refcnt;

    error = as->error;
    if (error) {
        as->error = 0;
        errorcb = lev->errorcb;
    } else {
        ext->GetAcceptExSockaddrs(
            as->addrbuf, 0, as->buflen/2, as->buflen/2,
            &sa_local, &socklen_local, &sa_remote,
            &socklen_remote);
        sock = as->s;
        cb = lev->cb; //lev的cb成员赋给了cb
        as->s = EVUTIL_INVALID_SOCKET;

        /* We need to call this so getsockname, getpeername, and
         * shutdown work correctly on the accepted socket. */
        /* XXXX handle error? */
        setsockopt(sock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
            (char *)&as->lev->fd, sizeof(&as->lev->fd));
    }
    data = lev->user_data;

    LeaveCriticalSection(&as->lock);
    UNLOCK(lev);

    if (errorcb) {
        WSASetLastError(error);
        errorcb(lev, data);
    } else if (cb) {
        cb(lev, sock, sa_remote, socklen_remote, data); //cb对应的参数
    }

    LOCK(lev);
    if (listener_decref_and_unlock(lev))
        return;

    EnterCriticalSection(&as->lock);
    start_accepting(as);
    LeaveCriticalSection(&as->lock);
}

sock:用于接收和发送消息的套接字

sa_remote: 客户端地址

socklen_remote: 客户端地址长度

data:客户端回复的消息

 

通过接口的说明知道,listener_cb是回调函数,用户自己编写,这里需要了解这个回调函数对应的参数。

static void
listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
    struct sockaddr *sa, int socklen, void *user_data)
{
    struct event_base *base = user_data;
    struct bufferevent *bev;

    bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    if (!bev) {
        fprintf(stderr, "Error constructing bufferevent!");
        event_base_loopbreak(base);
        return;
    }
    bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
    bufferevent_enable(bev, EV_WRITE);
    bufferevent_disable(bev, EV_READ);

    bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}

 

 

 

part2

而在上面的代码里可以看到一个新接口:bufferevent_socket_new

/**
  Create a new socket bufferevent over an existing socket.
在一个已存在的套接字的基础上,创建一个新的套接字bufferevent.
  @param base the event base to associate with the new bufferevent. 将和新的bufferevent绑定到一起的base
  @param fd the file descriptor from which data is read and written to. 文件描叙符,可以从它读取数据和写入数据
        This file descriptor is not allowed to be a pipe(2). 文件描叙符不允许设置为2
        It is safe to set the fd to -1, so long as you later
        set it with bufferevent_setfd or bufferevent_socket_connect().
文件描叙符可以设置为-1,只要在后面使用bufferevent_setfd或者bufferevent_socket_connect()来设置。 @param options Zero or more BEV_OPT_* flags 0或者多个WEV_OPT_ *类型的值 @return a pointer to a newly allocated bufferevent struct, or NULL if an error occurred
返回一个指针,该指针指向一个新分配的bufferevent结构体,或者NULL(当有错误发生时) @see bufferevent_free()
*/ EVENT2_EXPORT_SYMBOL struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int options);

 

posted @ 2020-03-04 17:41  N_zero  阅读(392)  评论(0)    收藏  举报