Libevent学习---evconnlistener使用和源码分析
2017-05-03 15:20
567 查看
libevent封装了监听套接字,将套接字和
官方的解释是:
The evonnlistener mechanism gives you a way to listen for and accept incoming TCP connections
首先还是学习怎么去使用
不过接口封装好了这些行为,只需要了解这些参数的含义即可。
base:即event_base
cb:当有新连接时调用的回调函数
ptr:callback的参数
flags:控制listner的行为
backlog:同 listen(2)中backlog的含义
fd:已经bind的fd,传入api让libevent做listen调用
代码也可参考gayhub
在另外一个接口
这也正是
2.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 programming2.libevent 深入浅出
相关文章推荐
- Libevent源码分析-----连接监听器evconnlistener
- Volley简单学习使用五—— 源码分析三
- 开源中国源码学习UI篇(二)之NavigationDrawer+Fragment的使用分析
- 一步步学习 Mybatis:缓存的使用及源码分析
- java学习之旅56--数组_StringBuilder和StringBuffer的使用_常用方法_方法链的实现_JDK源码分析
- AIDL使用学习(三):源码深入分析
- libevent源码学习-----事件驱动流程分析
- Libevent源码分析—从使用Libevent库开始
- java学习之旅65--常用类_Date类的使用_JDk源码分析
- 通过ArrayList迭代器使用进行JDK源码分析学习迭代器
- Google Test(GTest)使用方法和源码解析——Listener技术分析和应用
- libevent源码学习----io多路复用的封装和使用
- Adroid学习之 从源码角度分析-禁止使用回退按钮方案
- 开源中国源码学习UI篇(一)之FragmentTabHost的使用分析
- Android之使用NotificationListenerService使得自己的应用不被杀及其源码分析
- libevent源码分析:listener
- 一步步学习 MyBatis:缓存的使用及源码分析
- libevent源码分析-介绍、安装、使用
- Spring IOC学习心得之源码级分析ContextLoaderListener的作用(IOC容器初始化入口)
- Android开发学习之路-LruCache使用和源码分析