(接上一篇,本文给出具体应用的实例)
本文例子不加修改在windows下运行(须定义宏WIN32,链接ws2_32.lib,libevent_core.lib),稍加修改(例如去掉windows所特有的socket初始化)可运行于Linux。
该例子创建了一个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,实际上不算是事件发生了,而是每次发生超时,调用了事件处理函数。
这个例子创建一个监听某端口的服务器,采用事件的方式来处理请求。
#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系列方法读取端口,二是自己处理读写(可采用多线程)。