您的位置:首页 > 理论基础 > 计算机网络

基于事件的开源网络库—libevent:应用实例

2013-01-09 08:56 453 查看
(接上一篇,本文给出具体应用的实例)

七、应用实例

本文例子不加修改在windows下运行(须定义宏WIN32,链接ws2_32.lib,libevent_core.lib),稍加修改(例如去掉windows所特有的socket初始化)可运行于Linux。

1. 最简单的例子,定时器

该例子创建了一个event_base,在此base上,增加了两个event,也就是设置了两个定时器,主要用来测试事件处理是否是多线程(结论是单线程)。这个例子也同时试验了给事件函数传入自定义参数的情况(例子中是一个整数)。

在main()函数中调用testlibevent_timer()即可运行该例子。

#include <stdio.h>
#include <time.h>

#include "libevent\event.h"
#include "libevent_timer.h"

#include "libevent\event_struct.h"
#include "libevent\util.h"

struct timeval lasttime;
typedef struct
{
struct event *pevent;
void *otherargs;
} event_arg_t;

void timeout_cb1(evutil_socket_t fd, short event, void *arg)
{
struct timeval newtime, difference;
event_arg_t *parg = (event_arg_t*)arg;
struct event *timeout_ev = parg->pevent;
int handle = *(int *)parg->otherargs;
double elapsed;

evutil_gettimeofday(&newtime, NULL);
evutil_timersub(&newtime, &lasttime, &difference);
elapsed = difference.tv_sec + (difference.tv_usec / 1.0e6);

printf("timeout called at %d: %.3f s elapsed event=%d arg.handle=%d.\n",    (int)newtime.tv_sec, elapsed, event, handle);

//看看如果在一个事件处理中延时会出现什么情况
//Sleep(6000);
}
void timeout_cb2(evutil_socket_t fd, short event, void *arg)
{
struct timeval newtime, difference;
event_arg_t *parg = (event_arg_t*)arg;
struct event *timeout_ev = parg->pevent;
int handle = *(int *)parg->otherargs;
double elapsed;

evutil_gettimeofday(&newtime, NULL);
evutil_timersub(&newtime, &lasttime, &difference);
elapsed = difference.tv_sec + (difference.tv_usec / 1.0e6);

printf("timeout called at %d: %.3f s elapsed event=%d arg.handle=%d.\n",    (int)newtime.tv_sec, elapsed, event, handle);

}

int testlibevent_timer(void)
{
event_arg_t timeout_arg1,timeout_arg2;
struct event timeout_ev1,timeout_ev2 ;
struct timeval tv;
struct event_base *base;
int flags;
int handle1 = 2;
int handle2 = 3;
timeout_arg1.pevent = &timeout_ev1;
timeout_arg1.otherargs = (void *)&handle1;
timeout_arg2.pevent = &timeout_ev2;
timeout_arg2.otherargs = (void *)&handle2;

WORD wVersionRequested;
WSADATA wsaData;
int	err;

wVersionRequested = MAKEWORD(2, 2);

err = WSAStartup(wVersionRequested, &wsaData);

flags = EV_PERSIST;     //	EV_PERSIST or 0

/* Initalize the event library */
base = event_base_new();

/* Initalize one event */
event_assign(&timeout_ev1, base, -1, flags, timeout_cb1, (void*) &timeout_arg1);
event_assign(&timeout_ev2, base, -1, flags, timeout_cb2, (void*) &timeout_arg2);

evutil_timerclear(&tv);
tv.tv_sec = handle1;
event_add(&timeout_ev1, &tv);
tv.tv_sec = handle2;
event_add(&timeout_ev2, &tv);

evutil_gettimeofday(&lasttime, NULL);

event_base_dispatch(base);

return (0);
}

这个例子比较简单,监视的句柄为-1,实际上不算是事件发生了,而是每次发生超时,调用了事件处理函数。

2.监听服务器的例子

这个例子创建一个监听某端口的服务器,采用事件的方式来处理请求。

#include <stdio.h>
#include <time.h>

#include "libevent\event.h"

#include "libevent\event_struct.h"
#include "libevent\buffer.h"
#include "libevent\bufferevent.h"
#include "libevent\listener.h"
#include "libevent\util.h"

void client_process(evutil_socket_t fd)
{
//可以创建线程,自己编写read,recv,需要注意的是此时所有的socket操作都是非阻塞的。
//
evutil_closesocket(fd);
}

void echo_read_cb(struct bufferevent *bev, void *ctx)
{
/* This callback is invoked when there is data to read on bev. */
struct evbuffer *input = bufferevent_get_input(bev);
struct evbuffer *output = bufferevent_get_output(bev);

/* Copy all the data from the input buffer to the output buffer. */
evbuffer_add_buffer(output, input);
}

void echo_event_cb(struct bufferevent *bev, short events, void *ctx)
{
if (events & BEV_EVENT_ERROR)
perror("Error from bufferevent");
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
bufferevent_free(bev);
}
}

void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,  struct sockaddr *sa, int socklen, void *arg)
{

//方法一:采用libevent中的buffer系列方法
{
struct event_base *base = evconnlistener_get_base(listener);
struct bufferevent *bev = bufferevent_socket_new( base, fd, BEV_OPT_CLOSE_ON_FREE);

bufferevent_setcb(bev, echo_read_cb, NULL, echo_event_cb, NULL);

bufferevent_enable(bev, EV_READ|EV_WRITE);
}

//方法二:自己处理recv,read
//	{
//       client_process(fd);
//    }
}

void accept_interrupt(evutil_socket_t fd, short what, void *arg)
{
char timestr[60];
get_time_string(timestr);
printf("\n%s:accept_timeout.",timestr);

event_t *parg = (event_t*)arg;
struct event_base *base = event_get_base(&parg->eventbody);
int handle = *(int *)parg->parg;
if(handle == 3)
{
event_base_loopexit(base, NULL);
}
}

int socket_server_event_test(void)
{
WSADATA  Ws;
//Init Windows Socket
if ( WSAStartup(MAKEWORD(2,2), &Ws) != 0 )
{
return -1;
}

int port = 2200;
int max_conn = 10;       //backlog
struct event_base *base;
struct evconnlistener *listener;

struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);

// Initalize the event library
base = event_base_new();
if (!base) 	return -2;

listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, max_conn,
(struct sockaddr*)&sin,
sizeof(sin));

event_t interrupt_ev;
int handle = 3;
struct timeval tv;
interrupt_ev.parg = &handle;

event_assign(&interrupt_ev.eventbody, base, -1, EV_PERSIST, accept_interrupt, (void*) &interrupt_ev);
evutil_timerclear(&tv);
tv.tv_sec = 40;
event_add(&interrupt_ev.eventbody, &tv);

event_base_dispatch(base);

evconnlistener_free(listener);
event_base_free(base);

WSACleanup();
return 0;
}
上面的例子中,base需要监视两个事件,listening事件和定时器事件,设置定时器事件的目的是使该处理线程能有“机会”处理一下非listen事情,例如接到退出命令,停止listening等。接收到外部连接后,可以有两种办法:一是采用libevent的buffer系列方法读取端口,二是自己处理读写(可采用多线程)。

八、探究libevent的运行机制

研究libevent的运行机制,已超出本文范围。采用编译好的lib库的好处在于不必考虑其内部的运行,我们只要包含.h文件即可。libevent的源文件比较零散,作者在windows平台上,将它们集合在一起(一个目录下),修改了其引用路径,可以源代码级包含在项目中,这样可以设置断点来观察libevent的运行。有兴趣的同行可以从这里下载试试(其中只包含了libevent_core.lib的内容)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: