libevent绑定、监听和读写数据

作者: cpp加油站 | 来源:发表于2019-08-16 10:27 被阅读7次

1. 绑定和监听

在上一篇文章中,以epoll为例说到了事件机制,会按顺序调用init和dispatch这两个回调函数,但是,我们回忆一下网络编程的过程,首先是需要创建socket、绑定socket、监听socket的,但目前为止还并没有涉及到,再去看源代码,会发现里面有listener.c,这个文件里面就会去做创建socket的过程。

看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;

    if (backlog == 0)
        return NULL;

    if (flags & LEV_OPT_CLOSE_ON_EXEC)
        socktype |= EVUTIL_SOCK_CLOEXEC;

    //调用socket函数
    fd = evutil_socket_(family, socktype, 0);
    if (fd == -1)
        return NULL;

    //设置存货检测
    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;
    }

    //调用bind函数
    if (sa) {
        if (bind(fd, sa, socklen)<0)
            goto err;
    }

    //evconnlistener_new函数里面会调用listen函数
    listener = evconnlistener_new(base, cb, ptr, flags, backlog, fd);
    if (!listener)
        goto err;

    return listener;
err:
    evutil_closesocket(fd);
    return NULL;
}

上面的代码我加了注释,说的很清楚,从创建、绑定、设置属性一直到监听整个都调用了,这里不再多说。

evconnlistener_new函数不只会调用listen,还会注册一个监听回调函数,如下:

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

    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;
}

evconnlistener_cb回调函数声明如下:

typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);

该回调函数需要我们自己实现,看sample目录中hello-world.c中实现的该函数代码如下:

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));
}

2. 读写数据

libevent是基于事件的,它的很多动作都是调用事先注册好的回调函数来解决的,读写数据也不例外。

看上面第一节中,监听回调函数里面使用了bufferevent_setcb,这个函数会注册读写事件的回调函数,如下:

void
bufferevent_setcb(struct bufferevent *bufev,
    bufferevent_data_cb readcb, bufferevent_data_cb writecb,
    bufferevent_event_cb eventcb, void *cbarg)
{
    BEV_LOCK(bufev);

    bufev->readcb = readcb;
    bufev->writecb = writecb;
    bufev->errorcb = eventcb;

    bufev->cbarg = cbarg;
    BEV_UNLOCK(bufev);
}

当有可读事件时会调用readcb函数,当有可写事件时调用writecb函数,发生错误时调用eventcb函数。
bufferevent_data_cb声明如下:

typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);

还是看看sample目录中hello-world.c中对该函数的定义,如下:

static void
conn_writecb(struct bufferevent *bev, void *user_data)
{
    struct evbuffer *output = bufferevent_get_output(bev);
    if (evbuffer_get_length(output) == 0) {
        printf("flushed answer\n");
        bufferevent_free(bev);
    }
}

我们自己使用时可以参照sample目录中的例子,这里就不再细说了。

相关文章

  • libevent绑定、监听和读写数据

    1. 绑定和监听 在上一篇文章中,以epoll为例说到了事件机制,会按顺序调用init和dispatch这两个回调...

  • 53-Vue-watch属性

    这里主要实现监听数据变化进行某些操作,两种方法对比 一.监听键盘事件 双向数据绑定 添加数据 绑定键盘监听事件 添...

  • Vue-CLI、Vue-Router知识点复习

    1.MVVM Model-View-ViewModel数据-展示的绑定,ViewModel监听数据的改变和控制,处...

  • Android 使用ViewModel,LiveData高效、简

    一、思路 ViewModel作为View监听器和View进行绑定,LiveData数据存放在ViewModel数据...

  • Vue 简单语法

    动态绑定数据message是动态的 判断语句 循环语句 事件监听 UI与数据双向绑定 数据只绑定一次,后续数据改变...

  • Vue指令汇总

    数据绑定 属性绑定 判断条件 循环 事件监听 HTML的引入 组件的使用 组件数据的绑定 防止属性被修改——使响应...

  • Vue - v-model原理

    v-model相当于一个自定义指令,通过事件监听和属性绑定实现双向数据绑定。 使用:value绑定msg到inpu...

  • angular2应用用户输入

    用户输入触发 DOM 事件。我们通过事件绑定来监听它们,把更新过的数据导入回我们的组件和 model 。(1)绑定...

  • Vue.js学习笔记(6)关于资源使用

    获取本地图片两种方式assets、static中使用资源方式 双向数据绑定与监听 数据整体监听与计算

  • 2019-06-29

    vue的特点 Vue 扩展插件 vue的基本使用 模板语法 双大括号表达式 强制数据绑定 绑定事件监听 计算属性和...

网友评论

    本文标题:libevent绑定、监听和读写数据

    本文链接:https://www.haomeiwen.com/subject/oqcfsctx.html