青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

   C++ 技術(shù)中心

   :: 首頁 :: 聯(lián)系 ::  :: 管理
  160 Posts :: 0 Stories :: 87 Comments :: 0 Trackbacks

公告

鄭重聲明:本BLOG所發(fā)表的原創(chuàng)文章,作者保留一切權(quán)利。必須經(jīng)過作者本人同意后方可轉(zhuǎn)載,并注名作者(天空)和出處(CppBlog.com)。作者Email:coder@luckcoder.com

留言簿(27)

搜索

  •  

最新隨筆

最新評論

評論排行榜

出處:Blog of Felix021
時間:Sat, 25 Feb 2012 00:43:26 +0000
作者:felix021
地址:http://www.felix021.com/blog/read.php?2068
內(nèi)容:
花了兩天的時間在libevent上,想總結(jié)下,就以寫簡單tutorial的方式吧,貌似沒有一篇簡單的說明,讓人馬上就能上手用的。
首先給出官方文檔吧: http://libevent.org ,首頁有個Programming with Libevent,里面是一節(jié)一節(jié)的介紹libevent,但是感覺信息量太大了,而且還是英文的-。-(當(dāng)然,如果想好好用libevent,看看還是很有必要的),還有個Reference,大致就是對各個版本的libevent使用doxgen生成的文檔,用來查函數(shù)原型和基本用法什么的。
下面假定已經(jīng)學(xué)習(xí)過基本的socket編程(socket,bind,listen,accept,connect,recv,send,close),并且對異步/callback有基本認(rèn)識。
基本的socket編程是阻塞/同步的,每個操作除非已經(jīng)完成或者出錯才會返回,這樣對于每一個請求,要使用一個線程或者單獨(dú)的進(jìn)程去處理,系統(tǒng)資源沒法支撐大量的請求(所謂c10k problem?),例如內(nèi)存:默認(rèn)情況下每個線程需要占用2~8M的棧空間。posix定義了可以使用異步的select系統(tǒng)調(diào)用,但是因為其采用了輪詢的方式來判斷某個fd是否變成active,效率不高[O(n)],連接數(shù)一多,也還是撐不住。于是各系統(tǒng)分別提出了基于異步/callback的系統(tǒng)調(diào)用,例如Linux的epoll,BSD的kqueue,Windows的IOCP。由于在內(nèi)核層面做了支持,所以可以用O(1)的效率查找到active的fd。基本上,libevent就是對這些高效IO的封裝,提供統(tǒng)一的API,簡化開發(fā)。
libevent大概是這樣的:
    默認(rèn)情況下是單線程的(可以配置成多線程,如果有需要的話),每個線程有且只有一個event_base,對應(yīng)一個struct event_base結(jié)構(gòu)體(以及附于其上的事件管理器),用來schedule托管給它的一系列event,可以和操作系統(tǒng)的進(jìn)程管理類比,當(dāng)然,要更簡單一點。當(dāng)一個事件發(fā)生后,event_base會在合適的時間(不一定是立即)去調(diào)用綁定在這個事件上的函數(shù)(傳入一些預(yù)定義的參數(shù),以及在綁定時指定的一個參數(shù)),直到這個函數(shù)執(zhí)行完,再返回schedule其他事件。//創(chuàng)建一個event_base
struct event_base *base = event_base_new();
assert(base != NULL);
    event_base內(nèi)部有一個循環(huán),循環(huán)阻塞在epoll/kqueue等系統(tǒng)調(diào)用上,直到有一個/一些事件發(fā)生,然后去處理這些事件。當(dāng)然,這些事件要被綁定在這個event_base上。每個事件對應(yīng)一個struct event,可以是監(jiān)聽一個fd或者POSIX信號量之類(這里只講fd了,其他的看manual吧)。struct event使用event_new來創(chuàng)建和綁定,使用event_add來啟用://創(chuàng)建并綁定一個event
struct event *listen_event;
//參數(shù):event_base, 監(jiān)聽的fd,事件類型及屬性,綁定的回調(diào)函數(shù),給回調(diào)函數(shù)的參數(shù)
listen_event = event_new(base, listener, EV_READ|EV_PERSIST, callback_func, (void*)base);
//參數(shù):event,超時時間(struct timeval *類型的,NULL表示無超時設(shè)置)
event_add(listen_event, NULL);
    注:libevent支持的事件及屬性包括(使用bitfield實現(xiàn),所以要用 | 來讓它們合體)
    (a) EV_TIMEOUT: 超時
    (b) EV_READ: 只要網(wǎng)絡(luò)緩沖中還有數(shù)據(jù),回調(diào)函數(shù)就會被觸發(fā)
    (c) EV_WRITE: 只要塞給網(wǎng)絡(luò)緩沖的數(shù)據(jù)被寫完,回調(diào)函數(shù)就會被觸發(fā)
    (d) EV_SIGNAL: POSIX信號量,參考manual吧
    (e) EV_PERSIST: 不指定這個屬性的話,回調(diào)函數(shù)被觸發(fā)后事件會被刪除
    (f) EV_ET: Edge-Trigger邊緣觸發(fā),參考EPOLL_ET
    然后需要啟動event_base的循環(huán),這樣才能開始處理發(fā)生的事件。循環(huán)的啟動使用event_base_dispatch,循環(huán)將一直持續(xù),直到不再有需要關(guān)注的事件,或者是遇到event_loopbreak()/event_loopexit()函數(shù)。//啟動事件循環(huán)
event_base_dispatch(base);
    接下來關(guān)注下綁定到event的回調(diào)函數(shù)callback_func:傳遞給它的是一個socket fd、一個event類型及屬性bit_field、以及傳遞給event_new的最后一個參數(shù)(去上面幾行回顧一下,把event_base給傳進(jìn)來了,實際上更多地是分配一個結(jié)構(gòu)體,把相關(guān)的數(shù)據(jù)都撂進(jìn)去,然后丟給event_new,在這里就能取得到了)。其原型是:typedef void(* event_callback_fn)(evutil_socket_t sockfd, short event_type, void *arg)
    對于一個服務(wù)器而言,上面的流程大概是這樣組合的:
    1. listener = socket(),bind(),listen(),設(shè)置nonblocking(POSIX系統(tǒng)中可使用fcntl設(shè)置,windows不需要設(shè)置,實際上libevent提供了統(tǒng)一的包裝evutil_make_socket_nonblocking)
    2. 創(chuàng)建一個event_base
    3. 創(chuàng)建一個event,將該socket托管給event_base,指定要監(jiān)聽的事件類型,并綁定上相應(yīng)的回調(diào)函數(shù)(及需要給它的參數(shù))。對于listener socket來說,只需要監(jiān)聽EV_READ|EV_PERSIST
    4. 啟用該事件
    5. 進(jìn)入事件循環(huán)
    ---------------
    6. (異步) 當(dāng)有client發(fā)起請求的時候,調(diào)用該回調(diào)函數(shù),進(jìn)行處理。
    問題:為什么不在listen完馬上調(diào)用accept,獲得客戶端連接以后再丟給event_base呢?這個問題先想想噢。
    回調(diào)函數(shù)要做什么事情呢?當(dāng)然是處理client的請求了。首先要accept,獲得一個可以與client通信的sockfd,然后……調(diào)用recv/send嗎?錯!大錯特錯!如果直接調(diào)用recv/send的話,這個線程就阻塞在這個地方了,如果這個客戶端非常的陰險(比如一直不發(fā)消息,或者網(wǎng)絡(luò)不好,老是丟包),libevent就只能等它,沒法處理其他的請求了——所以應(yīng)該創(chuàng)建一個新的event來托管這個sockfd。
    在老版本libevent上的實現(xiàn),比較羅嗦[如果不想詳細(xì)了解的話,看下一部分]。
    對于服務(wù)器希望先從client獲取數(shù)據(jù)的情況,大致流程是這樣的:
    1. 將這個sockfd設(shè)置為nonblocking
    2. 創(chuàng)建2個event:
        event_read,綁上sockfd的EV_READ|EV_PERSIST,設(shè)置回調(diào)函數(shù)和參數(shù)(后面提到的struct)
        event_write,綁上sockfd的EV_WRITE|EV_PERSIST,設(shè)置回調(diào)函數(shù)和參數(shù)(后面提到的struct)
    3. 啟用event_read事件
    ------
    4. (異步) 等待event_read事件的發(fā)生, 調(diào)用相應(yīng)的回調(diào)函數(shù)。這里麻煩來了:回調(diào)函數(shù)用recv讀入的數(shù)據(jù),不能直接用send丟給sockfd了事——因為sockfd是nonblocking的,丟給它的話,不能保證正確(為什么呢?)。所以需要一個自己管理的緩存用來保存讀入的數(shù)據(jù)中(在accept以后就創(chuàng)建一個struct,作為第2步回調(diào)函數(shù)的arg傳進(jìn)來),在合適的時間(比如遇到換行符)啟用event_write事件【event_add(event_write, NULL)】,等待EV_WRITE事件的觸發(fā)
    ------
    5. (異步) 當(dāng)event_write事件的回調(diào)函數(shù)被調(diào)用的時候,往sockfd寫入數(shù)據(jù),然后刪除event_write事件【event_del(event_write)】,等待event_read事件的下一次執(zhí)行。
    以上步驟比較晦澀,具體代碼可參考官方文檔里面的【Example: A low-level ROT13 server with Libevent】
    由于需要自己管理緩沖區(qū),且過程晦澀難懂,并且不兼容于Windows的IOCP,所以libevent2開始,提供了bufferevent這個神器,用來提供更加優(yōu)雅、易用的API。struct bufferevent內(nèi)建了兩個event(read/write)和對應(yīng)的緩沖區(qū)【struct evbuffer *input, *output】,并提供相應(yīng)的函數(shù)用來操作緩沖區(qū)(或者直接操作bufferevent)。每當(dāng)有數(shù)據(jù)被讀入input的時候,read_cb函數(shù)被調(diào)用;每當(dāng)output被輸出完的時候,write_cb被調(diào)用;在網(wǎng)絡(luò)IO操作出現(xiàn)錯誤的情況(連接中斷、超時、其他錯誤),error_cb被調(diào)用。于是上一部分的步驟被簡化為:
    1. 設(shè)置sockfd為nonblocking
    2. 使用bufferevent_socket_new創(chuàng)建一個struct bufferevent *bev,關(guān)聯(lián)該sockfd,托管給event_base
    3. 使用bufferevent_setcb(bev, read_cb, write_cb, error_cb, (void *)arg)將EV_READ/EV_WRITE對應(yīng)的函數(shù)
    4. 使用bufferevent_enable(bev, EV_READ|EV_WRITE|EV_PERSIST)來啟用read/write事件
    ------
    5. (異步)
        在read_cb里面從input讀取數(shù)據(jù),處理完畢后塞到output里(會被自動寫入到sockfd)
        在write_cb里面(需要做什么嗎?對于一個echo server來說,read_cb就足夠了)
        在error_cb里面處理遇到的錯誤
    *. 可以使用bufferevent_set_timeouts(bev, struct timeval *READ, struct timeval *WRITE)來設(shè)置讀寫超時, 在error_cb里面處理超時。
    *. read_cb和write_cb的原型是
         void read_or_write_callback(struct bufferevent *bev, void *arg)
       error_cb的原型是
         void error_cb(struct bufferevent *bev, short error, void *arg) //這個是event的標(biāo)準(zhǔn)回調(diào)函數(shù)原型
       可以從bev中用libevent的API提取出event_base、sockfd、input/output等相關(guān)數(shù)據(jù),詳情RTFM~
    
    于是代碼簡化到只需要幾行的read_cb和error_cb函數(shù)即可:
void read_cb(struct bufferevent *bev, void *arg) {
    char line[256];
    int n;
    evutil_socket_t fd = bufferevent_getfd(bev);
    while (n = bufferevent_read(bev, line, 256), n > 0)
        bufferevent_write(bev, line, n);
}
void error_cb(struct bufferevent *bev, short event, void *arg) {
    bufferevent_free(bev);
}
    于是一個支持大并發(fā)量的echo server就成型了!下面附上無注釋的echo server源碼,110行,多抄幾遍,就能完全弄懂啦!更復(fù)雜的例子參見官方文檔里面的【Example: A simpler ROT13 server with Libevent】
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
#define LISTEN_PORT 9999
#define LISTEN_BACKLOG 32
void do_accept(evutil_socket_t listener, short event, void *arg);
void read_cb(struct bufferevent *bev, void *arg);
void error_cb(struct bufferevent *bev, short event, void *arg);
void write_cb(struct bufferevent *bev, void *arg);
int main(int argc, char *argv[])
{
    int ret;
    evutil_socket_t listener;
    listener = socket(AF_INET, SOCK_STREAM, 0);
    assert(listener > 0);
    evutil_make_listen_socket_reuseable(listener);
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = 0;
    sin.sin_port = htons(LISTEN_PORT);
    if (bind(listener, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
        perror("bind");
        return 1;
    }
    if (listen(listener, LISTEN_BACKLOG) < 0) {
        perror("listen");
        return 1;
    }
    printf ("Listening...\n");
    evutil_make_socket_nonblocking(listener);
    struct event_base *base = event_base_new();
    assert(base != NULL);
    struct event *listen_event;
    listen_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base);
    event_add(listen_event, NULL);
    event_base_dispatch(base);
    printf("The End.");
    return 0;
}
void do_accept(evutil_socket_t listener, short event, void *arg)
{
    struct event_base *base = (struct event_base *)arg;
    evutil_socket_t fd;
    struct sockaddr_in sin;
    socklen_t slen = sizeof(sin);
    fd = accept(listener, (struct sockaddr *)&sin, &slen);
    if (fd < 0) {
        perror("accept");
        return;
    }
    if (fd > FD_SETSIZE) { //這個if是參考了那個ROT13的例子,貌似是官方的疏漏,從select-based例子里抄過來忘了改
        perror("fd > FD_SETSIZE\n");
        return;
    }
    printf("ACCEPT: fd = %u\n", fd);
    struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    bufferevent_setcb(bev, read_cb, NULL, error_cb, arg);
    bufferevent_enable(bev, EV_READ|EV_WRITE|EV_PERSIST);
}
void read_cb(struct bufferevent *bev, void *arg)
{
#define MAX_LINE    256
    char line[MAX_LINE+1];
    int n;
    evutil_socket_t fd = bufferevent_getfd(bev);
    while (n = bufferevent_read(bev, line, MAX_LINE), n > 0) {
        line[n] = '\0';
        printf("fd=%u, read line: %s\n", fd, line);
        bufferevent_write(bev, line, n);
    }
}
void write_cb(struct bufferevent *bev, void *arg) {}
void error_cb(struct bufferevent *bev, short event, void *arg)
{
    evutil_socket_t fd = bufferevent_getfd(bev);
    printf("fd = %u, ", fd);
    if (event & BEV_EVENT_TIMEOUT) {
        printf("Timed out\n"); //if bufferevent_set_timeouts() called
    }
    else if (event & BEV_EVENT_EOF) {
        printf("connection closed\n");
    }
    else if (event & BEV_EVENT_ERROR) {
        printf("some other error\n");
    }
    bufferevent_free(bev);
}
posted on 2015-05-03 20:28 C++技術(shù)中心 閱讀(694) 評論(0)  編輯 收藏 引用 所屬分類: Windows 網(wǎng)絡(luò)編程
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久久免费观看视频| 欧美成人资源网| 欧美在线999| 亚洲五月六月| 亚洲免费一级电影| 午夜精品国产精品大乳美女| 欧美一区二粉嫩精品国产一线天| 欧美在线观看天堂一区二区三区| 夜夜爽99久久国产综合精品女不卡| 日韩午夜在线电影| 亚洲一二三级电影| 久久精品理论片| 欧美国产一区二区在线观看| 亚洲免费观看高清完整版在线观看| 日韩一级精品视频在线观看| 亚洲午夜激情免费视频| 久久国产福利| 欧美精品观看| 国产日韩欧美综合在线| 在线播放一区| 亚洲一区二区三区免费观看| 久久午夜精品一区二区| 亚洲狼人综合| 久久久久成人网| 欧美日本簧片| 韩国女主播一区| 亚洲私人黄色宅男| 欧美岛国激情| 亚洲欧美美女| 欧美欧美午夜aⅴ在线观看| 国产日韩精品一区| 日韩视频三区| 久久综合伊人77777| 一区二区日韩伦理片| 猫咪成人在线观看| 国产日韩一区二区三区| 一区二区三区色| 久久久久久久国产| 亚洲一区在线免费| 欧美日韩国产高清视频| 狠狠色丁香久久婷婷综合丁香| 9l国产精品久久久久麻豆| 美女黄毛**国产精品啪啪| 亚洲私人黄色宅男| 欧美日韩中文在线观看| 亚洲精品视频中文字幕| 玖玖国产精品视频| 午夜精品视频在线观看| 国产精品jizz在线观看美国| 99视频一区二区三区| 欧美激情亚洲另类| 毛片基地黄久久久久久天堂| 国产亚洲一区精品| 亚洲淫性视频| 中文久久精品| 欧美三区不卡| 亚洲午夜成aⅴ人片| 黄网站色欧美视频| 亚洲国产精品热久久| 99亚洲精品| 91久久精品国产91性色tv| 亚洲一区在线播放| 国产精品免费一区二区三区观看 | 欧美黄色影院| 亚洲国产精品一区在线观看不卡 | 美女国产精品| 久热精品视频在线免费观看| 亚洲第一区在线观看| 欧美大尺度在线| 欧美国产一区二区在线观看| 亚洲国产精品va在线看黑人动漫| 蜜桃久久精品乱码一区二区| 久久中文精品| 亚洲精品一区久久久久久| 亚洲激情成人| 欧美网站在线观看| 久久se精品一区精品二区| 久久久精品国产一区二区三区 | 国产日韩在线一区| 老司机午夜免费精品视频| 久久全国免费视频| 夜夜夜久久久| 亚洲尤物影院| 伊人春色精品| 最新国产成人av网站网址麻豆| 欧美片第一页| 久久九九99| 毛片精品免费在线观看| 亚洲午夜在线观看视频在线| 亚洲一区二区三区中文字幕在线| 海角社区69精品视频| 欧美国产日韩视频| 国产精品视频精品视频| 欧美大片免费久久精品三p| 欧美日韩国产区一| 久久久久久久久久看片| 欧美伦理a级免费电影| 欧美在线综合| 欧美人与性禽动交情品| 久久久久久噜噜噜久久久精品| 老司机精品久久| 欧美一区二区三区在线看| 你懂的网址国产 欧美| 久久精品卡一| 欧美日韩一区二区三区四区五区| 久久精品亚洲精品国产欧美kt∨| 欧美激情片在线观看| 久久国产精品免费一区| 欧美韩日亚洲| 老司机一区二区三区| 国产精品第三页| 欧美激情视频在线免费观看 欧美视频免费一 | 在线亚洲精品| 麻豆久久久9性大片| 午夜精品一区二区三区电影天堂 | 国产一区香蕉久久| 亚洲精品美女在线观看播放| 国产一区99| 中文日韩欧美| 一区二区三区蜜桃网| 久久久久这里只有精品| 欧美在线综合视频| 国产精品视屏| 99热这里只有精品8| 亚洲国产婷婷香蕉久久久久久| 欧美中文在线免费| 欧美在线网站| 国产精品视频内| 99视频在线观看一区三区| 99亚洲一区二区| 欧美片在线观看| 亚洲国产日韩一区| 亚洲美女91| 欧美日韩不卡| 日韩视频精品在线观看| 亚洲最黄网站| 欧美日韩国产美女| 日韩视频在线你懂得| 亚洲手机视频| 欧美性猛交xxxx乱大交蜜桃| a91a精品视频在线观看| 亚洲一区999| 欧美性猛交视频| 亚洲自啪免费| 久久久精品动漫| 国产一区二区三区黄视频| 欧美一区永久视频免费观看| 久久久午夜精品| 激情综合色综合久久| 麻豆av一区二区三区| 亚洲日本va午夜在线电影| 亚洲香蕉在线观看| 国产精品区一区| 欧美一区二区三区在线看| 久久久夜色精品亚洲| 亚洲国产高清在线观看视频| 模特精品裸拍一区| 亚洲麻豆国产自偷在线| 香蕉国产精品偷在线观看不卡| 国产午夜精品久久久久久免费视 | 欧美成人影音| 99综合在线| 久久国产精品99国产| 91久久久久| 国产精品成人一区二区艾草| 亚洲伊人久久综合| 欧美岛国激情| 午夜一区二区三区不卡视频| 国产一区二区三区在线观看网站 | 亚洲精品久久7777| 99在线精品观看| 国产精品久久久久久亚洲调教 | 欧美性一区二区| 欧美亚洲三区| 亚洲级视频在线观看免费1级| 亚洲天堂成人| 国产综合色产| 欧美日韩国产一区二区三区| 欧美一区二区三区的| 91久久精品一区| 久久精品一区二区国产| 日韩视频在线观看一区二区| 国产欧美一区二区精品秋霞影院 | 国产精品入口麻豆原神| 久久一区亚洲| 亚洲欧美成人网| 亚洲精品美女久久7777777| 久久久久国产精品一区| 中文有码久久| 最新日韩在线| 国产一区二区三区直播精品电影| 欧美日韩美女一区二区| 另类图片国产| 久久aⅴ国产欧美74aaa| 亚洲视频一二三| 亚洲免费观看视频| 91久久精品美女| 亚洲大胆人体视频| 蜜臀久久99精品久久久久久9| 久久av二区|