您的位置:首页 > 其它

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

2017-05-03 15:20 567 查看
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 和 disable

int evconnlistener_disable(struct evconnlistener *lev);
int evconnlistener_enable(struct evconnlistener *lev);

//调整callback
void evconnlistener_set_cb(struct evconnlistener*lev,evconnlistener_cb cb,void *arg)

//检查evconnlistener
evutil_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 void
echo_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 void
accept_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 void
listener_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 深入浅出
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: