出处:http://blog.csdn.net/luotuo44/article/details/38800363

使用evconnlistener:

基于event和event_base已经可以写一个CS模型了。但是对于服务器端来说,仍然需要用户自行调用socket、bind、listen、accept等步骤。这个过程有点繁琐,为此在2.0.2-alpha版本的Libevent推出了一些对应的封装函数。

用户只需初始化struct sockaddr_in结构体变量,然后把它作为参数传给函数evconnlistener_new_bind即可。该函数会完成上面说到的那4个过程。下面的代码是一个使用例子。

[cpp] view plaincopy print?
  1. #include<netinet/in.h>
  2. #include<sys/socket.h>
  3. #include<unistd.h>
  4. #include<stdio.h>
  5. #include<string.h>
  6. #include<event.h>
  7. #include<listener.h>
  8. #include<bufferevent.h>
  9. #include<thread.h>
  10. void listener_cb(evconnlistener *listener, evutil_socket_t fd,
  11. struct sockaddr *sock, int socklen, void *arg);
  12. void socket_read_cb(bufferevent *bev, void *arg);
  13. void socket_error_cb(bufferevent *bev, short events, void *arg);
  14. int main()
  15. {
  16. evthread_use_pthreads();//enable threads
  17. struct sockaddr_in sin;
  18. memset(&sin, 0, sizeof(struct sockaddr_in));
  19. sin.sin_family = AF_INET;
  20. sin.sin_port = htons(8989);
  21. event_base *base = event_base_new();
  22. evconnlistener *listener
  23. = evconnlistener_new_bind(base, listener_cb, base,
  24. LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE | LEV_OPT_THREADSAFE,
  25. 10, (struct sockaddr*)&sin,
  26. sizeof(struct sockaddr_in));
  27. event_base_dispatch(base);
  28. evconnlistener_free(listener);
  29. event_base_free(base);
  30. return 0;
  31. }
  32. //有新的客户端连接到服务器
  33. //当此函数被调用时,libevent已经帮我们accept了这个客户端。该客户端的
  34. //文件描述符为fd
  35. void listener_cb(evconnlistener *listener, evutil_socket_t fd,
  36. struct sockaddr *sock, int socklen, void *arg)
  37. {
  38. event_base *base = (event_base*)arg;
  39. //下面代码是为这个fd创建一个bufferevent
  40. bufferevent *bev =  bufferevent_socket_new(base, fd,
  41. BEV_OPT_CLOSE_ON_FREE);
  42. bufferevent_setcb(bev, socket_read_cb, NULL, socket_error_cb, NULL);
  43. bufferevent_enable(bev, EV_READ | EV_PERSIST);
  44. }
  45. void socket_read_cb(bufferevent *bev, void *arg)
  46. {
  47. char msg[4096];
  48. size_t len = bufferevent_read(bev, msg, sizeof(msg)-1 );
  49. msg[len] = '\0';
  50. printf("server read the data %s\n", msg);
  51. char reply[] = "I has read your data";
  52. bufferevent_write(bev, reply, strlen(reply) );
  53. }
  54. void socket_error_cb(bufferevent *bev, short events, void *arg)
  55. {
  56. if (events & BEV_EVENT_EOF)
  57. printf("connection closed\n");
  58. else if (events & BEV_EVENT_ERROR)
  59. printf("some other error\n");
  60. //这将自动close套接字和free读写缓冲区
  61. bufferevent_free(bev);
  62. }
