Libevent学习---evconnlistener使用和源码分析

来源:互联网 发布:war3无法连入网络 编辑:程序博客网 时间:2024/06/06 08:51

libevent封装了监听套接字,将套接字和struct event统一管理。

官方的解释是:

The evonnlistener mechanism gives you a way to listen for and accept incoming TCP connections

首先还是学习怎么去使用evconnlistener

(一)接口

//创建struct evconnlistener *evconnlistener_new(struct event_base*base,evconnlistener_cb cb,void *ptr,unsigned flags ,int backlog,evutil_socket_t fd);struct evconnlistener*evconnlistener_new_bind(struct event_base*base,evconnlistener_cb cb,void *ptr,unsigned flags,int backlog,const struct sockaddr*sa,int socklen);void evconnlistener_free(struct evconnlistener*lev)//其中callback的定义如下:typedef void (*evconnlistener_cb)(struct evconnlistener *listener,evutil_socket_t sock,        struct sockaddr*addr,int len,void *ptr);//enable 和 disableint evconnlistener_disable(struct evconnlistener *lev);int evconnlistener_enable(struct evconnlistener *lev);//调整callbackvoid evconnlistener_set_cb(struct evconnlistener*lev,evconnlistener_cb cb,void *arg)//检查evconnlistenerevutil_socket_t evconnlistener_get_fd(struct evconnlistener *lev)struct event_base *evconnlistener_get_base(struct evconnlistener*lev)

evconnlistener是对套接字和event的封装,可以想见,在这些接口中,除了调用socket api之外,还会关联套接子到event上进而注册到event_base上去

不过接口封装好了这些行为,只需要了解这些参数的含义即可。

base:即event_base
cb:当有新连接时调用的回调函数
ptr:callback的参数
flags:控制listner的行为
backlog:同 listen(2)中backlog的含义
fd:已经bind的fd,传入api让libevent做listen调用

(二)例子

使用evconnlistener管理监听套接字构建一个简单的echo server
代码也可参考gayhub

#include <event2/listener.h>#include <event2/bufferevent.h>#include <event2/buffer.h>#include <arpa/inet.h>#include <string.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <assert.h>static void echo_read_cb(struct bufferevent*bev,void*ctx){    struct evbuffer*input=bufferevent_get_input(bev);    struct evbuffer*output=bufferevent_get_output(bev);    evbuffer_add_buffer(output,input);}static voidecho_event_cb(struct bufferevent*bev,short events,void*ctx){    if(events&BEV_EVENT_ERROR)    perror("Error from bufferevent");    if(events&(BEV_EVENT_EOF|BEV_EVENT_ERROR))    {    bufferevent_free(bev);    }}static voidaccept_error_cb(struct evconnlistener*listener,void*ctx){    struct event_base*base=evconnlistener_get_base(listener);    event_base_loopexit(base,NULL);}void accept_conn_cb(struct evconnlistener*listener,evutil_socket_t fd,struct sockaddr*address,int socklen,void*ctx){    struct event_base*base=evconnlistener_get_base(listener);    struct bufferevent*bev = bufferevent_socket_new(base,        fd,        BEV_OPT_CLOSE_ON_FREE);    bufferevent_setcb(bev,echo_read_cb,NULL,echo_event_cb,NULL);    bufferevent_enable(bev,EV_READ|EV_WRITE);}int main(int argc,char**argv){    struct event_base*base;    struct evconnlistener *listener;    struct sockaddr_in sin;    int port = 1025;    base = event_base_new();    assert(base!=NULL);    memset(&sin,0,sizeof(sin));    sin.sin_family=AF_INET;    sin.sin_addr.s_addr=htonl(0);    sin.sin_port=htons(port);    listener=evconnlistener_new_bind(base,//event_base        accept_conn_cb,//cb        NULL,// ptr        LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,//flag        -1,//backlog        (struct sockaddr*)&sin,        sizeof(sin));    assert(listener!=NULL);    evconnlistener_set_error_cb(listener,accept_error_cb);    event_base_dispatch(base);    return 0;}

(三)源码

看看evconnlistenr做了哪些封装

3.1结构体

struct evconnlistener {    const struct evconnlistener_ops *ops; //一些回调函数    void *lock;    evconnlistener_cb cb;//    evconnlistener_errorcb errorcb;    void *user_data;//    unsigned flags;    short refcnt;    unsigned enabled : 1;};struct evconnlistener_event {    struct evconnlistener base;    struct event listener;};

evconnlistener结构体将一些基本的回调函数、用户数据等封装,然后evconnlistener_event将这些东西和struct event封装在一起

3.2创建一个evconnlistener

看一下初始化有哪些内容:

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;    if (backlog > 0) {        if (listen(fd, backlog) < 0)//调用listen            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是一个evconnlistener结构体    lev->base.ops = &evconnlistener_event_ops;//设置ops参数    lev->base.cb = cb;//设置回调    lev->base.user_data = ptr;//ptr    lev->base.flags = flags;//flag    lev->base.refcnt = 1;    if (flags & LEV_OPT_THREADSAFE) {        EVTHREAD_ALLOC_LOCK(lev->base.lock, EVTHREAD_LOCKTYPE_RECURSIVE);    }    //将fd关联到struct event上去 lev->listener是struct event结构体    //这样assigh将listener_read_cb设置到event的cb中,并将struct evconnlistner做为参数    //这是一个比较复杂的过程,首先由于event激活,将先调用listener_read_cb    //在listener_read_cb通过传入的struct evconnlistner调用用户自定义回调    event_assign(&lev->listener, base, fd, EV_READ|EV_PERSIST,        listener_read_cb, lev);        //这里底层调用event_enable    evconnlistener_enable(&lev->base);    return &lev->base;}

在另外一个接口evconnlistener_new_bind中由于指定了套接字结构体,因此在底层还调用了bind

3.3 listener_read_cb

这个底层的回调函数先于用户回调函数调用,在这个回调中会调用用户回调,并且会将已连接的套接字作为参数传入用户回调,
这也正是evconnlistener的精髓,无需要用户进行listen、bind、accept等操作

static voidlistener_read_cb(evutil_socket_t fd, short what, void *p){    struct evconnlistener *lev = p;    int err;    evconnlistener_cb cb;    evconnlistener_errorcb errorcb;    void *user_data;    LOCK(lev);    while (1) {        struct sockaddr_storage ss;        socklen_t socklen = sizeof(ss);        evutil_socket_t new_fd = accept(fd, (struct sockaddr*)&ss, &socklen);//accept        if (new_fd < 0)            break;        if (socklen == 0) {            /* This can happen with some older linux kernels in             * response to nmap. */            evutil_closesocket(new_fd);            continue;        }        if (!(lev->flags & LEV_OPT_LEAVE_SOCKETS_BLOCKING))            evutil_make_socket_nonblocking(new_fd);//non blocking        if (lev->cb == NULL) {            UNLOCK(lev);            return;        }        ++lev->refcnt;        cb = lev->cb;//拿到用户回调        user_data = lev->user_data;        UNLOCK(lev);        cb(lev, new_fd, (struct sockaddr*)&ss, (int)socklen,            user_data);//调用用户回调并将connfd传入用户回调        LOCK(lev);        if (lev->refcnt == 1) {            int freed = listener_decref_and_unlock(lev);            EVUTIL_ASSERT(freed);            return;        }        --lev->refcnt;    }}

(四)参考

1.libevent programming
2.libevent 深入浅出

1 0
原创粉丝点击