日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Libevent源码分析-----连接监听器evconnlistener

發布時間:2025/3/15 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Libevent源码分析-----连接监听器evconnlistener 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
出處: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?
  • #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?threads??
  • ??
  • ????struct?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創建一個bufferevent??
  • ????bufferevent?*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);??
  • }??
  • #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函數中設置的?,F在來看一下這個函數的參數有哪些,下面是其函數原型。

    [cpp] view plaincopy print?
  • //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);??
  • //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?
  • //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??
  • };??
  • //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結構體??梢韵胂?#xff0c;在實現時必然是將服務器端的socket fd賦值給struct event 類型變量listener的fd成員。然后將listener加入到event_base,這樣就完成了自動監聽工作。這也回歸到之前學過的內容。


    ? ? ? ??下面看一下具體是怎么實現的。

    初始化服務器socket:

    [cpp] view plaincopy print?
  • //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;??
  • ??
  • ????//監聽個數不能為0??
  • ????if?(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;??
  • }??
  • //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?
  • //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函數??
  • ????else??
  • ????????r?=?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);??
  • }??
  • //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?
  • //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?WIN32??
  • ????????int?socklen?=?sizeof(ss);??
  • #else??
  • ????????socklen_t?socklen?=?sizeof(ss);??
  • #endif??
  • ????????evutil_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);//調用用戶設置的回調函數,讓用戶處理這個fd??
  • ????????LOCK(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))?{//還可以accept??
  • ????????UNLOCK(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);??
  • ????}??
  • }??
  • //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?
  • //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);??
  • }??
  • //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?
  • //listener.c文件??
  • void??
  • evconnlistener_free(struct?evconnlistener?*lev)??
  • {??
  • ????LOCK(lev);??
  • ????lev->cb?=?NULL;??
  • ????lev->errorcb?=?NULL;??
  • ????if?(lev->ops->shutdown)//這里的shutdown為NULL??
  • ????????lev->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_destroy??
  • ????????listener->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)//如果用戶設置了這個選項,那么要關閉socket??
  • ????????evutil_closesocket(event_get_fd(&lev_e->listener));??
  • }??
  • //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的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。