#include<netinet/in.h>
#include<sys/socket.h>
#include<unistd.h>#include<stdio.h>
#include<string.h>#include<event.h>
#include<listener.h>
#include<bufferevent.h>
#include<thread.h>void listener_cb(evconnlistener *listener, evutil_socket_t fd,struct sockaddr *sock, int socklen, void *arg);void socket_read_cb(bufferevent *bev, void *arg);
void socket_error_cb(bufferevent *bev, short events, void *arg);int main()
{evthread_use_pthreads();//enable threadsstruct sockaddr_in sin;memset(&sin, 0, sizeof(struct sockaddr_in));sin.sin_family = AF_INET;sin.sin_port = htons(8989);event_base *base = event_base_new();evconnlistener *listener= evconnlistener_new_bind(base, listener_cb, base,LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE | LEV_OPT_THREADSAFE,10, (struct sockaddr*)&sin,sizeof(struct sockaddr_in));event_base_dispatch(base);evconnlistener_free(listener);event_base_free(base);return 0;
}//有新的客户端连接到服务器
//当此函数被调用时,libevent已经帮我们accept了这个客户端。该客户端的
//文件描述符为fd
void listener_cb(evconnlistener *listener, evutil_socket_t fd,struct sockaddr *sock, int socklen, void *arg)
{event_base *base = (event_base*)arg;//下面代码是为这个fd创建一个buffereventbufferevent *bev =  bufferevent_socket_new(base, fd,BEV_OPT_CLOSE_ON_FREE);bufferevent_setcb(bev, socket_read_cb, NULL, socket_error_cb, NULL);bufferevent_enable(bev, EV_READ | EV_PERSIST);
}void socket_read_cb(bufferevent *bev, void *arg)
{char msg[4096];size_t len = bufferevent_read(bev, msg, sizeof(msg)-1 );msg[len] = '\0';printf("server read the data %s\n", msg);char reply[] = "I has read your data";bufferevent_write(bev, reply, strlen(reply) );
}void socket_error_cb(bufferevent *bev, short events, void *arg)
{if (events & BEV_EVENT_EOF)printf("connection closed\n");else if (events & BEV_EVENT_ERROR)printf("some other error\n");//这将自动close套接字和free读写缓冲区bufferevent_free(bev);
}

上面的代码是一个服务器端的例子,客户端代码可以使用《Libevent使用例子,从简单到复杂》博文中的客户端。这里就不贴客户端代码了。

从上面代码可以看到,当服务器端监听到一个客户端的连接请求后,就会调用listener_cb这个回调函数。这个回调函数是在evconnlistener_new_bind函数中设置的。现在来看一下这个函数的参数有哪些,下面是其函数原型。

[cpp] view plaincopy print?
  1. //listener.h文件
  2. typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);
  3. struct evconnlistener *evconnlistener_new_bind(struct event_base *base,
  4. evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
  5. const struct sockaddr *sa, int socklen);
//listener.h文件
typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);struct evconnlistener *evconnlistener_new_bind(struct event_base *base,evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,const struct sockaddr *sa, int socklen);

第一个参数是很熟悉的event_base,无论怎么样都是离不开event_base这个发动机的。

第二个参数是一个函数指针,该函数指针的格式如代码所示。当有新的客户端请求连接时,该函数就会调用。要注意的是:当这个回调函数被调用时,Libevent已经帮我们accept了这个客户端。所以,该回调函数有一个参数是文件描述符fd。我们直接使用这个fd即可。真是方便。这个参数是可以为NULL的,此时用户并不能接收到客户端。当用户调用evconnlistener_set_cb函数设置回调函数后,就可以了。

第三个参数是传给回调函数的用户参数,作用就像event_new函数的最后一个参数。

参数flags是一些标志值,有下面这些:

  • LEV_OPT_LEAVE_SOCKETS_BLOCKING:默认情况下,当连接监听器接收到新的客户端socket连接后,会把该socket设置为非阻塞的。如果设置该选项,那么就把之客户端socket保留为阻塞的
  • LEV_OPT_CLOSE_ON_FREE:当连接监听器释放时,会自动关闭底层的socket
  • LEV_OPT_CLOSE_ON_EXEC:为底层的socket设置close-on-exec标志
  • LEV_OPT_REUSEABLE: 在某些平台,默认情况下当一个监听socket被关闭时,其他socket不能马上绑定到同一个端口,要等一会儿才行。设置该标志后,Libevent会把该socket设置成reuseable。这样,关闭该socket后,其他socket就能马上使用同一个端口
  • LEV_OPT_THREADSAFE:为连接监听器分配锁。这样可以确保线程安全

