libevent的简单应用【转载】
本文转载自: 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 == && !strcmp(argv[], "-p")) {
event_is_persistent = ;
flags = EV_PERSIST; //使得事件具有持久性(否则事件只会调度一次)
} else {
event_is_persistent = ;
flags = ;
}
/* Initalize the event library */
base = event_base_new(); //创建事件"总管"
/* Initalize one event */
event_assign(&timeout, base, -, flags, timeout_cb, (void*) &timeout);
evutil_timerclear(&tv);
tv.tv_sec = ;
event_add(&timeout, &tv); //添加事件,同时设置超时时间
evutil_gettimeofday(&lasttime, NULL);
event_base_dispatch(base); //循环监视事件,事件标志的条件发生,就调用回调函数
return ();
}
/*******************************************************/
启动进程:
[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();
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 ();
}
启动进程:
[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, , 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, , sizeof(struct event));
memset(se->write_ev, , sizeof(struct event));
memset(se->buffer, , EV_BUFSIZE_T);
se->gapbeats = gapbeats;
se->maxbeats = maxbeats;
se->pastbeats = ; 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), ) < ) {
printf("server send msg error: errno %d--%s\n", errno, strerror(errno));
return;
}
memset(buffer, , 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, , EV_BUFSIZE_T);
size = recv(sock, sockev->buffer, EV_BUFSIZE_T, );
if (size <= ) { //接收数据失败
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 = ;
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(, )) == NULL)
return;
sin_size = sizeof(struct sockaddr_in);
connetfd = accept(sock, (struct sockaddr*)&cli_addr, &sin_size);
if (connetfd == -) {
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, );
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (int *), sizeof(int));
memset(&server_addr, , 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)) == -) {
printf("bind socket error: errno %d--%s\n", errno, strerror(errno));
exit();
}
if (listen(sock, BACKLOG) == -) {
printf("listen socket error: errno %d--%s\n", errno, strerror(errno));
exit();
} 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();
}
// 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) == ) { //从监视事件集中删除
event_free(listen_ev); //删除事件,释放空间
}
event_base_free(base); //删除base对象
exit();
}
客户端: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 = ;
pid_t *pidr = &testr;
struct sockaddr_in servaddr;
struct timeval tv;
struct event_base* base;
struct event *client_ev; if( argc != ){
printf("usage: ./client <ip address>\n");
exit();
} memset(sendline, , MAXLINE);
if( (sockfd = socket(AF_INET, SOCK_STREAM, )) < ){
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
exit();
}
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(); //把16位值从主机字节序转换成网络字节序
if( inet_pton(AF_INET, argv[], &servaddr.sin_addr) <= ){ // [将“点分十进制”ip-> 网络字节序“整数”ip]
printf("inet_pton error for %s\n",argv[]);
exit();
}
if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < ){
printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
exit();
}
printf("send msg to server: \n");
evutil_timerclear(&tv);
tv.tv_sec = ; base = event_base_new();
client_ev = event_new(base, sockfd, EV_PERSIST, heartbit_cb, pids);
if (!client_ev) {
printf("event_new() fail\n");
exit();
}
// 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) == ) {
event_free(client_ev);
}
event_base_free(base);
close(sockfd);
exit();
} /* 功能: 向服务端发送心跳包
* 参数:
*
*/
static void heartbit_cb(evutil_socket_t fd, short event, void *arg)
{
pid_t *pid = (pid_t *)arg;
pid_t testr = ;
pid_t *pid_recv = &testr;
int len; len = sizeof(pid_t);
if( send(fd, pid, len, ) != len) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit();
}
//接收从服务端的返回数据
fputs("echo from server:\n", stdout);
if(recv(fd, pid_recv, len, ) < ) {
printf("Failed to receive bytes from client\n");
exit(-);
}
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,所以这样是不行的,所以客户端的定时发送心跳包应该改用信号做成一个小模块加入到客户端进程中。
libevent的简单应用【转载】的更多相关文章
- VPN理论简单介绍(转载)
		标签:VPN理论简单介绍 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://lvnian.blog.51cto.com/715528 ... 
