處理大并發(fā)之四?libevent?demo詳細(xì)分析(對(duì)比epoll)
libevent默認(rèn)情況下是單線(xiàn)程,每個(gè)線(xiàn)程有且僅有一個(gè)event_base,對(duì)應(yīng)一個(gè)struct?event_base結(jié)構(gòu)體,以及賦予其上的事件管理器,用來(lái)安排托管給它的一系列的事件。
當(dāng)有一個(gè)事件發(fā)生的時(shí)候,event_base會(huì)在合適的時(shí)間去調(diào)用綁定在這個(gè)事件上的函數(shù),直到這個(gè)函數(shù)執(zhí)行完成,然后在返回安排其他事件。需要注意的是:合適的時(shí)間并不是立即。
例如:
[cpp] view plain
copy struct?event_base?*base;??base?=?event_base_new();??
event_base_new對(duì)比epoll,可以理解為epoll里的epoll_create。
event_base內(nèi)部有一個(gè)循環(huán),循環(huán)阻塞在epoll調(diào)用上,當(dāng)有一個(gè)事件發(fā)生的時(shí)候,才會(huì)去處理這個(gè)事件。其中,這個(gè)事件是被綁定在event_base上面的,每一個(gè)事件就會(huì)對(duì)應(yīng)一個(gè)struct?event,可以是監(jiān)聽(tīng)的fd。?
其中struct?event?使用event_new?來(lái)創(chuàng)建和綁定,使用event_add來(lái)啟用,例如:
[cpp] view plain
copy struct?event?*listener_event;??listener_event?=?event_new(base,?listener,?EV_READ|EV_PERSIST,?do_accept,?(void*)base);??
參數(shù)說(shuō)明:
base:event_base類(lèi)型,event_base_new的返回值
listener:監(jiān)聽(tīng)的fd,listen的fd
EV_READ|EV_PERSIST:事件的類(lèi)型及屬性
do_accept:綁定的回調(diào)函數(shù)
(void*)base:給回調(diào)函數(shù)的參數(shù)
event_add(listener_event,?NULL);
對(duì)比epoll:
event_new相當(dāng)于epoll中的epoll_wait,其中的epoll里的while循環(huán),在libevent里使用event_base_dispatch。
event_add相當(dāng)于epoll中的epoll_ctl,參數(shù)是EPOLL_CTL_ADD,添加事件。
注:libevent支持的事件及屬性包括(使用bitfield實(shí)現(xiàn),所以要用?|?來(lái)讓它們合體)
EV_TIMEOUT:?超時(shí)
EV_READ:?只要網(wǎng)絡(luò)緩沖中還有數(shù)據(jù),回調(diào)函數(shù)就會(huì)被觸發(fā)
EV_WRITE:?只要塞給網(wǎng)絡(luò)緩沖的數(shù)據(jù)被寫(xiě)完,回調(diào)函數(shù)就會(huì)被觸發(fā)
EV_SIGNAL:?POSIX信號(hào)量
EV_PERSIST:?不指定這個(gè)屬性的話(huà),回調(diào)函數(shù)被觸發(fā)后事件會(huì)被刪除
EV_ET:?Edge-Trigger邊緣觸發(fā),相當(dāng)于EPOLL的ET模式
事件創(chuàng)建添加之后,就可以處理發(fā)生的事件了,相當(dāng)于epoll里的epoll_wait,在libevent里使用event_base_dispatch啟動(dòng)event_base循環(huán),直到不再有需要關(guān)注的事件。
有了上面的分析,結(jié)合之前做的epoll服務(wù)端程序,對(duì)于一個(gè)服務(wù)器程序,流程基本是這樣的:
1.?創(chuàng)建socket,bind,listen,設(shè)置為非阻塞模式
2.?創(chuàng)建一個(gè)event_base,即
[cpp] view plain
copy struct?event_base?*??event_base_new(void)??
3.?創(chuàng)建一個(gè)event,將該socket托管給event_base,指定要監(jiān)聽(tīng)的事件類(lèi)型,并綁定上相應(yīng)的回調(diào)函數(shù)(及需要給它的參數(shù))。即
[cpp] view plain
copy struct?event?*??event_new(struct?event_base?*base,?evutil_socket_t?fd,?short?events,?void?(*cb)(evutil_socket_t,?short,?void?*),?void?*arg)??
4.?啟用該事件,即
[cpp] view plain
copy int??event_add(struct?event?*ev,?const?struct?timeval?*tv)??
5.??進(jìn)入事件循環(huán),即
[cpp] view plain
copy int??event_base_dispatch(struct?event_base?*event_base)??
?
有了這些知識(shí)儲(chǔ)備,來(lái)看下官網(wǎng)上的demo,網(wǎng)址:http://www.wangafu.net/~nickm/libevent-book/01_intro.html,這里引用的例子是Example:?A?low-level?ROT13?server?with?Libevent
首先來(lái)翻譯下例子上面的一段話(huà):
對(duì)于select函數(shù)來(lái)說(shuō),不同的操作系統(tǒng)有不同的代替函數(shù),它包括:poll,epoll,kqueue,evport和/dev/poll。這些函數(shù)的性能都比select要好,其中epoll在IO中添加,刪除,通知socket準(zhǔn)備好方面性能復(fù)雜度為O(1)。
不幸的是,沒(méi)有一個(gè)有效的接口是一個(gè)普遍存在的標(biāo)準(zhǔn),linux下有epoll,BSDS有kqueue,Solaris?有evport和/dev/poll,等等。沒(méi)有任何一個(gè)操作系統(tǒng)有它們中所有的,所以如果你想做一個(gè)輕便的高性能的異步應(yīng)用程序,你就需要把這些接口抽象的封裝起來(lái),并且無(wú)論哪一個(gè)系統(tǒng)使用它都是最高效的。
這對(duì)于你來(lái)說(shuō)就是最低級(jí)的libevent?API,它提供了統(tǒng)一的接口取代了select,當(dāng)它在計(jì)算機(jī)上運(yùn)行的時(shí)候,使用了最有效的版本。
這里是ROT13服務(wù)器的另外一個(gè)版本,這次,他使用了libevent代替了select。這意味著我們不再使用fd_sets,取而代之的使用event_base添加和刪除事件,它可能在select,poll,epoll,kqueue等中執(zhí)行。
代碼分析:
這是一個(gè)服務(wù)端的程序,可以處理客戶(hù)端大并發(fā)的連接,當(dāng)收到客戶(hù)端的連接后,將收到的數(shù)據(jù)做了一個(gè)變換,如果是?’a’-‘m’之間的字符,將其增加13,如果是?’n’-‘z’之間的字符,將其減少13,其他字符不變,然后將轉(zhuǎn)換后的數(shù)據(jù)發(fā)送給客戶(hù)端。
例如:客戶(hù)端發(fā)送:Client?0?send??Message!
服務(wù)端會(huì)回復(fù):Pyvrag?0?fraq??Zrffntr!
在這個(gè)代碼中沒(méi)有使用bufferevent這個(gè)強(qiáng)大的東西,在一個(gè)結(jié)構(gòu)體中自己管理了一個(gè)緩沖區(qū)。結(jié)構(gòu)體為:
[cpp] view plain
copy struct?fd_state?{??????char?buffer[MAX_LINE];??????size_t?buffer_used;????????size_t?n_written;??????size_t?write_upto;????????struct?event?*read_event;??????struct?event?*write_event;??};??
代碼中自己管理了一個(gè)緩沖區(qū),用于存放接收到的數(shù)據(jù),發(fā)送的數(shù)據(jù)將其轉(zhuǎn)換后也放入該緩沖區(qū)中,代碼晦澀難懂,我也是經(jīng)過(guò)打日志分析后,才明白點(diǎn),這個(gè)緩沖區(qū)自己還得控制好。但是libevent?2已經(jīng)提供了一個(gè)神器bufferevent,我們?cè)谑褂玫倪^(guò)程中最好不要自己管理這個(gè)緩沖區(qū),之所以分析這個(gè)代碼,是為了熟悉libevent?做服務(wù)端程序的流程及原理。
下面是代碼,加有部分注釋和日志:
代碼:lowlevel_libevent_server.c?
[cpp] view plain
copy ??????#include?<netinet/in.h>????#include?<sys/socket.h>????#include?<fcntl.h>????#include?<event2/event.h>????#include?<assert.h>??#include?<unistd.h>??#include?<string.h>??#include?<stdlib.h>??#include?<stdio.h>??#include?<errno.h>????#define?MAX_LINE?80????void?do_read(evutil_socket_t?fd,?short?events,?void?*arg);??void?do_write(evutil_socket_t?fd,?short?events,?void?*arg);????char?rot13_char(char?c)??{?????????????if?((c?>=?'a'?&&?c?<=?'m')?||?(c?>=?'A'?&&?c?<=?'M'))??????????return?c?+?13;??????else?if?((c?>=?'n'?&&?c?<=?'z')?||?(c?>=?'N'?&&?c?<=?'Z'))??????????return?c?-?13;??????else??????????return?c;??}????struct?fd_state?{??????char?buffer[MAX_LINE];??????size_t?buffer_used;????????size_t?n_written;??????size_t?write_upto;????????struct?event?*read_event;??????struct?event?*write_event;??};????struct?fd_state?*?alloc_fd_state(struct?event_base?*base,?evutil_socket_t?fd)??{??????struct?fd_state?*state?=?malloc(sizeof(struct?fd_state));??????if?(!state)??????????return?NULL;????????state->read_event?=?event_new(base,?fd,?EV_READ|EV_PERSIST,?do_read,?state);??????if?(!state->read_event)??????{??????????free(state);??????????return?NULL;??????}????????state->write_event?=?event_new(base,?fd,?EV_WRITE|EV_PERSIST,?do_write,?state);??????if?(!state->write_event)??????{??????????event_free(state->read_event);??????????free(state);??????????return?NULL;??????}????????state->buffer_used?=?state->n_written?=?state->write_upto?=?0;????????assert(state->write_event);??????return?state;??}????void?free_fd_state(struct?fd_state?*state)??{??????event_free(state->read_event);??????event_free(state->write_event);??????free(state);??}????void?do_read(evutil_socket_t?fd,?short?events,?void?*arg)??{??????struct?fd_state?*state?=?arg;??????char?buf[20];??????int?i;??????ssize_t?result;??????printf("\ncome?in?do_read:?fd:?%d,?state->buffer_used:?%d,?sizeof(state->buffer):?%d\n",?fd,?state->buffer_used,?size??of(state->buffer));??????while?(1)??????{??????????assert(state->write_event);??????????result?=?recv(fd,?buf,?sizeof(buf),?0);??????????if?(result?<=?0)??????????????break;??????????printf("recv?once,?fd:?%d,?recv?size:?%d,?recv?buff:?%s\n",?fd,?result,?buf);????????????for?(i=0;?i?<?result;?++i)??????????{??????????????if?(state->buffer_used?<?sizeof(state->buffer))??????????????????state->buffer[state->buffer_used++]?=?rot13_char(buf[i]);????????????????if?(buf[i]?==?'\n')???????????????{??????????????????assert(state->write_event);??????????????????event_add(state->write_event,?NULL);??????????????????state->write_upto?=?state->buffer_used;??????????????????printf("遇到換行符,state->write_upto:?%d,?state->buffer_used:?%d\n",state->write_upto,?state->buffer_use??d);??????????????}??????????}??????????printf("recv?once,?state->buffer_used:?%d\n",?state->buffer_used);??}??????????????if?(result?==?0)??????{??????????free_fd_state(state);??????}??????else?if?(result?<?0)??????{??????????if?(errno?==?EAGAIN)???????????????return;??????????perror("recv");??????????free_fd_state(state);??????}??}????void?do_write(evutil_socket_t?fd,?short?events,?void?*arg)??{??????struct?fd_state?*state?=?arg;????????printf("\ncome?in?do_write,?fd:?%d,?state->n_written:?%d,?state->write_upto:?%d\n",fd,?state->n_written,?state->write??_upto);??????while?(state->n_written?<?state->write_upto)??????{??????????ssize_t?result?=?send(fd,?state->buffer?+?state->n_written,?state->write_upto?-?state->n_written,?0);??????????if?(result?<?0)?{??????????????if?(errno?==?EAGAIN)???????????????????return;??????????????free_fd_state(state);??????????????return;??????????}??????????assert(result?!=?0);????????????state->n_written?+=?result;??????????printf("send?fd:?%d,?send?size:?%d,?state->n_written:?%d\n",?fd,?result,?state->n_written);??????}????????if?(state->n_written?==?state->buffer_used)??????{??????????printf("state->n_written?==?state->buffer_used:?%d\n",?state->n_written);??????????state->n_written?=?state->write_upto?=?state->buffer_used?=?1;??????????printf("state->n_written?=?state->write_upto?=?state->buffer_used?=?1\n");??????}????????event_del(state->write_event);??}????void?do_accept(evutil_socket_t?listener,?short?event,?void?*arg)??{??????struct?event_base?*base?=?arg;??????struct?sockaddr_storage?ss;??????socklen_t?slen?=?sizeof(ss);??????int?fd?=?accept(listener,?(struct?sockaddr*)&ss,?&slen);??????if?(fd?<?0)??????{???????????perror("accept");??????}??????else?if?(fd?>?FD_SETSIZE)??????{??????????close(fd);???????}??????else??????{??????????struct?fd_state?*state;??????????evutil_make_socket_nonblocking(fd);??????????state?=?alloc_fd_state(base,?fd);??????????assert(state);???????????assert(state->write_event);??????????event_add(state->read_event,?NULL);??????}??}????void?run(void)??{??????evutil_socket_t?listener;??????struct?sockaddr_in?sin;??????struct?event_base?*base;??????struct?event?*listener_event;????????base?=?event_base_new();??????if?(!base)??????????return;?????????sin.sin_family?=?AF_INET;??????sin.sin_addr.s_addr?=?0;??????sin.sin_port?=?htons(8000);????????listener?=?socket(AF_INET,?SOCK_STREAM,?0);??????evutil_make_socket_nonblocking(listener);????#ifndef?WIN32??????{??????????int?one?=?1;??????????setsockopt(listener,?SOL_SOCKET,?SO_REUSEADDR,?&one,?sizeof(one));??????}??#endif????????if?(bind(listener,?(struct?sockaddr*)&sin,?sizeof(sin))?<?0)??????{??????????perror("bind");??????????return;?? }?? ?? ?? ????if?(listen(listener,?16)<0)?? ????{?? ????????perror("listen");?? ????????return;?? ????}?? ?? ????listener_event?=?event_new(base,?listener,?EV_READ|EV_PERSIST,?do_accept,?(void*)base);?? ?????? ????event_add(listener_event,?NULL);?? ?? ????event_base_dispatch(base);?? }?? ?? int?main(int?c,?char?**v)?? {?? ?? ?? ????run();?? ????return?0;?? }??
編譯:gcc?-I/usr/include?-o?test?lowlevel_libevent_server.c?-L/usr/local/lib?-levent
運(yùn)行結(jié)果:
總結(jié)
以上是生活随笔為你收集整理的处理大并发之四 libevent demo详细分析(对比epoll)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。