参数backlog是系统调用listen的第二个参数。最后两个参数就不多说了。

evconnlistener的封装:

接下来看一下Libevent是怎么封装evconnlistener的。

用到的结构体:

[cpp] view plaincopy print?
  1. //listener.c文件
  2. struct evconnlistener_ops {//一系列的工作函数
  3. int (*enable)(struct evconnlistener *);
  4. int (*disable)(struct evconnlistener *);
  5. void (*destroy)(struct evconnlistener *);
  6. void (*shutdown)(struct evconnlistener *);
  7. evutil_socket_t (*getfd)(struct evconnlistener *);
  8. struct event_base *(*getbase)(struct evconnlistener *);
  9. };
  10. struct evconnlistener {
  11. const struct evconnlistener_ops *ops;//操作函数
  12. void *lock; //锁变量,用于线程安全
  13. evconnlistener_cb cb;//用户的回调函数
  14. evconnlistener_errorcb errorcb;//发生错误时的回调函数
  15. void *user_data;//回调函数的参数
  16. unsigned flags;//属性标志
  17. short refcnt;//引用计数
  18. unsigned enabled : 1;//位域为1.即只需一个比特位来存储这个成员
  19. };
  20. struct evconnlistener_event {
  21. struct evconnlistener base;
  22. struct event listener; //内部event,插入到event_base
  23. };
//listener.c文件
struct evconnlistener_ops {//一系列的工作函数int (*enable)(struct evconnlistener *);int (*disable)(struct evconnlistener *);void (*destroy)(struct evconnlistener *);void (*shutdown)(struct evconnlistener *);evutil_socket_t (*getfd)(struct evconnlistener *);struct event_base *(*getbase)(struct evconnlistener *);
};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;//位域为1.即只需一个比特位来存储这个成员
};struct evconnlistener_event {struct evconnlistener base;struct event listener; //内部event,插入到event_base
};

在evconnlistener_event结构体有一个event结构体。可以想象,在实现时必然是将服务器端的socket fd赋值给struct event 类型变量listener的fd成员。然后将listener加入到event_base,这样就完成了自动监听工作。这也回归到之前学过的内容。

下面看一下具体是怎么实现的。

初始化服务器socket:

[cpp] view plaincopy print?
  1. //listener.c文件
  2. struct evconnlistener *
  3. evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb,
  4. void *ptr, unsigned flags, int backlog, const struct sockaddr *sa,
  5. int socklen)
  6. {
  7. struct evconnlistener *listener;
  8. evutil_socket_t fd;
  9. int on = 1;
  10. int family = sa ? sa->sa_family : AF_UNSPEC;
  11. //监听个数不能为0
  12. if (backlog == 0)
  13. return NULL;
  14. fd = socket(family, SOCK_STREAM, 0);
  15. if (fd == -1)
  16. return NULL;
  17. //LEV_OPT_LEAVE_SOCKETS_BLOCKING选项是应用于accept到的客户端socket
  18. //所以对于服务器端的socket,直接将之设置为非阻塞的
  19. if (evutil_make_socket_nonblocking(fd) < 0) {
  20. evutil_closesocket(fd);
  21. return NULL;
  22. }
  23. if (flags & LEV_OPT_CLOSE_ON_EXEC) {
  24. if (evutil_make_socket_closeonexec(fd) < 0) {
  25. evutil_closesocket(fd);
  26. return NULL;
  27. }
  28. }
  29. if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on))<0) {
  30. evutil_closesocket(fd);
  31. return NULL;
  32. }
  33. if (flags & LEV_OPT_REUSEABLE) {
  34. if (evutil_make_listen_socket_reuseable(fd) < 0) {
  35. evutil_closesocket(fd);
  36. return NULL;
  37. }
  38. }
  39. if (sa) {
  40. if (bind(fd, sa, socklen)<0) {//绑定
  41. evutil_closesocket(fd);
  42. return NULL;
  43. }
  44. }
  45. listener = evconnlistener_new(base, cb, ptr, flags, backlog, fd);
  46. if (!listener) {
  47. evutil_closesocket(fd);
  48. return NULL;
  49. }
  50. return listener;
  51. }
//listener.c文件
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;//监听个数不能为0if (backlog == 0)return NULL;fd = socket(family, SOCK_STREAM, 0);if (fd == -1)return NULL;//LEV_OPT_LEAVE_SOCKETS_BLOCKING选项是应用于accept到的客户端socket//所以对于服务器端的socket,直接将之设置为非阻塞的if (evutil_make_socket_nonblocking(fd) < 0) {evutil_closesocket(fd);return NULL;}if (flags & LEV_OPT_CLOSE_ON_EXEC) {if (evutil_make_socket_closeonexec(fd) < 0) {evutil_closesocket(fd);return NULL;}}if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on))<0) {evutil_closesocket(fd);return NULL;}if (flags & LEV_OPT_REUSEABLE) {if (evutil_make_listen_socket_reuseable(fd) < 0) {evutil_closesocket(fd);return NULL;}}if (sa) {if (bind(fd, sa, socklen)<0) {//绑定evutil_closesocket(fd);return NULL;}}listener = evconnlistener_new(base, cb, ptr, flags, backlog, fd);if (!listener) {evutil_closesocket(fd);return NULL;}return listener;
}

evconnlistener_new_bind函数申请一个socket,然后对之进行一些有关非阻塞、重用、保持连接的处理、绑定到特定的IP和端口。最后把业务逻辑交给evconnlistener_new处理。

[cpp] view plaincopy print?
  1. //listener.c文件
  2. static const struct evconnlistener_ops evconnlistener_event_ops = {
  3. event_listener_enable,
  4. event_listener_disable,
  5. event_listener_destroy,
  6. NULL, /* shutdown */
  7. event_listener_getfd,
  8. event_listener_getbase
  9. };
  10. struct evconnlistener *
  11. evconnlistener_new(struct event_base *base,
  12. evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
  13. evutil_socket_t fd)
  14. {
  15. struct evconnlistener_event *lev;
  16. if (backlog > 0) {
  17. if (listen(fd, backlog) < 0)
  18. return NULL;
  19. } else if (backlog < 0) {
  20. if (listen(fd, 128) < 0)
  21. return NULL;
  22. }
  23. lev = mm_calloc(1, sizeof(struct evconnlistener_event));
  24. if (!lev)
  25. return NULL;
  26. //赋值
  27. lev->base.ops = &evconnlistener_event_ops;
  28. lev->base.cb = cb;
  29. lev->base.user_data = ptr;
  30. lev->base.flags = flags;
  31. lev->base.refcnt = 1;
  32. if (flags & LEV_OPT_THREADSAFE) {//线程安全就需要分配锁
  33. EVTHREAD_ALLOC_LOCK(lev->base.lock, EVTHREAD_LOCKTYPE_RECURSIVE);
  34. }
  35. //在多路IO复用函数中,新客户端的连接请求也被当作读事件
  36. event_assign(&lev->listener, base, fd, EV_READ|EV_PERSIST,
  37. listener_read_cb, lev);
  38. //会调用event_add,把event加入到event_base中
  39. evconnlistener_enable(&lev->base);
  40. return &lev->base;
  41. }
  42. int
  43. evconnlistener_enable(struct evconnlistener *lev)
  44. {
  45. int r;
  46. LOCK(lev);
  47. lev->enabled = 1;
  48. if (lev->cb)
  49. r = lev->ops->enable(lev);//实际上是调用下面的event_listener_enable函数
  50. else
  51. r = 0;
  52. UNLOCK(lev);
  53. return r;
  54. }
  55. static int
  56. event_listener_enable(struct evconnlistener *lev)
  57. {
  58. struct evconnlistener_event *lev_e =
  59. EVUTIL_UPCAST(lev, struct evconnlistener_event, base);
  60. //加入到event_base,完成监听工作。
  61. return event_add(&lev_e->listener, NULL);
  62. }
