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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

处理大并发之四 libevent demo详细分析(对比epoll)

發(fā)布時(shí)間:2025/3/21 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 处理大并发之四 libevent demo详细分析(对比epoll) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

處理大并發(fā)之四?libevent?demo詳細(xì)分析(對(duì)比epoll

libevent默認(rèn)情況下是單線程,每個(gè)線程有且僅有一個(gè)event_base,對(duì)應(yīng)一個(gè)struct?event_base結(jié)構(gòu)體,以及賦予其上的事件管理器,用來安排托管給它的一系列的事件。

當(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();//初始化libevent??
  • 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)聽的fd。?

    其中struct?event?使用event_new?來創(chuàng)建和綁定,使用event_add來啟用,例如:

    [cpp] view plain copy
  • struct?event?*listener_event;??
  • listener_event?=?event_new(base,?listener,?EV_READ|EV_PERSIST,?do_accept,?(void*)base);??
  • 參數(shù)說明:

    base:event_base類型,event_base_new的返回值

    listener:監(jiān)聽的fd,listen的fd

    EV_READ|EV_PERSIST事件的類型及屬性

    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),所以要用?|?來讓它們合體)
    EV_TIMEOUT:?超時(shí)
    EV_READ:?只要網(wǎng)絡(luò)緩沖中還有數(shù)據(jù),回調(diào)函數(shù)就會(huì)被觸發(fā)
    EV_WRITE:?只要塞給網(wǎng)絡(luò)緩沖的數(shù)據(jù)被寫完,回調(diào)函數(shù)就會(huì)被觸發(fā)
    EV_SIGNAL:?POSIX信號(hào)量
    EV_PERSIST:?不指定這個(gè)屬性的話,回調(diào)函數(shù)被觸發(fā)后事件會(huì)被刪除
    EV_ET:?Edge-Trigger邊緣觸發(fā),相當(dāng)于EPOLLET模式

    事件創(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)建socketbindlisten,設(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)聽的事件類型,并綁定上相應(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ǔ)備,來看下官網(wǎng)上的demo,網(wǎng)址:http://www.wangafu.net/~nickm/libevent-book/01_intro.html,這里引用的例子是Example:?A?low-level?ROT13?server?with?Libevent

    首先來翻譯下例子上面的一段話:

    對(duì)于select函數(shù)來說,不同的操作系統(tǒng)有不同的代替函數(shù),它包括:poll,epoll,kqueue,evport/dev/poll。這些函數(shù)的性能都比select要好,其中epollIO中添加,刪除,通知socket準(zhǔn)備好方面性能復(fù)雜度為O(1)。

    不幸的是,沒有一個(gè)有效的接口是一個(gè)普遍存在的標(biāo)準(zhǔn),linux下有epollBSDSkqueueSolaris?有evport/dev/poll,等等。沒有任何一個(gè)操作系統(tǒng)有它們中所有的,所以如果你想做一個(gè)輕便的高性能的異步應(yīng)用程序,你就需要把這些接口抽象的封裝起來,并且無論哪一個(gè)系統(tǒng)使用它都是最高效的。

    這對(duì)于你來說就是最低級(jí)的libevent?API,它提供了統(tǒng)一的接口取代了select,當(dāng)它在計(jì)算機(jī)上運(yùn)行的時(shí)候,使用了最有效的版本。

    這里是ROT13服務(wù)器的另外一個(gè)版本,這次,他使用了libevent代替了select。這意味著我們不再使用fd_sets,取而代之的使用event_base添加和刪除事件,它可能在selectpollepollkqueue等中執(zhí)行。

    代碼分析:

    這是一個(gè)服務(wù)端的程序,可以處理客戶端大并發(fā)的連接,當(dāng)收到客戶端的連接后,將收到的數(shù)據(jù)做了一個(gè)變換,如果是?’a’-‘m’之間的字符,將其增加13,如果是?’n’-‘z’之間的字符,將其減少13,其他字符不變,然后將轉(zhuǎn)換后的數(shù)據(jù)發(fā)送給客戶端。

    例如:客戶端發(fā)送:Client?0?send??Message!

    服務(wù)端會(huì)回復(fù):Pyvrag?0?fraq??Zrffntr!

    在這個(gè)代碼中沒有使用bufferevent這個(gè)強(qiáng)大的東西,在一個(gè)結(jié)構(gòu)體中自己管理了一個(gè)緩沖區(qū)。結(jié)構(gòu)體為:

    [cpp] view plain copy
  • struct?fd_state?{??
  • ????char?buffer[MAX_LINE];//緩沖區(qū)的大小??
  • ????size_t?buffer_used;//接收到已經(jīng)使用的buffer大小,每次將接收到的數(shù)據(jù)字節(jié)數(shù)相加,當(dāng)發(fā)送的字節(jié)數(shù)累計(jì)相加和buffer_used都相等時(shí)候,將它們都置為1??
  • ??
  • ????size_t?n_written;//發(fā)送的累加字節(jié)數(shù)??
  • ????size_t?write_upto;//相當(dāng)于一個(gè)臨時(shí)變量,當(dāng)遇到換行符的時(shí),將其收到的字節(jié)數(shù)(換行符除外)賦給該值,當(dāng)檢測到寫事件的時(shí)候,用已經(jīng)發(fā)送的字節(jié)數(shù)和該數(shù)值做比較,若收到的字節(jié)總數(shù)小于該值,則發(fā)送數(shù)據(jù),等于該值,將結(jié)構(gòu)體中3個(gè)字節(jié)數(shù)統(tǒng)計(jì)變量都置為1,為什么會(huì)置為1呢,因?yàn)橛幸粋€(gè)換行符吧。??
  • ??
  • ????struct?event?*read_event;??
  • ????struct?event?*write_event;??
  • };??
  • 代碼中自己管理了一個(gè)緩沖區(qū),用于存放接收到的數(shù)據(jù),發(fā)送的數(shù)據(jù)將其轉(zhuǎn)換后也放入該緩沖區(qū)中,代碼晦澀難懂,我也是經(jīng)過打日志分析后,才明白點(diǎn),這個(gè)緩沖區(qū)自己還得控制好。但是libevent?2已經(jīng)提供了一個(gè)神器bufferevent,我們?cè)谑褂玫倪^程中最好不要自己管理這個(gè)緩沖區(qū),之所以分析這個(gè)代碼,是為了熟悉libevent?做服務(wù)端程序的流程及原理。

    下面是代碼,加有部分注釋和日志:

    代碼:lowlevel_libevent_server.c?

    [cpp] view plain copy
  • //說明,為了使我們的代碼兼容win32網(wǎng)絡(luò)API,我們使用evutil_socket_t代替int,使用evutil_make_socket_nonblocking代替fcntl??
  • ??
  • /*?For?sockaddr_in?*/??
  • #include?<netinet/in.h>??
  • /*?For?socket?functions?*/??
  • #include?<sys/socket.h>??
  • /*?For?fcntl?*/??
  • #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)??
  • {??
  • ????/*?We?don't?want?to?use?isalpha?here;?setting?the?locale?would?change?
  • ?????*?which?characters?are?considered?alphabetical.?*/??
  • ????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))//如果讀事件的緩沖區(qū)還未滿,將收到的數(shù)據(jù)做轉(zhuǎn)換??
  • ????????????????state->buffer[state->buffer_used++]?=?rot13_char(buf[i]);??
  • //??????????????state->buffer[state->buffer_used++]?=?buf[i];//接收什么發(fā)送什么,不經(jīng)過轉(zhuǎn)換,測試用??
  • ????????????if?(buf[i]?==?'\n')?//如果遇到換行,添加寫事件,并設(shè)置寫事件的大小??
  • ????????????{??
  • ????????????????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);??
  • }??
  • ??
  • ????//判斷最后一次接收的字節(jié)數(shù)??
  • ????if?(result?==?0)??
  • ????{??
  • ????????free_fd_state(state);??
  • ????}??
  • ????else?if?(result?<?0)??
  • ????{??
  • ????????if?(errno?==?EAGAIN)?//?XXXX?use?evutil?macro??
  • ????????????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)?//?XXX?use?evutil?macro??
  • ????????????????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)??
  • ????{?//?XXXX?eagain????
  • ????????perror("accept");??
  • ????}??
  • ????else?if?(fd?>?FD_SETSIZE)??
  • ????{??
  • ????????close(fd);?//?XXX?replace?all?closes?with?EVUTIL_CLOSESOCKET?*/??
  • ????}??
  • ????else??
  • ????{??
  • ????????struct?fd_state?*state;??
  • ????????evutil_make_socket_nonblocking(fd);??
  • ????????state?=?alloc_fd_state(base,?fd);??
  • ????????assert(state);?/*XXX?err*/??
  • ????????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();//初始化libevent??
  • ????if?(!base)??
  • ????????return;?/*XXXerr*/??
  • ??
  • ????sin.sin_family?=?AF_INET;??
  • ????sin.sin_addr.s_addr?=?0;//本機(jī)??
  • ????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);??
  •   ????/*XXX?check?it?*/??
  •   ????event_add(listener_event,?NULL);??
  •   ??
  •   ????event_base_dispatch(base);??
  •   }??
  •   ??
  •   int?main(int?c,?char?**v)??
  •   {??
  •   //????setvbuf(stdout,?NULL,?_IONBF,?0);??
  •   ??
  •   ????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)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。