出處:http://blog.csdn.net/luotuo44/article/details/38800363
使用evconnlistener:
? ? ? ? 基于event和event_base已經(jīng)可以寫一個(gè)CS模型了。但是對(duì)于服務(wù)器端來(lái)說(shuō),仍然需要用戶自行調(diào)用socket、bind、listen、accept等步驟。這個(gè)過(guò)程有點(diǎn)繁瑣,為此在2.0.2-alpha版本的Libevent推出了一些對(duì)應(yīng)的封裝函數(shù)。
? ? ? ??用戶只需初始化struct sockaddr_in結(jié)構(gòu)體變量,然后把它作為參數(shù)傳給函數(shù)evconnlistener_new_bind即可。該函數(shù)會(huì)完成上面說(shuō)到的那4個(gè)過(guò)程。下面的代碼是一個(gè)使用例子。
[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();????????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;??}????????????void?listener_cb(evconnlistener?*listener,?evutil_socket_t?fd,???????????????????struct?sockaddr?*sock,?int?socklen,?void?*arg)??{??????event_base?*base?=?(event_base*)arg;??????????????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");??????????????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;
}//有新的客戶端連接到服務(wù)器
//當(dāng)此函數(shù)被調(diào)用時(shí),libevent已經(jīng)幫我們accept了這個(gè)客戶端。該客戶端的
//文件描述符為fd
void listener_cb(evconnlistener *listener, evutil_socket_t fd,struct sockaddr *sock, int socklen, void *arg)
{event_base *base = (event_base*)arg;//下面代碼是為這個(gè)fd創(chuàng)建一個(gè)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");//這將自動(dòng)close套接字和free讀寫緩沖區(qū)bufferevent_free(bev);
}
? ? ? ??上面的代碼是一個(gè)服務(wù)器端的例子,客戶端代碼可以使用《Libevent使用例子,從簡(jiǎn)單到復(fù)雜》博文中的客戶端。這里就不貼客戶端代碼了。
?
? ? ? ??從上面代碼可以看到,當(dāng)服務(wù)器端監(jiān)聽(tīng)到一個(gè)客戶端的連接請(qǐng)求后,就會(huì)調(diào)用listener_cb這個(gè)回調(diào)函數(shù)。這個(gè)回調(diào)函數(shù)是在evconnlistener_new_bind函數(shù)中設(shè)置的。現(xiàn)在來(lái)看一下這個(gè)函數(shù)的參數(shù)有哪些,下面是其函數(shù)原型。
[cpp] view plaincopy print?
??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);
? ? ? ??第一個(gè)參數(shù)是很熟悉的event_base,無(wú)論怎么樣都是離不開(kāi)event_base這個(gè)發(fā)動(dòng)機(jī)的。
? ? ? ??第二個(gè)參數(shù)是一個(gè)函數(shù)指針,該函數(shù)指針的格式如代碼所示。當(dāng)有新的客戶端請(qǐng)求連接時(shí),該函數(shù)就會(huì)調(diào)用。要注意的是:當(dāng)這個(gè)回調(diào)函數(shù)被調(diào)用時(shí),Libevent已經(jīng)幫我們accept了這個(gè)客戶端。所以,該回調(diào)函數(shù)有一個(gè)參數(shù)是文件描述符fd。我們直接使用這個(gè)fd即可。真是方便。這個(gè)參數(shù)是可以為NULL的,此時(shí)用戶并不能接收到客戶端。當(dāng)用戶調(diào)用evconnlistener_set_cb函數(shù)設(shè)置回調(diào)函數(shù)后,就可以了。
? ? ? ??第三個(gè)參數(shù)是傳給回調(diào)函數(shù)的用戶參數(shù),作用就像event_new函數(shù)的最后一個(gè)參數(shù)。
? ? ? ??參數(shù)flags是一些標(biāo)志值,有下面這些:
- LEV_OPT_LEAVE_SOCKETS_BLOCKING:默認(rèn)情況下,當(dāng)連接監(jiān)聽(tīng)器接收到新的客戶端socket連接后,會(huì)把該socket設(shè)置為非阻塞的。如果設(shè)置該選項(xiàng),那么就把之客戶端socket保留為阻塞的
- LEV_OPT_CLOSE_ON_FREE:當(dāng)連接監(jiān)聽(tīng)器釋放時(shí),會(huì)自動(dòng)關(guān)閉底層的socket
- LEV_OPT_CLOSE_ON_EXEC:為底層的socket設(shè)置close-on-exec標(biāo)志
- LEV_OPT_REUSEABLE:?在某些平臺(tái),默認(rèn)情況下當(dāng)一個(gè)監(jiān)聽(tīng)socket被關(guān)閉時(shí),其他socket不能馬上綁定到同一個(gè)端口,要等一會(huì)兒才行。設(shè)置該標(biāo)志后,Libevent會(huì)把該socket設(shè)置成reuseable。這樣,關(guān)閉該socket后,其他socket就能馬上使用同一個(gè)端口
- LEV_OPT_THREADSAFE:為連接監(jiān)聽(tīng)器分配鎖。這樣可以確保線程安全
? ? ? ??參數(shù)backlog是系統(tǒng)調(diào)用listen的第二個(gè)參數(shù)。最后兩個(gè)參數(shù)就不多說(shuō)了。
evconnlistener的封裝:
? ? ? ??接下來(lái)看一下Libevent是怎么封裝evconnlistener的。
用到的結(jié)構(gòu)體:
[cpp] view plaincopy print?
??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;??};????struct?evconnlistener_event?{??????struct?evconnlistener?base;??????struct?event?listener;???};??
//listener.c文件
struct evconnlistener_ops {//一系列的工作函數(shù)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;//操作函數(shù)void *lock; //鎖變量,用于線程安全evconnlistener_cb cb;//用戶的回調(diào)函數(shù)evconnlistener_errorcb errorcb;//發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)void *user_data;//回調(diào)函數(shù)的參數(shù)unsigned flags;//屬性標(biāo)志short refcnt;//引用計(jì)數(shù)unsigned enabled : 1;//位域?yàn)?.即只需一個(gè)比特位來(lái)存儲(chǔ)這個(gè)成員
};struct evconnlistener_event {struct evconnlistener base;struct event listener; //內(nèi)部event,插入到event_base
};
? ? ? ??在evconnlistener_event結(jié)構(gòu)體有一個(gè)event結(jié)構(gòu)體。可以想象,在實(shí)現(xiàn)時(shí)必然是將服務(wù)器端的socket fd賦值給struct event 類型變量listener的fd成員。然后將listener加入到event_base,這樣就完成了自動(dòng)監(jiān)聽(tīng)工作。這也回歸到之前學(xué)過(guò)的內(nèi)容。
? ? ? ??下面看一下具體是怎么實(shí)現(xiàn)的。
初始化服務(wù)器socket:
[cpp] view plaincopy print?
??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;??????????????if?(backlog?==?0)??????????return?NULL;????????fd?=?socket(family,?SOCK_STREAM,?0);??????if?(fd?==?-1)??????????return?NULL;????????????????????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;//監(jiān)聽(tīng)個(gè)數(shù)不能為0if (backlog == 0)return NULL;fd = socket(family, SOCK_STREAM, 0);if (fd == -1)return NULL;//LEV_OPT_LEAVE_SOCKETS_BLOCKING選項(xiàng)是應(yīng)用于accept到的客戶端socket//所以對(duì)于服務(wù)器端的socket,直接將之設(shè)置為非阻塞的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函數(shù)申請(qǐng)一個(gè)socket,然后對(duì)之進(jìn)行一些有關(guān)非阻塞、重用、保持連接的處理、綁定到特定的IP和端口。最后把業(yè)務(wù)邏輯交給evconnlistener_new處理。
[cpp] view plaincopy print?
??static?const?struct?evconnlistener_ops?evconnlistener_event_ops?=?{??????event_listener_enable,??????event_listener_disable,??????event_listener_destroy,??????NULL,???????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);??????}??????????????event_assign(&lev->listener,?base,?fd,?EV_READ|EV_PERSIST,??????????listener_read_cb,?lev);??????????????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);??????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);??????????????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復(fù)用函數(shù)中,新客戶端的連接請(qǐng)求也被當(dāng)作讀事件event_assign(&lev->listener, base, fd, EV_READ|EV_PERSIST,listener_read_cb, lev);//會(huì)調(diào)用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);//實(shí)際上是調(diào)用下面的event_listener_enable函數(shù)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,完成監(jiān)聽(tīng)工作。return event_add(&lev_e->listener, NULL);
}
? ? ? ??幾個(gè)函數(shù)的一路調(diào)用,思路還是挺清晰的。就是申請(qǐng)一個(gè)socket,進(jìn)行一些處理,然后用之賦值給event。最后把之a(chǎn)dd到event_base中。event_base會(huì)對(duì)新客戶端的請(qǐng)求連接進(jìn)行監(jiān)聽(tīng)。
? ? ? ??在evconnlistener_enable函數(shù)里面,如果用戶沒(méi)有設(shè)置回調(diào)函數(shù),那么就不會(huì)調(diào)用event_listener_enable。也就是說(shuō)并不會(huì)add到event_base中。
? ? ? ??event_listener_enable函數(shù)里面的宏EVUTIL_UPCAST可以根據(jù)結(jié)構(gòu)體成員變量的地址推算出結(jié)構(gòu)體的起始地址。有關(guān)這個(gè)宏,可以查看”結(jié)構(gòu)體偏移量”。
處理客戶端的連接請(qǐng)求:
? ? ? ??現(xiàn)在來(lái)看一下event的回調(diào)函數(shù)listener_read_cb。
[cpp] view plaincopy print?
??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)?{?????????????????????????????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;??????????}????????????????????????????????????????????????????++lev->refcnt;??????????cb?=?lev->cb;??????????user_data?=?lev->user_data;??????????UNLOCK(lev);??????????cb(lev,?new_fd,?(struct?sockaddr*)&ss,?(int)socklen,??????????????user_data);??????????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))?{??????????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) { //可能有多個(gè)客戶端同時(shí)請(qǐng)求連接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);//用戶還沒(méi)設(shè)置連接監(jiān)聽(tīng)器的回調(diào)函數(shù)if (lev->cb == NULL) {UNLOCK(lev);return;}//由于refcnt被初始化為1.這里有++了,所以一般情況下并不會(huì)進(jìn)入下面的//if判斷里面。但如果程在下面UNLOCK之后,第二個(gè)線調(diào)用evconnlistener_free//釋放這個(gè)evconnlistener時(shí),就有可能使得refcnt為1了。即進(jìn)入那個(gè)判斷體里//執(zhí)行l(wèi)istener_decref_and_unlock。在下面會(huì)討論這個(gè)問(wèn)題。++lev->refcnt;cb = lev->cb;user_data = lev->user_data;UNLOCK(lev);cb(lev, new_fd, (struct sockaddr*)&ss, (int)socklen,user_data);//調(diào)用用戶設(shè)置的回調(diào)函數(shù),讓用戶處理這個(gè)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;}//當(dāng)有錯(cuò)誤發(fā)生時(shí)才會(huì)運(yùn)行到這里if (lev->errorcb != NULL) {++lev->refcnt;errorcb = lev->errorcb;user_data = lev->user_data;UNLOCK(lev);errorcb(lev, user_data);//調(diào)用用戶設(shè)置的錯(cuò)誤回調(diào)函數(shù)LOCK(lev);listener_decref_and_unlock(lev);}
}
? ? ? ??這個(gè)函數(shù)所做的工作也比較簡(jiǎn)單,就是accept客戶端,然后調(diào)用用戶設(shè)置的回調(diào)函數(shù)。所以,用戶回調(diào)函數(shù)的參數(shù)fd是一個(gè)已經(jīng)連接好了的socket。
? ? ? ??上面函數(shù)說(shuō)到了錯(cuò)誤回調(diào)函數(shù),可以通過(guò)下面的函數(shù)設(shè)置連接監(jiān)聽(tīng)器的錯(cuò)誤監(jiān)聽(tīng)函數(shù)。
[cpp] view plaincopy print?
??typedef?void?(*evconnlistener_errorcb)(struct?evconnlistener?*,?void?*);??????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:
? ? ? ??調(diào)用evconnlistener_free可以釋放一個(gè)evconnlistener。由于evconnlistener擁有一些系統(tǒng)資源,在釋放evconnlistener_free的時(shí)候會(huì)釋放這些系統(tǒng)資源。
[cpp] view plaincopy print?
??void??evconnlistener_free(struct?evconnlistener?*lev)??{??????LOCK(lev);??????lev->cb?=?NULL;??????lev->errorcb?=?NULL;??????if?(lev->ops->shutdown)??????????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)?{????????????????????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_del(&lev_e->listener);??????if?(lev->flags?&?LEV_OPT_CLOSE_ON_FREE)??????????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);//引用次數(shù)減一,并解鎖listener_decref_and_unlock(lev);
}static int
listener_decref_and_unlock(struct evconnlistener *listener)
{int refcnt = --listener->refcnt;if (refcnt == 0) {//實(shí)際調(diào)用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)//如果用戶設(shè)置了這個(gè)選項(xiàng),那么要關(guān)閉socketevutil_closesocket(event_get_fd(&lev_e->listener));
}
? ? ? ??要注意一點(diǎn),LEV_OPT_CLOSE_ON_FREE選項(xiàng)關(guān)閉的是服務(wù)器端的監(jiān)聽(tīng)socket,而非那些連接客戶端的socket。
? ? ? ??現(xiàn)在來(lái)說(shuō)一下那個(gè)listener_decref_and_unlock。前面注釋說(shuō)到,在函數(shù)listener_read_cb中,一般情況下是不會(huì)調(diào)用listener_decref_and_unlock,但在多線程的時(shí)候可能會(huì)調(diào)用。這種特殊情況是:當(dāng)主線程accept到一個(gè)新客戶端時(shí),會(huì)解鎖,并調(diào)用用戶設(shè)置的回調(diào)函數(shù)。此時(shí),引用計(jì)數(shù)等于2。就在這個(gè)時(shí)候,第二個(gè)線程執(zhí)行evconnlistener_free函數(shù)。該函數(shù)會(huì)執(zhí)行l(wèi)istener_decref_and_unlock。明顯主線程還在用這個(gè)evconnlistener,肯定不能刪除。此時(shí)引用計(jì)數(shù)也等于2也不會(huì)刪除。但用戶已經(jīng)調(diào)用了evconnlistener_free。Libevent必須要響應(yīng)。當(dāng)?shù)诙€(gè)線程執(zhí)行完后,主線程搶到CPU,此時(shí)引用計(jì)數(shù)就變成1了,也就進(jìn)入到if判斷里面了。在判斷體里面執(zhí)行函數(shù)listener_decref_and_unlock,并且完成刪除工作。
?
? ? ? ??總得來(lái)說(shuō),Libevent封裝的這個(gè)evconnlistener和一系列操作函數(shù),還是比較簡(jiǎn)單的。思路也比較清晰。
總結(jié)
以上是生活随笔為你收集整理的Libevent源码分析-----连接监听器evconnlistener的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。