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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

LibEvent中文帮助文档--第1、2、3、4章

發布時間:2023/12/14 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LibEvent中文帮助文档--第1、2、3、4章 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


?

?LibEvent中文幫助文檔--第1、2、3、4章

返回主目錄

Libevent

快速可移植非阻塞式網絡編程

?

?

修訂歷史

版本

日期

作者

備注

V1.0

2016-11-15

周勇

Libevent編程中文幫助文檔

?

文檔是2009-2012年由Nick-Mathewson基于Attribution-Noncommercial-Share Alike許可協議3.0創建,未來版本將會使用約束性更低的許可來創建.

此外,本文檔的源代碼示例也是基于BSD"3條款""修改"條款.詳情請參考BSD文件全部條款.本文檔最新下載地址:

英文:http://libevent.org/

中文:http://blog.csdn.net/zhouyongku/article/details/53431597

請下載并運行"gitclonegit://github.com/nmathewson/libevent- book.git"獲取本文檔描述的最新版本源碼.

返回主目錄

1.關于本文檔

為了更好掌握Libevent(2.0)進行快速可移植的異步IO網絡編程,你需要具備:

  • C語言基本知識

  • C語言網絡開發函數調用(socket(),connect()).

2.示例代碼注意事項

本文檔描述的源碼示例需要運行在LinuxFreeBSDOpenBSDNetBSDMacOSXSolarisAndroid這些操作系統中,Windows環境下編譯可能會有一些不兼容的情況發生.

3.一個小的異步IO例子

許多初學者往往都是使用阻塞式IO調用進行編程.當你調用一個同步IO的時候,除非操作系統已經完成了操作或者時間長到你的網絡堆棧放棄的時候,否則系統是不會返回完成的.舉個例子,當你調用"connect"做一個TCP連接的時候,你的操作系統必須排隊處理來自發送到服務器的SYN,除非等到SYN_ACK包從對面接收到,或者是超時,否則操作是不會返回給你的應用程序.

?

TCP三次握手

第一次握手:建立連接時,客戶端發送syn(syn=j)到服務器,并進入SYN_SENT狀態,等待服務器確認;SYN:同步序列編號(Synchronize Sequence Numbers).第二次握手:服務器收到syn,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN(syn=k),SYN+ACK,此時服務器進入SYN_RECV狀態;第三次握手:客戶端收到服務器的SYN+ACK,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態,完成三次握手.

這里有一個很簡單的阻塞式網絡調用的例子,它打開一個連接到www.google.com,發送它簡單的HTTP請求,并打印輸出到stdout.

示例:一個簡單的阻塞式HTTP客戶端

/* For sockaddr_in*/ #include <netinet/in.h> /* For socket functions*/ #include <sys/socket.h> /* For gethostbyname*/ #include <netdb.h> #include <unistd.h> #include <string.h> #include <stdio.h> int main(int c, char** v) { const char query[] = "GET / HTTP/1.0\r\n" "Host: www.google.com\r\n" "\r\n"; const char hostname[] = "www.google.com"; struct sockaddr_in sin; struct hostent* h; const char* cp; int fd; ssize_t n_written, remaining; char buf[1024]; /* Look up the IP address for the hostname. Watch out; this isn’t threadsafe on most platforms.*/ h = gethostbyname(hostname); if (!h) { fprintf(stderr, "Couldn’t lookup %s: %s", hostname, hstrerror(h_errno));return 1; } if (h->h_addrtype != AF_INET) {fprintf(stderr, "No ipv6 support, sorry.");return 1; } /* Allocate a new socket*/ fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) {perror("socket");return 1; } /* Connect to the remote host*/ sin.sin_family = AF_INET; sin.sin_port = htons(80); sin.sin_addr = * (struct in_addr * )h->h_addr; if (connect(fd, (struct sockaddr * ) &sin, sizeof(sin))) {perror("connect");close(fd);return 1; } /* Write the query.*/ /* XXX Can send succeed partially?*/ cp = query; remaining = strlen(query); while (remaining) {n_written = send(fd, cp, remaining, 0);if (n_written <= 0) {perror("send");return 1; } remaining -= n_written; cp += n_written; } /* Get an answer back.*/ while (1) {ssize_t result = recv(fd, buf, sizeof(buf), 0);if (result == 0) {break; } else if (result < 0) {perror("recv");close(fd);return 1; } fwrite(buf, 1, result, stdout); } close(fd); return 0; }

我們可以看出,上面的代碼均是阻塞式操作:調用gethostbyname的去解析www.google.com的時候不等到它成功或者失敗不會返回;調用connect的時候不等到它連接成功不會返回;調用recv的時候不等到它接受到數據或關連接關閉的時候不會返回;同樣,調用send的時候至少等到待把輸出區間待發送數據刷新到內核的寫緩沖區之后才會返回.

?

?

現在看起來阻塞式IO還沒有讓人多厭煩,因為當你的程序在處理網絡IO的同時不會處理其它業務,那么阻塞式IO能滿足你的編程需求,但是想一下,當你想寫一個程序支持多個連接,比如說需要同時從兩個連接中讀取數據,那么這個時候你就不知道到底先從哪個連接讀取數據.

一個糟糕的例子:

/* This won’t work.*/ char buf[1024]; int i, n; while (i_still_want_to_read()) { for (i=0; i<n_sockets; ++i) { n = recv(fd[i], buf, sizeof(buf), 0); if (n==0) handle_close(fd[i]); else if (n<0) handle_error(fd[i], errno); else handle_input(fd[i], buf, n); } }

因為如果數據到達fd[2]首先,程序甚至不會嘗試從fd[2]讀取數據,直到讀取fd[0]fd[1]得到一些完成數據的讀取.

?

有時候人們使用多線程或多進程服務來解決這個問題.最簡單的方法就是讓單獨的每個進程或線程來處理它們各自的連接.由于每個連接都有自己的處理過程所以等待一個連接過程的阻塞IO方法的調用將不會影響到其它任何別的連接的處理過程.

?

這里有一個別的程序示例.這是一個很小的服務器,在端口40713上偵聽TCP連接,每次一條一條地將數據讀出來,當數據讀出來的時候立刻進行R13加密,一條一條地寫進輸出緩沖區,在這個過程中它調用Unix fork()來創建一個新的過程來處理服務器接受到的連接.

示例:Forking ROT13 服務器

* For sockaddr_in*/ #include <netinet/in.h> /* For socket functions*/ #include <sys/socket.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #define MAX_LINE 16384 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; } void child(int fd) { char outbuf[MAX_LINE+1]; size_t outbuf_used = 0; ssize_t result; while (1) { char ch; result = recv(fd, &ch, 1, 0); if (result == 0) { break; } else if (result == -1) { perror("read"); break; } /* We do this test to keep the user from overflowing the buffer.*/ if (outbuf_used < sizeof(outbuf)) { outbuf[outbuf_used++] = rot13_char(ch); } if (ch == ’\n’) { send(fd, outbuf, outbuf_used, 0); outbuf_used = 0; continue; } } } void run(void) { int listener; struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; sin.sin_port = htons(40713); listener = socket(AF_INET, SOCK_STREAM, 0); #ifndef WIN32 {int one = 1;setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); } #endifif (bind(listener, (struct sockaddr * )&sin, sizeof(sin)) < 0) {perror("bind");return; } if (listen(listener, 16)<0) {perror("listen");return; } while (1) {struct sockaddr_storage ss;socklen_t slen = sizeof(ss);int fd = accept(listener, (struct sockaddr * )&ss, &slen);if (fd < 0) {perror("accept");} else {if (fork() == 0) {child(fd);exit(0);}} } } int main(int c, char** v) { run(); return 0; }

那么,我們有了一個完美的解決方案來處理多個即時連接,我就可以停這本書去做別的事了么?不完全是.首先,進程創建(甚至線程創建)的代價在一些平臺上可能會非常昂貴.在實際中,你可能更想使用一個線程池,而不是創建新的進程.但更重要的是,線程在規模上并不盡如人意.如果您的程序同事需要處理成千上萬的連接,可能會效率極低因為cpu同時運行的線程只有屈指可數的幾個.

?

但是如果連多線程都不能解決多個連接中的問題的話,那么還有什么別的方法?在Unix編程中,你就需要將你的socket設置成為非阻塞模式.Unix的調用方法如下:

fcntl(fd, F_SETFL, O_NONBLOCK);

這里fd代表的是socket的文件描述符.一旦你設置fd(套接字)為非阻塞模式,從那以后,無論什么時候,你調用fd函數都將立即完成操作或返回表示?"現在我無法取得任何進展,再試一次"的錯誤碼.所以我們的兩個套接字的例子可能會天真地寫成下面的代碼:

?

糟糕的例子:忙查詢所有套接字