- redis在Java web项目的简单应用(转载)
		看到一篇关于redis和spring集成的文章,实际测试后,可以.转载以备用.谢谢 亲昵YY! html,body { font-size: 15px } body { font-family: He ... 
- libevent库简单使用
		一.libevent库简介 Libevent是一个用于开发可扩展性网络服务器的基于事件驱动(event-driven)模型的网络库.Libevent有几个显著的亮点: (1)事件驱动(event-dr ... 
- Kubernetes1-K8s的简单介绍(转载)
		一.简介 1.什么是Kubernetes 简称K8s,用8代替8个字符"ubernerte"而成的速写,K8s是一个开源的容器编排平台,它是一个跨主机集群的开源容器调度平台,用于管 ... 
- 用VC调用EXCEL简单代码(转载自越长大越孤单,觉得很好)
		首先在stdafx.h里加入对IDispatch接口提供支持的头文件: #include <afxDisp.h> 再在应用程序类的InitInstance()函数里加入: AfxOleIn ... 
- ubuntu16.04中supervisor安装与简单使用(转载)
		ubuntu16.04中supervisor安装与简单使用 supervisor 进程管理是可以让进程在后台运行,而不占用控制台影响使用 1. 安装 supervisor sudo apt insta ... 
- Java WebService 简单实例[转载]
		[注意,本文转载自 http://hyan.iteye.com/ ] 一.准备工作(以下为本实例使用工具) 1.MyEclipse10.7.1 2.JDK 1.6.0_22 二.创建服务端 1 ... 
- 使用 CXF 做 webservice 简单例子(转载)
		使用 CXF 做 webservice 简单例子 Apache CXF 是一个开放源代码框架,提供了用于方便地构建和开发 Web 服务的可靠基础架构.它允许创建高性能和可扩展的服务,您可以将这 ... 
- MySQL 触发器简单实例 - 转载
		MySQL 触发器简单实例 触发器:可以更新,删除,插入触发器,不同种类的触发器可以存在于同一个表,但同种类的不能有多个.一个更新.一个删除是可以共存的. ~~语法~~ CREATE TRIGGER ... 
随机推荐
- [转] boost.circular_buffer简介
			http://www.cnblogs.com/TianFang/archive/2013/02/05/2892503.html 很多时候,我们需要在内存中记录最近一段时间的数据,如操作记录等.由于这部 ... 
- NSIndexPath初始化
			在UITableView中经常用到这个类,但一直不知道怎么初始化,网上抄录的代码如下,果然好用 NSIndexPath *index = [NSIndexPath indexPathForRow:0 ... 
- yii自动登陆的验证机制浅析
			一直在使用yii进行开发, 也知道如何去使用, 也仅仅是知道怎么去用罢了, 终归是没研究过源码, 心里发虚, 今天遇到一个问题, 关于自动登陆的问题. 要求就是, 修改登陆保存session天数为自定 ... 
- Handler导致内存泄露分析
			(非静态)内部类引起内存泄漏的原因 内部类的实现其实是通过编译器的语法糖(Syntactic sugar)实现的,通过生成相应的子类即以OutClassName$InteriorCla ... 
- Android手机适配——UI图片适配
			转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/50727753 在Android项目当中,drawable文件夹都是用来放置图片资源 ... 
- JavaScript细节整理
			JavaScript是一个绝冠全球的编程语言,可用于Web开发.移动应用开发(PhoneGap.Appcelerator).服务器端开发(Node.js和Wakanda)等等.JavaScript还是 ... 
- 在CSS文件中引入其他CSS文件
			引入CSS的方法有两种,一种是@import,一种是link 一.@import url('地址');二.<link href="地址" rel="styleshe ... 
- (转)dedecms代码详解 很全面
			dedecms代码研究(1)开篇dedecms 相信大家一定都知道这个cms 系统,功能比较强大,有比较完善的内容发布,还有内容静态化系统,还有就是它有自己独特的标签系统和模板系统.而模板系统也是其他 ... 
- (转帖) 如何將值delay n個clock? (SOC) (Verilog)
			来源:http://www.cnblogs.com/oomusou/archive/2009/06/15/verilog_dly_n_clk.html /* (C) OOMusou 2009 http ... 
- MySQL 序列使用
			MySQL 序列使用 MySQL序列是一组整数:1, 2, 3, ...,由于一张数据表只能有一个字段自增主键, 如果你想实现其他字段也实现自动增加,就可以使用MySQL序列来实现. 本章我们将介绍如 ... 
