本文轉載自:?http://blog.csdn.net/liuguanghui1988/article/details/7090531
Libevent的應用主要圍繞幾大事件:超時事件、信號事件、讀/寫事件。
下面就一一簡單介紹一下它們的使用。
超時事件
示例:
/*
* 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;
//
/* 啟動此測試程序時,不加-p參數,使用以下代碼也可實現相同功能 */
//
struct timeval tv;
//
evutil_timerclear(&tv);
//
tv.tv_sec = 1;
//
event_add(timeout, &tv);
//
再次添加定時事件
}
int
main(
int
argc,
char
**
argv)
{
struct
event
timeout;
//
創建事件
struct
timeval tv;
struct
event_base *
base
;
//
創建事件"總管"的指針
int
flags;
//
事件標志,超時事件可不設EV_TIMEOUT,因為在添加事件時可設置
if
(argc ==
2
&& !strcmp(argv[
1
],
"
-p
"
)) {
event_is_persistent
=
1
;
flags
= EV_PERSIST;
//
使得事件具有持久性(否則事件只會調度一次)
}
else
{
event_is_persistent
=
0
;
flags
=
0
;
}
/*
Initalize the event library
*/
base
= event_base_new();
//
創建事件"總管"
/*
Initalize one event
*/
event_assign(
&timeout,
base
, -
1
, flags, timeout_cb, (
void
*) &
timeout);
evutil_timerclear(
&
tv);
tv.tv_sec
=
1
;
event_add(
&timeout, &tv);
//
添加事件,同時設置超時時間
evutil_gettimeofday(&
lasttime, NULL);
event_base_dispatch(
base
);
//
循環監視事件,事件標志的條件發生,就調用回調函數
return
(
0
);
}
?/*******************************************************/
啟動進程:
[lgh@localhost test]$ ./libe_timer_test -p
結果:
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.
以一行/秒的速率打印以上信息,也就是每秒打印一行。因為超時為一秒。
==============================
啟動進程:
[lgh@localhost test]$ ./libe_timer_test
結果:
timeout_cb called at 1325693516: 1.000 seconds elapsed.
沒有-p參數,事件只調度一次。如果不加EV_PERSIST標志也想實現事件的持續性,還有一種辦法,就是在回調函數的后面再添加該事件,即上面回調函數的批量注釋代碼。
?
信號事件
示例:
/*
* 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
);
}
啟動進程:
[lgh@localhost test]$ ./libe_signal_test &
[1] 2998
用kill -10 2998命令給進程發送信號SIGUSR1,進程的的執行結果如下:
[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
給進程發送了3次SIGUSR1信號,信號回調函數執行了三次(其中最后一行隔了幾秒才打印出來)。這說明libevent對linux中的不可靠信號也是支持排隊的。
?
讀/寫事件
文件描述符是否可讀/寫,這個不太好模擬(可能用文件的讀/寫鎖可以實現模擬,鄙人目前還沒有嘗試過,有試過的朋友可以指點一下),有一種方法就是用socket連接來模擬,先建立一個服務端和客戶端,當服務端的監聽端口可讀時說明有一個新的連接請求。
示例:
?服務端——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
//
----------需改靈活點
#define
BACKLOG 10
//
好像沒起到作用,我設置為1,在同一機子下開兩個連接,沒彈出警告信息
#define
EV_BUFSIZE_T sizeof(pid_t)
//
服務端與客戶端傳遞數據的buffer大小
/*
管理每一個連接的讀寫事件和數據
*/
typedef
struct
sock_event {
struct
event
*read_ev;
//
讀事件
struct
event
*write_ev;
//
寫事件
pid_t *buffer;
//
buffer僅存進程pid
int
gapbeats;
//
定時間隔
int
maxbeats;
//
在客戶端不工作或退出的情況下,服務端的最大檢測次數
int
pastbeats;
//
沒有收到數據的情況下,當前的心跳檢測數
}sock_ev;
struct
event_base*
base
;
//
管理所有連接事件
/*
釋放堆分配的sock_ev結構體
*/
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);
}
/*
功能:創建一個sock_ev結構體,并且將它初始化.
* 參數:gapbeats,服務端兩個檢測心跳的間隔時間(單位:秒);
* maxbeats,沒有收到客戶端數據的最大檢測次數.
* 返回:sock_ev結構體指針.
*/
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;
}
/*
功能:寫事件回調函數
* 參數:libevent回調函數的三個典型參數
* sock,文件描述符;event,事件類型(EV_WRITE);arg,傳給函數的數據指針(buffer)
* 返回: void (libevent回調函數的返回為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));
}
/*
功能:讀事件回調函數
* 參數:libevent回調函數的三個典型參數
* sock,文件描述符;event,事件類型(EV_READ);arg,傳給函數的數據指針(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
) {
//
接收數據失敗
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);
//
添加端口寫事件,將數據返回給客戶端
}
/*
功能:接受新連接請求
* 參數:libevent回調函數的三個典型參數
* sock,文件描述符;event,事件類型(EV_READ,監聽端口可讀,表示有新連接請求);
arg,目前為空指針.
* 返回: void.
*/
void
connect_accept(
int
sock,
short
event
,
void
*
arg)
{
struct
sockaddr_in cli_addr;
int
connetfd, sin_size;
struct
timeval beat;
//
定時讀事件,來檢測客戶端發送了數據
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|,則定時讀會失效)
//
event_base_set(base, sockev->read_ev);
evutil_timerclear(&
beat);
beat.tv_sec
= sockev->gapbeats;
//
定期檢查端口是否可讀,來判斷客戶端是否存在
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;
//
創建連接請求監聽事件
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);
//
創建evnet對象并初始化
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);
//
添加到監視事件集中,event就變成的未決狀態
event_base_dispatch(
base
);
//
輪詢監視所有事件
if
(event_del(listen_ev) ==
0
) {
//
從監視事件集中刪除
event_free(listen_ev);
//
刪除事件,釋放空間
}
event_base_free(
base
);
//
刪除base對象
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位值從主機字節序轉換成網絡字節序
if
( inet_pton(AF_INET, argv[
1
], &servaddr.sin_addr) <=
0
){
//
[將“點分十進制”ip-> 網絡字節序“整數”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可寫,則會一直寫(與定時事件是關系或) 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
);
}
/*
功能: 向服務端發送心跳包
* 參數:
*
*/
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
);
}
//
接收從服務端的返回數據
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);
}
結果:
[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
=============================================================
測試設置:客戶端每2秒發一次心跳,服務端每1秒去查看端口是否可讀.?
??????????????????? 服務端若經過10次還沒收到心跳,則認為客戶端已退出.
?測試過程:先讓客戶端發3次心跳,再終止掉客戶端.---用時6秒。
? ??????????????? ??此時,服務端已經對端口發起了6次檢測,有3次接收到了數據,有3次沒有收到數據。
? ?????????????????? 當終止掉客戶端后,服務端每次檢測都會收不到數據,現象是:前3次是連續執行了超時回調函數socket_read,這3次沒有經過每隔1秒執行。
? ?????????????????? 后面再每秒檢測7次,即每秒執行一次回調函數。總計10次沒有收到來自客戶端的數據,判斷客戶端已退出。
?
? 測試結果判斷: libevent對定時事件支持排隊,即有多少次定時,它就執行回調函數多少次.
? 編程建議:服務端的心跳頻率要小于等于客戶端的心跳頻率.(小于,會有丟包現象,但我們的需求只是檢測客戶端是否存在)
?
小結:這個小例子用來做進程管理,客戶端是不行的,因為這里的客戶端也是libevent的超時事件,它在輪詢超時事件的時候會一直占用進程的cpu,所以這樣是不行的,所以客戶端的定時發送心跳包應該改用信號做成一個小模塊加入到客戶端進程中。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