/* This will work, but the performance will be unforgivably bad.*/ int i, n; char buf[1024]; for (i=0; i < n_sockets; ++i)fcntl(fd[i], F_SETFL, O_NONBLOCK); while (i_still_want_to_read()) {for (i=0; i < n_sockets; ++i) {n = recv(fd[i], buf, sizeof(buf), 0);if (n == 0) {handle_close(fd[i]);} else if (n < 0){if (errno == EAGAIN) ; /* The kernel didn’t have any data for us to read.*/else handle_error(fd[i], errno);} else {handle_input(fd[i], buf, n);}} }

現在我們正在使用非阻塞套接字,上面的代碼會有效,但只有很少.性能將會很糟糕,有兩個原因:首先,當任何連接中都沒有數據的時候,該循環將繼續,并且消耗完你的CPU;其次,如果你想處理一個或兩個以上連接,無論有沒有數據你都需要為每個連接調用系統內核.所以我們需要一種方法告訴內核:你必須等到其中一個socket已經準備好給我數據才通知我,并且告訴我是哪一個socket.解決這個問題人們常用的是select()方法.Select()方法的調用需要三套fd(數組),一個作為寫,一個作為讀,一個作為異常.該函數等待socket集合,并且修改這個集合以知道哪些socket可以使用了.

下面是一個select()例子:

示例:使用select()

/* If you only have a couple dozen fds, this version won’t be awful*/ fd_set readset; int i, n; char buf[1024]; while (i_still_want_to_read()) {int maxfd = -1;FD_ZERO(&readset);/* Add all of the interesting fds to readset*/for (i=0; i < n_sockets; ++i) {if (fd[i]>maxfd) maxfd = fd[i];FD_SET(fd[i], &readset);}/* Wait until one or more fds are ready to read*/select(maxfd+1, &readset, NULL, NULL, NULL);/* Process all of the fds that are still set in readset*/for (i=0; i < n_sockets; ++i) {if (FD_ISSET(fd[i], &readset)) {n = recv(fd[i], buf, sizeof(buf), 0);if (n == 0) {handle_close(fd[i]);}else if (n < 0){if (errno == EAGAIN); /* The kernel didn’t have any data for us to read.*/elsehandle_error(fd[i], errno);} else{handle_input(fd[i], buf, n);}}} }

這是我們ROT13服務器重新實現,使用select().

示例:基于select()ROT13服務器

/* For sockaddr_in*/ #include <netinet/in.h> /* For socket functions*/ #include <sys/socket.h> /* For fcntl*/ #include <fcntl.h> /* for select*/ #include <sys/select.h> #include <assert.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #define MAX_LINE 16384char rot13_char(char c) {/* We don’t want to use isalpha here; setting the locale 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;elsereturn c; } struct fd_state {char buffer[MAX_LINE];size_t buffer_used;int writing;size_t n_written;size_t write_upto; }; struct fd_state*alloc_fd_state(void) {struct fd_state* state = malloc(sizeof(struct fd_state));if (!state)return NULL;state->buffer_used = state->n_written = state->writing =state->write_upto = 0;return state; } void free_fd_state(struct fd_state* state) {free(state); } void make_nonblocking(int fd) {fcntl(fd, F_SETFL, O_NONBLOCK); } int do_read(int fd, struct fd_state* state) {char buf[1024];int i;ssize_t result;while (1) {result = recv(fd, buf, sizeof(buf), 0);if (result <= 0)break;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’) {state->writing = 1;state->write_upto = state->buffer_used;}}}if (result == 0) {return 1;} else if (result < 0) {if (errno == EAGAIN)return 0;return -1;}return 0; } int do_write(int fd, struct fd_state * state) {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 0;return -1;}assert(result != 0);state->n_written += result;}if (state->n_written == state->buffer_used)state->n_written = state->write_upto = state->buffer_used = 0;state->writing = 0;return 0; } void run(void) {int listener;struct fd_state* state[FD_SETSIZE];struct sockaddr_in sin;int i, maxfd;fd_set readset, writeset, exset;sin.sin_family = AF_INET;sin.sin_addr.s_addr = 0;sin.sin_port = htons(40713);for (i = 0; i < FD_SETSIZE; ++i)state[i] = NULL;listener = socket(AF_INET, SOCK_STREAM, 0);make_nonblocking(listener); #ifndef WIN32{int one = 1;setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));} #endifif (bind(listener, (struct sockaddr * )&sin, sizeof(sin)) < 0) {perror("bind");return;}if (listen(listener, 16)<0) {perror("listen");return;}FD_ZERO(&readset);FD_ZERO(&writeset);FD_ZERO(&exset);while (1) {maxfd = listener;FD_ZERO(&readset);FD_ZERO(&writeset);FD_ZERO(&exset);FD_SET(listener, &readset);for (i=0; i < FD_SETSIZE; ++i) {if (state[i]) {if (i > maxfd)maxfd = i;FD_SET(i, &readset);if (state[i]->writing) {FD_SET(i, &writeset);}}}if (select(maxfd+1, &readset, &writeset, &exset, NULL) < 0) {perror("select");return;}if (FD_ISSET(listener, &readset)) {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 {make_nonblocking(fd);state[fd] = alloc_fd_state();assert(state[fd]);/* XXX */}}for (i=0; i < maxfd+1; ++i) {int r = 0;if (i == listener)continue;if (FD_ISSET(i, &readset)) {r = do_read(i, state[i]);}if (r == 0 && FD_ISSET(i, &writeset)) {r = do_write(i, state[i]);}if (r) {free_fd_state(state[i]);state[i] = NULL;close(i);}}} } int main(int c, char** v) {setvbuf(stdout, NULL, _IONBF, 0);run();return 0; }

這還沒完,因為生成和讀select()的二進制流花費的時間與需要的最大fd成正比,而當socket的數量非常大的時候,select()的花費將會更恐怖.

?

不同的操作系統提供了不同的替代select()功能的函數,例如poll()eopll()kqueqe()evports/dev/poll.這些都比select()具有更好的性能,除了poll()之外增加一個套接字、刪除一個套接字以及通知套接字已經為IO準備好了這些動作的時間花費都是O(1).

?

不幸的是,這些都沒有一個有效的接口統一標準.Linuxeopll(),BSD(包含Darwin)kqueue(),Solarisevports/dev/poll,除此之外這些操作系統沒有別的接口了.所以如果你想要寫一個可移植的高效異步處理應用程序,你需要用抽象的方法封裝這些所有的接口,并且提供其中最有效的方法.

?

這些都是LibEventAPI最底層工作的API,提供了可代替select()的各種方法的統一接口,在運行的計算機上使用可用的最有效的版本.

?

下面有另一個版本的ROT13異步服務器,這一次,我們將使用LibEvent2來代替select(),注意fd_sets已經變為:使用通過select()poll()epoll()kqueue()等一系列函數實現的event_base結構來聚合和分離事件.

?

示例:一個給予LibEvent實現的低級ROT13

/*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 16384 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 wouldchange*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; elsereturn c; }struct fd_state {char buffer[MAX_LINE]; size_t buffer_used; size_t n_written; size_twrite_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[1024]; int i; ssize_t result; while (1){assert(state->write_event); result = recv(fd, buf, sizeof(buf), 0); if(result <= 0)break; 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; } }}if (result == 0){free_fd_state(state); }else if (result < 0){if (errno == EAGAIN) // XXXX use evutil macroreturn ; perror("recv"); free_fd_state(state); } }void do_write(evutil_socket_t fd, short events, void*arg) {struct fd_state*state = arg; 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 macroreturn ; free_fd_state(state); return ; } assert(result != 0); state->n_written += result; }if (state->n_written == state->buffer_used)state->n_written = state->write_upto = state->buffer_used = 1; 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(); if (!base)return ; /*XXXerr */ sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0;sin.sin_port = htons(40713); 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; }

代碼中需要注意:socket的類型定義我們使用evutil_socket_t類型來代替int,使用evutil_make_socket_noblocking來代替fcntl(O_NOBLOCK)生成一個非阻塞式socket.這樣使我們的代碼能夠很好的兼容win32網絡API.

3.1怎樣才能更方便?(Windows下怎么弄)

你可能也注意到了,隨著我們的代碼的效率越來越高,代碼的也越來越復雜.回到之前,我們不必親自管理每個連接的緩沖區,而只需要為每個過程分配棧內存.我們也不必去監控socket是可以讀還是可寫,這些都是自動隱含在我們的代碼中的.我們也不需要一個結構體去跟蹤每個操作有多少完成了,只需要使用循環和棧變量就夠了.

?

此外,如果你深入Windows網絡編程,你可能意識到在上面的例子中LibEvent可能并沒有表現出最優的性能.Windows下要進行高速的異步IO不會使用select()接口,而是使用IOCP(完成端口) API.與其他快速的網絡API不同,socket為操作準備好的時候,程序在即將執行操作之前IOCP是不會通知的.相反,程序會告訴Windows網絡棧開始網絡操作,IOCP會通知程序操作完成.

?

幸運的是,LibEvent2bufferevents接口解決了這兩個問題:首先使程序更加簡單,其次提供了在WindowsLinux上高效運行的接口.

?

下面是我們用bufferevents編寫的最后一個ROT13服務器.

?

示例:使用LibEvent編寫的簡單ROT13服務器

/* For sockaddr_in*/ #include <netinet/in.h> /* For socket functions*/ #include <sys/socket.h> /* For fcntl*/ #include <fcntl.h> #include <event2/event.h> #include <event2/buffer.h> #include <event2/bufferevent.h> #include <assert.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #define MAX_LINE 16384 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 changewhich 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;elsereturn c; } void readcb(struct bufferevent* bev, void * ctx) {struct evbuffer* input, * output;char* line;size_t n;int i;input = bufferevent_get_input(bev);output = bufferevent_get_output(bev);while ((line = evbuffer_readln(input, &n, EVBUFFER_EOL_LF))) {for (i = 0; i < n; ++i)line[i] = rot13_char(line[i]);evbuffer_add(output, line, n);evbuffer_add(output, "\n", 1);free(line);}if (evbuffer_get_length(input) >= MAX_LINE) {/* Too long; just process what there is and go on sothat the buffer doesn’t grow infinitely long.*/char buf[1024];while (evbuffer_get_length(input)) {int n = evbuffer_remove(input, buf, sizeof(buf));for (i = 0; i < n; ++i)buf[i] = rot13_char(buf[i]);evbuffer_add(output, buf, n);}evbuffer_add(output, "\n", 1);} } void errorcb(struct bufferevent* bev, short error, void * ctx) {if (error & BEV_EVENT_EOF) {/* connection has been closed, do any clean up here*/} else if (error & BEV_EVENT_ERROR) {/* check errno to see what error occurred*/} else if (error & BEV_EVENT_TIMEOUT) {/* must be a timeout event handle, handle it*/}bufferevent_free(bev); }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 bufferevent* bev;evutil_make_socket_nonblocking(fd);bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);bufferevent_setcb(bev, readcb, NULL, errorcb, NULL);bufferevent_setwatermark(bev, EV_READ, 0, MAX_LINE);bufferevent_enable(bev, EV_READ|EV_WRITE);} }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; /*XXXerr*/sin.sin_family = AF_INET;sin.sin_addr.s_addr = 0;sin.sin_port = htons(40713);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));}#endifif (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; }

3.2這一切效率如何,當真?

在這里寫了一個高效的代碼.libevent頁面上的基準是過時了

  • 這些文件是版權(c)2009 - 2012年由尼克·馬修森和可用創意下議院Attribution-Noncommercial-Share都許可,3.0版.未來版本

  • 可能會用更少的限制性許可協議.

  • 此外,這些文檔的源代碼示例都是基于"3條款""修改的"BSD許可,請參考license_bsd文件全部條款.

  • 本文檔的最新版本,請參閱【http://libevent.org/】

  • 本文檔對應的最新版本代碼,請安裝git然后執行【git clone git://github.com/nmathewson/libevent-book.git】

4.正文前頁

4.11000英尺看LibEvent

LibEvent是用于編寫高速可移植的非阻塞IO,它的目標是:

  • 可移植性:使用LibEvent編寫的程序應該在LibEvent支持跨越的所有平臺上工作,即使沒有更好的方法來處理非阻塞式IO:LibEvent也應該支持一般的方法使程序可以運行在某些限制的環境中.

  • 速度:LibEvent試圖在每一個平臺實現最快的非阻塞式IO,而不會引入太多的額外開銷.

  • 可擴展性:LibEvent設計為即使在成千上萬的socket情況下也能良好工作.

  • 方便:無論在什么情況下,用LibEvent來編寫程序最自然的方式都應該是穩定可靠的.

    ?

    LibEvent由下列組件構成:

  • evutil:用于抽象出不同平臺網絡實現的通用功能.

  • event and event_base:libevent的核心,為各種平臺特定的、基于事件的非阻塞?IO后端提供抽象?API,讓程序可以知道套接字何時已經準備好,可以讀或者寫,并且處理基本的超時功能,檢測?OS信號.

  • eufferevent:?libevent基于事件的核心提供使用更方便的封裝.除了通知程序套接字已經準備好讀寫之外,還讓程序可以請求緩沖的讀寫操作,可以知道何時?IO已經真正發生.(bufferevent接口有多個后端,可以采用系統能夠提供的更快的非阻塞?IO方式?,?Windows中的?IOCP)

  • evbuffer:?bufferevent層之下實現了緩沖功能,并且提供了方便有效的訪問函數.

  • evhttp:一個簡單的?HTTP客戶端/服務器實現.

  • evdns:一個簡單的?DNS客戶端/服務器實現.

  • evrpc:一個簡單的?RPC實現.

    ?

4.2

創建?libevent,默認安裝下列庫:

  • libevent_core:所有核心的事件和緩沖功能,包含了所有的?event_baseevbufferbufferevent和工具函數.

  • libevent_extra:定義了程序可能需要,也可能不需要的協議特定功能,包括?HTTPDNS?RPC.

  • libevent:這個庫因為歷史原因而存在,它包含?libevent_core?libevent_extra的內容?.不應該使用這個庫未來版本的?libevent可能去掉這個庫.

    ?

    某些平臺上可能安裝下列庫:

  • libevent_pthreads:添加基于?pthread可移植線程庫的線程和鎖定實現.它獨立于

  • libevent_core,這樣程序使用?libevent時就不需要鏈接到?pthread,除非是以多線程方式使用?libevent.

  • libevent_openssl:這個庫為使用?bufferevent?OpenSSL進行加密的通信提供支持?.它獨立于?libevent_core,這樣程序使用?libevent時就不需要鏈接到?OpenSSL,除非是進行加密通信.

    ?

4.3頭文件

libevent公用頭文件都安裝在?event2目錄中,分為三類:

  • API頭文件:定義?libevent公用接口.這類頭文件沒有特定后綴.

  • 兼容頭文件:為已廢棄的函數提供兼容的頭部包含定義.不應該使用這類頭文件,除非是在移植使用較老版本?libevent的程序時.

  • 結構頭文件:這類頭文件以相對不穩定的布局定義各種結構體.這些結構體中的一些是為了提供快速訪問而暴露;一些是因為歷史原因而暴露.直接依賴這類頭文件中的任何結構體都會破壞程序對其他版本?libevent的二進制兼容性,有時候是以非常難以調試的方式出現.這類頭文件具有后綴"_struct.h".(還存在不在?event2目錄中的較老版本?libevent的頭文件,請參考下節:如果需要使用老版本?libevent)

    ?

4.4如果需要使用老版本libevent

libevent 2.0以更合理的、不易出錯的方式修正了?API.如果可能,編寫新程序時應該使用libevent 2.0.但是有時候可能需要使用較老的?API,例如在升級已存的應用時,或者支持因為某些原因不能安裝2.0或者更新版本?libevent的環境時.較老版本的?libevent頭文件較少,也不安裝在?event2目錄中.

老版本頭文件

當版本前頭文件

event.h

event2/event*.h,event2/buffer*.h,

event2/bufferevent*.h,event2/tag*.h

evdns.h

event2/dns*.h

evhttp.h

event2/http*/

evrpc.h

event2/rpc*.h

evutil.h

event2/util*.h

?

2.0以及以后版本的?libevent,老的頭文件仍然會作為新頭文件的封裝而存在.

?

其他關于使用較老版本的提示:

  • 1.4版之前只有一個庫?libevent,它包含現在分散到?libevent_core?libevent_extra中的所有功能.

  • 2.0版之前不支持鎖定:只有確定不同時在多個線程中使用同一個結構體時,libevent才是線程安全的.

    下面的節還將討論特定代碼區域可能遇到的已經廢棄的?API.

4.4.1版本狀態告知

之前的LibEvent版本1.4.7應該是已經完全放棄了.版本1.3e之前的也應該是滿是bug.(另外,請不要向LibEvent維護者發送任何在1.4x或更早版本的新的特色,這些版本都已經發行了realease版本.如果你在1.3x或更早的版本發現了一個bug,在你提交反饋報告之前請確認在最新版本也出現了這些bug.所以后續版本的發行都是有原因的.)

?

<<下一章>>

  

總結

以上是生活随笔為你收集整理的LibEvent中文帮助文档--第1、2、3、4章的全部內容,希望文章能夠幫你解決所遇到的問題。

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