//listener.c文件
static const struct evconnlistener_ops evconnlistener_event_ops = {event_listener_enable,event_listener_disable,event_listener_destroy,NULL, /* shutdown */event_listener_getfd,event_listener_getbase
};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)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;if (flags & LEV_OPT_THREADSAFE) {//线程安全就需要分配锁EVTHREAD_ALLOC_LOCK(lev->base.lock, EVTHREAD_LOCKTYPE_RECURSIVE);}//在多路IO复用函数中,新客户端的连接请求也被当作读事件event_assign(&lev->listener, base, fd, EV_READ|EV_PERSIST,listener_read_cb, lev);//会调用event_add,把event加入到event_base中evconnlistener_enable(&lev->base);return &lev->base;
}int
evconnlistener_enable(struct evconnlistener *lev)
{int r;LOCK(lev);lev->enabled = 1;if (lev->cb)r = lev->ops->enable(lev);//实际上是调用下面的event_listener_enable函数elser = 0;UNLOCK(lev);return r;
}static int
event_listener_enable(struct evconnlistener *lev)
{struct evconnlistener_event *lev_e =EVUTIL_UPCAST(lev, struct evconnlistener_event, base);//加入到event_base,完成监听工作。return event_add(&lev_e->listener, NULL);
}

几个函数的一路调用,思路还是挺清晰的。就是申请一个socket,进行一些处理,然后用之赋值给event。最后把之add到event_base中。event_base会对新客户端的请求连接进行监听。

在evconnlistener_enable函数里面,如果用户没有设置回调函数,那么就不会调用event_listener_enable。也就是说并不会add到event_base中。

event_listener_enable函数里面的宏EVUTIL_UPCAST可以根据结构体成员变量的地址推算出结构体的起始地址。有关这个宏,可以查看”结构体偏移量”。

处理客户端的连接请求:

现在来看一下event的回调函数listener_read_cb。

[cpp] view plaincopy print?
  1. //listener.c文件
  2. static void
  3. listener_read_cb(evutil_socket_t fd, short what, void *p)
  4. {
  5. struct evconnlistener *lev = p;
  6. int err;
  7. evconnlistener_cb cb;
  8. evconnlistener_errorcb errorcb;
  9. void *user_data;
  10. LOCK(lev);
  11. while (1) { //可能有多个客户端同时请求连接
  12. struct sockaddr_storage ss;
  13. #ifdef WIN32
  14. int socklen = sizeof(ss);
  15. #else
  16. socklen_t socklen = sizeof(ss);
  17. #endif
  18. evutil_socket_t new_fd = accept(fd, (struct sockaddr*)&ss, &socklen);
  19. if (new_fd < 0)
  20. break;
  21. if (socklen == 0) {
  22. /* This can happen with some older linux kernels in
  23. * response to nmap. */
  24. evutil_closesocket(new_fd);
  25. continue;
  26. }
  27. if (!(lev->flags & LEV_OPT_LEAVE_SOCKETS_BLOCKING))
  28. evutil_make_socket_nonblocking(new_fd);
  29. //用户还没设置连接监听器的回调函数
  30. if (lev->cb == NULL) {
  31. UNLOCK(lev);
  32. return;
  33. }
  34. //由于refcnt被初始化为1.这里有++了,所以一般情况下并不会进入下面的
  35. //if判断里面。但如果程在下面UNLOCK之后,第二个线调用evconnlistener_free
  36. //释放这个evconnlistener时,就有可能使得refcnt为1了。即进入那个判断体里
  37. //执行listener_decref_and_unlock。在下面会讨论这个问题。
  38. ++lev->refcnt;
  39. cb = lev->cb;
  40. user_data = lev->user_data;
  41. UNLOCK(lev);
  42. cb(lev, new_fd, (struct sockaddr*)&ss, (int)socklen,
  43. user_data);//调用用户设置的回调函数,让用户处理这个fd
  44. LOCK(lev);
  45. if (lev->refcnt == 1) {
  46. int freed = listener_decref_and_unlock(lev);
  47. EVUTIL_ASSERT(freed);
  48. return;
  49. }
  50. --lev->refcnt;
  51. }
  52. err = evutil_socket_geterror(fd);
  53. if (EVUTIL_ERR_ACCEPT_RETRIABLE(err)) {//还可以accept
  54. UNLOCK(lev);
  55. return;
  56. }
  57. //当有错误发生时才会运行到这里
  58. if (lev->errorcb != NULL) {
  59. ++lev->refcnt;
  60. errorcb = lev->errorcb;
  61. user_data = lev->user_data;
  62. UNLOCK(lev);
  63. errorcb(lev, user_data);//调用用户设置的错误回调函数
  64. LOCK(lev);
  65. listener_decref_and_unlock(lev);
  66. }
  67. }
