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

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

2014-08-07 01:23 459 查看


七、应用实例

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


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

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

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

[cpp] view
plaincopy

#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.监听服务器的例子

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

[cpp] view
plaincopy

#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的内容)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: