本文轉(zhuǎn)載自:?http://blog.csdn.net/liuguanghui1988/article/details/7090531
Libevent的應(yīng)用主要圍繞幾大事件:超時(shí)事件、信號(hào)事件、讀/寫事件。
下面就一一簡(jiǎn)單介紹一下它們的使用。
超時(shí)事件
示例:
/* * Compile with: * gcc time-test time-test.c -o time-test time-test -I/usr/local/include -L/usr/local/lib -levent */ /* * XXX This sample code was once meant to show how to use the basic Libevent * interfaces, but it never worked on non-Unix platforms, and some of the * interfaces have changed since it was first written. It should probably * be removed or replaced with something better. * * Compile with: * gcc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent */ #include <sys/types.h> #include <sys/stat.h> #include <time.h> #ifdef _EVENT_HAVE_SYS_TIME_H #include <sys/time.h> #endif #include <stdio.h> #include <event2/ event .h> #include <event2/event_struct.h> #include <event2/util.h> struct timeval lasttime; int event_is_persistent; static void timeout_cb(evutil_socket_t fd, short event , void * arg) { struct timeval newtime, difference; struct event *timeout = arg; double elapsed; evutil_gettimeofday( & newtime, NULL); evutil_timersub( &newtime, &lasttime, & difference); elapsed = difference.tv_sec + (difference.tv_usec / 1.0e6 ); printf( " timeout_cb called at %d': %.3f seconds elapsed.\n " , ( int )newtime.tv_sec, elapsed); lasttime = newtime; // /* 啟動(dòng)此測(cè)試程序時(shí),不加-p參數(shù),使用以下代碼也可實(shí)現(xiàn)相同功能 */ // struct timeval tv; // evutil_timerclear(&tv); // tv.tv_sec = 1; // event_add(timeout, &tv); // 再次添加定時(shí)事件 } int main( int argc, char ** argv) { struct event timeout; // 創(chuàng)建事件 struct timeval tv; struct event_base * base ; // 創(chuàng)建事件"總管"的指針 int flags; // 事件標(biāo)志,超時(shí)事件可不設(shè)EV_TIMEOUT,因?yàn)樵谔砑邮录r(shí)可設(shè)置 if (argc == 2 && !strcmp(argv[ 1 ], " -p " )) { event_is_persistent = 1 ; flags = EV_PERSIST; // 使得事件具有持久性(否則事件只會(huì)調(diào)度一次) } else { event_is_persistent = 0 ; flags = 0 ; } /* Initalize the event library */ base = event_base_new(); // 創(chuàng)建事件"總管" /* Initalize one event */ event_assign( &timeout, base , - 1 , flags, timeout_cb, ( void *) & timeout); evutil_timerclear( & tv); tv.tv_sec = 1 ; event_add( &timeout, &tv); // 添加事件,同時(shí)設(shè)置超時(shí)時(shí)間 evutil_gettimeofday(& lasttime, NULL); event_base_dispatch( base ); // 循環(huán)監(jiān)視事件,事件標(biāo)志的條件發(fā)生,就調(diào)用回調(diào)函數(shù) return ( 0 ); }
?/*******************************************************/
啟動(dòng)進(jìn)程:
[lgh@localhost test]$ ./libe_timer_test -p
結(jié)果:
timeout_cb called at 1325693811': 1.000 seconds elapsed.
timeout_cb called at 1325693812': 1.000 seconds elapsed.
timeout_cb called at 1325693813': 1.001 seconds elapsed.
timeout_cb called at 1325693814': 1.000 seconds elapsed.
timeout_cb called at 1325693815': 1.000 seconds elapsed.
以一行/秒的速率打印以上信息,也就是每秒打印一行。因?yàn)槌瑫r(shí)為一秒。
==============================
啟動(dòng)進(jìn)程:
[lgh@localhost test]$ ./libe_timer_test
結(jié)果:
timeout_cb called at 1325693516: 1.000 seconds elapsed.
沒(méi)有-p參數(shù),事件只調(diào)度一次。如果不加EV_PERSIST標(biāo)志也想實(shí)現(xiàn)事件的持續(xù)性,還有一種辦法,就是在回調(diào)函數(shù)的后面再添加該事件,即上面回調(diào)函數(shù)的批量注釋代碼。
?
信號(hào)事件
示例:
/* * Compile with: * gcc libe_signal_test.c -o libe_signal_test -I/usr/local/include -L/usr/local/lib -levent */ #include <signal.h> #include <stdio.h> #include < event .h> #include <event2/ event .h> #ifdef _EVENT___func__ #define __func__ _EVENT___func__ #endif // int called = 0; static void signal_cb(evutil_socket_t fd, short event , void * arg) { struct event *signal = arg; sleep( 10 ); printf( " %s: got signal %d\n " , __func__, EVENT_SIGNAL(signal)); // if (called >= 2) // event_del(signal); // called++; } int main( int argc, char ** argv) { struct event signal_usr; struct event_base* base ; /* Initalize the event library */ base = event_base_new(); /* Initalize one event */ event_assign( &signal_usr, base , SIGUSR1, EV_SIGNAL| EV_PERSIST, signal_cb, & signal_usr); event_add( & signal_usr, NULL); event_base_dispatch( base ); event_base_free( base ); printf( " end of main!\n " ); return ( 0 ); }
啟動(dòng)進(jìn)程:
[lgh@localhost test]$ ./libe_signal_test &
[1] 2998
用kill -10 2998命令給進(jìn)程發(fā)送信號(hào)SIGUSR1,進(jìn)程的的執(zhí)行結(jié)果如下:
[lgh@localhost test]$ kill -10 2998
[lgh@localhost test]$ kill -10 2998
signal_cb: got signal 10
?[lgh@localhost test]$ kill -10 2998
signal_cb: got signal 10
[lgh@localhost test]$ signal_cb: got signal 10
給進(jìn)程發(fā)送了3次SIGUSR1信號(hào),信號(hào)回調(diào)函數(shù)執(zhí)行了三次(其中最后一行隔了幾秒才打印出來(lái))。這說(shuō)明libevent對(duì)linux中的不可靠信號(hào)也是支持排隊(duì)的。
?
讀/寫事件
文件描述符是否可讀/寫,這個(gè)不太好模擬(可能用文件的讀/寫鎖可以實(shí)現(xiàn)模擬,鄙人目前還沒(méi)有嘗試過(guò),有試過(guò)的朋友可以指點(diǎn)一下),有一種方法就是用socket連接來(lái)模擬,先建立一個(gè)服務(wù)端和客戶端,當(dāng)服務(wù)端的監(jiān)聽端口可讀時(shí)說(shuō)明有一個(gè)新的連接請(qǐng)求。
示例:
?服務(wù)端——proc_server.c
#include <sys/socket.h> #include <sys/types.h> #include <netinet/ in .h> #include <stdio.h> #include < event .h> #include <event2/ event .h> #include <errno.h> #define PORT 6666 // ----------需改靈活點(diǎn) #define BACKLOG 10 // 好像沒(méi)起到作用,我設(shè)置為1,在同一機(jī)子下開兩個(gè)連接,沒(méi)彈出警告信息 #define EV_BUFSIZE_T sizeof(pid_t) // 服務(wù)端與客戶端傳遞數(shù)據(jù)的buffer大小 /* 管理每一個(gè)連接的讀寫事件和數(shù)據(jù) */ typedef struct sock_event { struct event *read_ev; // 讀事件 struct event *write_ev; // 寫事件 pid_t *buffer; // buffer僅存進(jìn)程pid int gapbeats; // 定時(shí)間隔 int maxbeats; // 在客戶端不工作或退出的情況下,服務(wù)端的最大檢測(cè)次數(shù) int pastbeats; // 沒(méi)有收到數(shù)據(jù)的情況下,當(dāng)前的心跳檢測(cè)數(shù) }sock_ev; struct event_base* base ; // 管理所有連接事件 /* 釋放堆分配的sock_ev結(jié)構(gòu)體 */ void release_sock_ev(sock_ev * ev) { event_del(ev -> read_ev); free(ev -> read_ev); event_del(ev -> write_ev); free(ev -> write_ev); free(ev -> buffer); free(ev); } /* 功能:創(chuàng)建一個(gè)sock_ev結(jié)構(gòu)體,并且將它初始化. * 參數(shù):gapbeats,服務(wù)端兩個(gè)檢測(cè)心跳的間隔時(shí)間(單位:秒); * maxbeats,沒(méi)有收到客戶端數(shù)據(jù)的最大檢測(cè)次數(shù). * 返回:sock_ev結(jié)構(gòu)體指針. */ sock_ev * create_sock_ev( int gapbeats, int maxbeats) { sock_ev * se = (sock_ev *)malloc( sizeof (sock_ev)); if (! se) return NULL; memset(se, 0 , sizeof (sock_ev)); se ->read_ev = ( struct event *)malloc( sizeof ( struct event )); se ->write_ev = ( struct event *)malloc( sizeof ( struct event )); se ->buffer = (pid_t * )malloc(EV_BUFSIZE_T); if (!se->read_ev || !se->write_ev || !se-> buffer) return NULL; memset(se ->read_ev, 0 , sizeof ( struct event )); memset(se ->write_ev, 0 , sizeof ( struct event )); memset(se ->buffer, 0 , EV_BUFSIZE_T); se ->gapbeats = gapbeats; se ->maxbeats = maxbeats; se ->pastbeats = 0 ; return se; } /* 功能:寫事件回調(diào)函數(shù) * 參數(shù):libevent回調(diào)函數(shù)的三個(gè)典型參數(shù) * sock,文件描述符;event,事件類型(EV_WRITE);arg,傳給函數(shù)的數(shù)據(jù)指針(buffer) * 返回: void (libevent回調(diào)函數(shù)的返回為void) */ void socket_write( int sock, short event , void * arg) { pid_t * buffer; if (! arg) return ; buffer = (pid_t* )arg; if (send(sock, buffer, sizeof (*buffer), 0 ) < 0 ) { printf( " server send msg error: errno %d--%s\n " , errno, strerror(errno)); return ; } memset(buffer, 0 , sizeof (* buffer)); } /* 功能:讀事件回調(diào)函數(shù) * 參數(shù):libevent回調(diào)函數(shù)的三個(gè)典型參數(shù) * sock,文件描述符;event,事件類型(EV_READ);arg,傳給函數(shù)的數(shù)據(jù)指針(sock_ev) * 返回: void. */ void socket_read( int sock, short event , void * arg) { int size; sock_ev * sockev = (sock_ev* )arg; if (! sockev) return ; memset(sockev ->buffer, 0 , EV_BUFSIZE_T); size = recv(sock, sockev->buffer, EV_BUFSIZE_T, 0 ); if (size <= 0 ) { // 接收數(shù)據(jù)失敗 sockev->pastbeats++; // 全局變量 printf( " pastbeats:\t%d\n " , sockev->pastbeats); // --debug if (sockev->pastbeats >= sockev-> maxbeats) { printf( " ---client error or exit:please restart\n " ); // --debug release_sock_ev(sockev); close(sock); } return ; } sockev ->pastbeats = 0 ; printf( " pastbeats:\t%d\n " , sockev->pastbeats); // --debug printf( " receive data:\t%d size:\t%d\n " , *sockev-> buffer, size); event_add(sockev ->write_ev, NULL); // 添加端口寫事件,將數(shù)據(jù)返回給客戶端 } /* 功能:接受新連接請(qǐng)求 * 參數(shù):libevent回調(diào)函數(shù)的三個(gè)典型參數(shù) * sock,文件描述符;event,事件類型(EV_READ,監(jiān)聽端口可讀,表示有新連接請(qǐng)求); arg,目前為空指針. * 返回: void. */ void connect_accept( int sock, short event , void * arg) { struct sockaddr_in cli_addr; int connetfd, sin_size; struct timeval beat; // 定時(shí)讀事件,來(lái)檢測(cè)客戶端發(fā)送了數(shù)據(jù) sock_ev* sockev; // 為連接建立端口事件 if ((sockev = create_sock_ev( 1 , 10 )) == NULL) return ; sin_size = sizeof ( struct sockaddr_in); connetfd = accept(sock, ( struct sockaddr*)&cli_addr, & sin_size); if (connetfd == - 1 ) { printf( " server accept() error: errno %d--%s\n " , errno, strerror(errno)); return ; } event_assign(sockev ->read_ev, base , connetfd, EV_PERSIST, socket_read, sockev); // 下面是老版接口 // event_set(sockev->read_ev, connetfd, EV_PERSIST, socket_read, sockev); // 讀事件 (若加上EV_READ|,則定時(shí)讀會(huì)失效) // event_base_set(base, sockev->read_ev); evutil_timerclear(& beat); beat.tv_sec = sockev->gapbeats; // 定期檢查端口是否可讀,來(lái)判斷客戶端是否存在 event_add(sockev->read_ev, & beat); event_assign(sockev ->write_ev, base , connetfd, EV_WRITE, socket_write, sockev->buffer); // 寫事件 // event_set(sockev->write_ev, connetfd, EV_WRITE, socket_write, sockev->buffer); // event_base_set(base, sockev->write_ev); } int main( int argc, char * argv[]) { struct sockaddr_in server_addr; int sock; // struct event listen_ev; // 創(chuàng)建連接請(qǐng)求監(jiān)聽事件 struct event * listen_ev; sock = socket(AF_INET, SOCK_STREAM, 0 ); setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, ( int *) 1 , sizeof ( int )); memset( &server_addr, 0 , sizeof (server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); server_addr.sin_addr.s_addr = INADDR_ANY; if (bind(sock, ( struct sockaddr*)&server_addr, sizeof ( struct sockaddr)) == - 1 ) { printf( " bind socket error: errno %d--%s\n " , errno, strerror(errno)); exit( 0 ); } if (listen(sock, BACKLOG) == - 1 ) { printf( " listen socket error: errno %d--%s\n " , errno, strerror(errno)); exit( 0 ); } base = event_base_new(); // base,全局變量. listen_ev = event_new( base , sock, EV_READ|EV_PERSIST, connect_accept, NULL); // 創(chuàng)建evnet對(duì)象并初始化 if (! listen_ev) { printf( " event_new() fail\n " ); exit( 0 ); } // event_set(&listen_ev, sock, EV_READ|EV_PERSIST, connect_accept, NULL); 這是老接口 // event_base_set(base, &listen_ev); event_add(listen_ev, NULL); // 添加到監(jiān)視事件集中,event就變成的未決狀態(tài) event_base_dispatch( base ); // 輪詢監(jiān)視所有事件 if (event_del(listen_ev) == 0 ) { // 從監(jiān)視事件集中刪除 event_free(listen_ev); // 刪除事件,釋放空間 } event_base_free( base ); // 刪除base對(duì)象 exit( 0 ); }
客戶端:proc_client.c
#include<stdio.h> #include <stdlib.h> #include < string .h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/ in .h> #include < event .h> #include <event2/ event .h> #include <event2/util.h> #define MAXLINE 1024 static void heartbit_cb(evutil_socket_t fd, short event , void * arg); int main( int argc, char ** argv) { int sockfd, n, received; int len, bytes; char recvline[MAXLINE], sendline[MAXLINE]; pid_t tests = getpid(); pid_t *pids = & tests; pid_t testr = 0 ; pid_t *pidr = & testr; struct sockaddr_in servaddr; struct timeval tv; struct event_base* base ; struct event * client_ev; if ( argc != 2 ){ printf( " usage: ./client <ip address>\n " ); exit( 0 ); } memset(sendline, 0 , MAXLINE); if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0 )) < 0 ){ printf( " create socket error: %s(errno: %d)\n " , strerror(errno),errno); exit( 0 ); } memset( &servaddr, 0 , sizeof (servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons( 6666 ); // 把16位值從主機(jī)字節(jié)序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序 if ( inet_pton(AF_INET, argv[ 1 ], &servaddr.sin_addr) <= 0 ){ // [將“點(diǎn)分十進(jìn)制”ip-> 網(wǎng)絡(luò)字節(jié)序“整數(shù)”ip] printf( " inet_pton error for %s\n " ,argv[ 1 ]); exit( 0 ); } if ( connect(sockfd, ( struct sockaddr*)&servaddr, sizeof (servaddr)) < 0 ){ printf( " connect error: %s(errno: %d)\n " ,strerror(errno),errno); exit( 0 ); } printf( " send msg to server: \n " ); evutil_timerclear( & tv); tv.tv_sec = 2 ; base = event_base_new(); client_ev = event_new( base , sockfd, EV_PERSIST, heartbit_cb, pids); if (! client_ev) { printf( " event_new() fail\n " ); exit( 0 ); } // event_set(&client_ev, sockfd, EV_PERSIST, heartbit_cb, pids); // 若加上EV_WRITE|,sockfd可寫,則會(huì)一直寫(與定時(shí)事件是關(guān)系或) EV_PERSIST // event_base_set(base, &client_ev); event_add(client_ev, & tv); event_base_dispatch( base ); if (event_del(client_ev) == 0 ) { event_free(client_ev); } event_base_free( base ); close(sockfd); exit( 0 ); } /* 功能: 向服務(wù)端發(fā)送心跳包 * 參數(shù): * */ static void heartbit_cb(evutil_socket_t fd, short event , void * arg) { pid_t *pid = (pid_t * )arg; pid_t testr = 0 ; pid_t *pid_recv = & testr; int len; len = sizeof (pid_t); if ( send(fd, pid, len, 0 ) != len) { printf( " send msg error: %s(errno: %d)\n " , strerror(errno), errno); exit( 0 ); } // 接收從服務(wù)端的返回?cái)?shù)據(jù) fputs( " echo from server:\n " , stdout); if (recv(fd, pid_recv, len, 0 ) < 0 ) { printf( " Failed to receive bytes from client\n " ); exit( - 1 ); } printf( " %d\n " , * pid_recv); fputs( " \n " , stdout); }
結(jié)果:
[lgh@localhost proc]$ ./proc_client 192.168.1.107
send msg to server:
echo from server:
7482
echo from server:
7482
echo from server:
7482
--------------------------------------------------------------------------------------------
[lgh@localhost proc]$ ./proc_server
pastbeats: 0
receive data:7482?????? size:4
pastbeats: 0
receive data:7482?????? size:4
pastbeats: 0
receive data:7482?????? size:4
pastbeats: 1
pastbeats: 2
pastbeats: 3
pastbeats: 4
pastbeats: 5
pastbeats: 6
pastbeats: 7
pastbeats: 8
pastbeats: 9
pastbeats: 10
---pastbeats > maxbeats
=============================================================
測(cè)試設(shè)置:客戶端每2秒發(fā)一次心跳,服務(wù)端每1秒去查看端口是否可讀.?
??????????????????? 服務(wù)端若經(jīng)過(guò)10次還沒(méi)收到心跳,則認(rèn)為客戶端已退出.
?測(cè)試過(guò)程:先讓客戶端發(fā)3次心跳,再終止掉客戶端.---用時(shí)6秒。
? ??????????????? ??此時(shí),服務(wù)端已經(jīng)對(duì)端口發(fā)起了6次檢測(cè),有3次接收到了數(shù)據(jù),有3次沒(méi)有收到數(shù)據(jù)。
? ?????????????????? 當(dāng)終止掉客戶端后,服務(wù)端每次檢測(cè)都會(huì)收不到數(shù)據(jù),現(xiàn)象是:前3次是連續(xù)執(zhí)行了超時(shí)回調(diào)函數(shù)socket_read,這3次沒(méi)有經(jīng)過(guò)每隔1秒執(zhí)行。
? ?????????????????? 后面再每秒檢測(cè)7次,即每秒執(zhí)行一次回調(diào)函數(shù)。總計(jì)10次沒(méi)有收到來(lái)自客戶端的數(shù)據(jù),判斷客戶端已退出。
?
? 測(cè)試結(jié)果判斷: libevent對(duì)定時(shí)事件支持排隊(duì),即有多少次定時(shí),它就執(zhí)行回調(diào)函數(shù)多少次.
? 編程建議:服務(wù)端的心跳頻率要小于等于客戶端的心跳頻率.(小于,會(huì)有丟包現(xiàn)象,但我們的需求只是檢測(cè)客戶端是否存在)
?
小結(jié):這個(gè)小例子用來(lái)做進(jìn)程管理,客戶端是不行的,因?yàn)檫@里的客戶端也是libevent的超時(shí)事件,它在輪詢超時(shí)事件的時(shí)候會(huì)一直占用進(jìn)程的cpu,所以這樣是不行的,所以客戶端的定時(shí)發(fā)送心跳包應(yīng)該改用信號(hào)做成一個(gè)小模塊加入到客戶端進(jìn)程中。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