//listener.c文件
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;
#ifdef WIN32int socklen = sizeof(ss);
#elsesocklen_t socklen = sizeof(ss);
#endifevutil_socket_t new_fd = accept(fd, (struct sockaddr*)&ss, &socklen);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);//用户还没设置连接监听器的回调函数if (lev->cb == NULL) {UNLOCK(lev);return;}//由于refcnt被初始化为1.这里有++了,所以一般情况下并不会进入下面的//if判断里面。但如果程在下面UNLOCK之后,第二个线调用evconnlistener_free//释放这个evconnlistener时,就有可能使得refcnt为1了。即进入那个判断体里//执行listener_decref_and_unlock。在下面会讨论这个问题。++lev->refcnt;cb = lev->cb;user_data = lev->user_data;UNLOCK(lev);cb(lev, new_fd, (struct sockaddr*)&ss, (int)socklen,user_data);//调用用户设置的回调函数,让用户处理这个fdLOCK(lev);if (lev->refcnt == 1) {int freed = listener_decref_and_unlock(lev);EVUTIL_ASSERT(freed);return;}--lev->refcnt;}err = evutil_socket_geterror(fd);if (EVUTIL_ERR_ACCEPT_RETRIABLE(err)) {//还可以acceptUNLOCK(lev);return;}//当有错误发生时才会运行到这里if (lev->errorcb != NULL) {++lev->refcnt;errorcb = lev->errorcb;user_data = lev->user_data;UNLOCK(lev);errorcb(lev, user_data);//调用用户设置的错误回调函数LOCK(lev);listener_decref_and_unlock(lev);}
}

这个函数所做的工作也比较简单,就是accept客户端,然后调用用户设置的回调函数。所以,用户回调函数的参数fd是一个已经连接好了的socket。

上面函数说到了错误回调函数,可以通过下面的函数设置连接监听器的错误监听函数。

[cpp] view plaincopy print?
  1. //listener.h文件
  2. typedef void (*evconnlistener_errorcb)(struct evconnlistener *, void *);
  3. //listener.c文件
  4. void
  5. evconnlistener_set_error_cb(struct evconnlistener *lev,
  6. evconnlistener_errorcb errorcb)
  7. {
  8. LOCK(lev);
  9. lev->errorcb = errorcb;
  10. UNLOCK(lev);
  11. }
//listener.h文件
typedef void (*evconnlistener_errorcb)(struct evconnlistener *, void *);//listener.c文件
void
evconnlistener_set_error_cb(struct evconnlistener *lev,evconnlistener_errorcb errorcb)
{LOCK(lev);lev->errorcb = errorcb;UNLOCK(lev);
}

释放evconnlistener:

调用evconnlistener_free可以释放一个evconnlistener。由于evconnlistener拥有一些系统资源,在释放evconnlistener_free的时候会释放这些系统资源。

