关闭

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

标签: libevent
282人阅读 评论(0) 收藏 举报
分类:

七、应用实例

本文例子不加修改在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的内容)。
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:68775次
    • 积分:1135
    • 等级:
    • 排名:千里之外
    • 原创:3篇
    • 转载:264篇
    • 译文:0篇
    • 评论:2条