[cpp] view plaincopy print?
  1. //listener.c文件
  2. void
  3. evconnlistener_free(struct evconnlistener *lev)
  4. {
  5. LOCK(lev);
  6. lev->cb = NULL;
  7. lev->errorcb = NULL;
  8. if (lev->ops->shutdown)//这里的shutdown为NULL
  9. lev->ops->shutdown(lev);
  10. //引用次数减一,并解锁
  11. listener_decref_and_unlock(lev);
  12. }
  13. static int
  14. listener_decref_and_unlock(struct evconnlistener *listener)
  15. {
  16. int refcnt = --listener->refcnt;
  17. if (refcnt == 0) {
  18. //实际调用event_listener_destroy
  19. listener->ops->destroy(listener);
  20. UNLOCK(listener);
  21. //释放锁
  22. EVTHREAD_FREE_LOCK(listener->lock, EVTHREAD_LOCKTYPE_RECURSIVE);
  23. mm_free(listener);
  24. return 1;
  25. } else {
  26. UNLOCK(listener);
  27. return 0;
  28. }
  29. }
  30. static void
  31. event_listener_destroy(struct evconnlistener *lev)
  32. {
  33. struct evconnlistener_event *lev_e =
  34. EVUTIL_UPCAST(lev, struct evconnlistener_event, base);
  35. //把event从event_base中删除
  36. event_del(&lev_e->listener);
  37. if (lev->flags & LEV_OPT_CLOSE_ON_FREE)//如果用户设置了这个选项,那么要关闭socket
  38. evutil_closesocket(event_get_fd(&lev_e->listener));
  39. }
//listener.c文件
void
evconnlistener_free(struct evconnlistener *lev)
{LOCK(lev);lev->cb = NULL;lev->errorcb = NULL;if (lev->ops->shutdown)//这里的shutdown为NULLlev->ops->shutdown(lev);//引用次数减一,并解锁listener_decref_and_unlock(lev);
}static int
listener_decref_and_unlock(struct evconnlistener *listener)
{int refcnt = --listener->refcnt;if (refcnt == 0) {//实际调用event_listener_destroylistener->ops->destroy(listener);UNLOCK(listener);//释放锁EVTHREAD_FREE_LOCK(listener->lock, EVTHREAD_LOCKTYPE_RECURSIVE);mm_free(listener);return 1;} else {UNLOCK(listener);return 0;}
}static void
event_listener_destroy(struct evconnlistener *lev)
{struct evconnlistener_event *lev_e =EVUTIL_UPCAST(lev, struct evconnlistener_event, base);//把event从event_base中删除event_del(&lev_e->listener);if (lev->flags & LEV_OPT_CLOSE_ON_FREE)//如果用户设置了这个选项,那么要关闭socketevutil_closesocket(event_get_fd(&lev_e->listener));
}

要注意一点,LEV_OPT_CLOSE_ON_FREE选项关闭的是服务器端的监听socket,而非那些连接客户端的socket。

现在来说一下那个listener_decref_and_unlock。前面注释说到,在函数listener_read_cb中,一般情况下是不会调用listener_decref_and_unlock,但在多线程的时候可能会调用。这种特殊情况是:当主线程accept到一个新客户端时,会解锁,并调用用户设置的回调函数。此时,引用计数等于2。就在这个时候,第二个线程执行evconnlistener_free函数。该函数会执行listener_decref_and_unlock。明显主线程还在用这个evconnlistener,肯定不能删除。此时引用计数也等于2也不会删除。但用户已经调用了evconnlistener_free。Libevent必须要响应。当第二个线程执行完后,主线程抢到CPU,此时引用计数就变成1了,也就进入到if判断里面了。在判断体里面执行函数listener_decref_and_unlock,并且完成删除工作。

总得来说,Libevent封装的这个evconnlistener和一系列操作函数,还是比较简单的。思路也比较清晰。

Libevent源码分析-----连接监听器evconnlistener相关推荐

  1. Libevent源码分析-----TAILQ_QUEUE队列

     出处:  http://blog.csdn.net/luotuo44/article/details/38374009 Libevent源码中有一个queue.h文件,位于compat/sys目 ...

  2. MQTT-Eclipse paho mqtt源码分析-连接MQTT Broker

    Eclipse paho mqtt源码分析 MQTT paho mqtt 源码分析 org.eclipse.paho.client.mqttv3.MqttClient MQTT MQTT(消息队列遥测 ...

  3. Libevent源码分析-----配置event_base

     出处:  http://blog.csdn.net/luotuo44/article/details/38443569 前面的博文都是讲一些Libevent的一些辅助结构,现在来讲一下关键结构体 ...

  4. libevent 源码分析丨libevent组件构成以及编程要领

    1,前言 Libevent是一个轻量级的开源高性能网络库,使用者众多,研究者更甚,相关文章也不少.写这一系列文章的用意在于,一则分享心得:二则对libevent代码和设计思想做系统的.更深层次的分析, ...

  5. SOFA 源码分析 — 连接管理器

    前言 RPC 框架需要维护客户端和服务端的连接,通常是一个客户端对应多个服务端,而客户端看到的是接口,并不是服务端的地址,服务端地址对于客户端来讲是透明的. 那么,如何实现这样一个 RPC 框架的网络 ...

  6. libevent源码分析:eventop

    eventop:定义了event_base使用的后端IO复用的一个统一接口 1 /** Structure to define the backend of a given event_base. * ...

  7. Libevent源码分析

    http://blog.csdn.net/column/details/libevent-src.html?&page=2

  8. Thrift异步IO服务器源码分析

    http://yanyiwu.com/work/2014/12/06/thrift-tnonblockingserver-analysis.html 最近在使用 libevent 开发项目,想起之前写 ...

  9. libevent 源码阅读 Ubuntu下muduo库的安装与使用

    libevent源码分析--代码结构_鱼思故渊的专栏-CSDN博客 LINUX实战:Ubuntu下muduo库的安装与使用 LINUX实战:Ubuntu下muduo库的安装与使用-LINUX入门-维易 ...

最新文章

  1. (原)ubuntu14.04中安装gcc4.9和g++4.9
  2. pandas将某一列变为索引_Pandas 基础语法入门
  3. mysql的c接口_mysql C接口大全
  4. 碧蓝航线8.20服务器维护,碧蓝航线半人马来袭 8.20更新公告
  5. C++一位的字符数字转数字
  6. url过滤怎么解除_腾讯面试官是这样来问布隆过滤器的?
  7. 前端一倍图、二倍图、多倍图
  8. 使用JS获取当前地理位置方法汇总
  9. Android蓝牙4.0单车锁应用实例开发
  10. 远程桌面3389加固
  11. 【FinE】资本市场理论(1) CAPM模型
  12. JAVA Zip压缩 Tar压缩 tar.gz打包压缩
  13. ios设备如何安装chatgpt
  14. cxf调用报错Could not find conduit initiator for address:
  15. 什么是集合,集合的定义,集合与数组的区别,怎么创建一个集合?
  16. InfoSphere Guardium在医疗保健行业中对System Z的好处
  17. 2022广州国际食品饮料包装展览会
  18. Django的前后端分离以及Rest风格接口开发大全
  19. kindle的重生:看视频
  20. CodeForces - 732A

热门文章

  1. html网页中使用mock,关于Mock.js使用
  2. 数学教材里的神秘数表在国外红出圈,网友:引人入胜、猜不到结局
  3. 免费Linux系统和生信宝典原创学习教程
  4. 快速卷积与快速相关——MATLAB
  5. 如何Super Vectorizer从Mac 上的图像中删除多余的颜色区域?
  6. 1282:最大子矩阵《信息学奥赛一本通》
  7. informatica数据脱敏_助您首个大数据项目破茧成蝶的实践指南
  8. 下列哪个不是python元组的定义方式_Python基础知识笔试
  9. SQL笔记-使用not in在多个没有外键关联的表中查询
  10. vs2013 qt5.1出现“无法找到源文件ui.xxx.h”解决办法详细步骤