Libevent初探
检查Libevent支持的IO复用方法
int main(int argc, char **argv)
{
// 版本信息
cout << event_get_version() << endl; // 所支持的IO复用方法
const char **methods = event_get_supported_methods();
for (int i = ; methods[i] != NULL; i++) {
cout << methods[i] << endl;
} return ;
}
输出结果为:(Centos7 Clion 2016.1.3环境)
event_get_supported_methods()函数返回Libevent支持的IO复用方法名称数组,以NULL结尾。该函数实际返回的是全局变量eventops数组,eventops数组存放的是所有支持的IO复用函数,eventops声明部分的代码如下:
/* Array of backends in order of preference. */
/* Libevent通过遍历eventops数组来选择其后端IO复用技术,遍历的顺序是从数组的第一个元素开始,
* 到最后一个元素结束。Linux系统下,默认选择的后端IO复用技术是epoll。*/
static const struct eventop *eventops[] = {
#ifdef _EVENT_HAVE_EVENT_PORTS
&evportops,
#endif
#ifdef _EVENT_HAVE_WORKING_KQUEUE
&kqops,
#endif
#ifdef _EVENT_HAVE_EPOLL
&epollops,
#endif
#ifdef _EVENT_HAVE_DEVPOLL
&devpollops,
#endif
#ifdef _EVENT_HAVE_POLL
&pollops,
#endif
#ifdef _EVENT_HAVE_SELECT
&selectops,
#endif
#ifdef WIN32
&win32ops,
#endif
NULL
};
Libevent是如何打日志的
void log(const char *fmt, ...)
{
char buff[];
va_list ap; va_start(ap, fmt);
int len = vsnprintf(buff, sizeof(buff), fmt, ap);
buff[len] = '\0';
va_end(ap); cout << buff << endl;
}
- va_start:宏定义,引用最后一个固定参数所以它能够对可变参数进行定位。
- va_end:宏定义,函数返回之前一定要调用va_end,这是因为某些实现在函数返回之前需要调整控制信息。
log("hi, are you %s?", "luxon28");
log("name=%s, age=%d", "luoxn28", );
更多va_start/va_end信息请点击:http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html。
定时器的使用
#include <iostream> #include <event.h>
#include <event2/http.h> using namespace std; // Time callback function
void onTime(int sock, short event, void *arg)
{
static int cnt = ;
cout << "Game Over! " << cnt++ << endl; struct timeval tv;
tv.tv_sec = ;
tv.tv_usec = ;
if (cnt < ) {
// Add timer event
event_add((struct event *) arg, &tv);
}
else {
cout << "onTime is over" << endl;
}
} int main(int argc, char **argv)
{
cout << event_get_version() << endl; struct event_base *base = event_init();
struct event ev; evtimer_set(&ev, onTime, &ev); struct timeval timeevent;
timeevent.tv_sec = ;
timeevent.tv_usec = ; event_add(&ev, &timeevent); // Start event loop
event_base_dispatch(base);
event_base_free(base); return ;
}
输出结果如下:
LZ安装的是Libevent版本是2.0版本,event_init()函数初始化一个事件类结构体,其中已经选择好了IO复用函数,比如Linux下一般是epoll;初始化了一个事件活动队列,当事件发生时,会被加入到该事件活动队列中,然后统一执行事件活动队列中的所有事件(也就是调用对应的回调函数)。event_base结构体详细内容如下:
/* 结构体event_base是Libevent的Reactor */
struct event_base {
/* 初始化Reactor时选择的一种后端IO复用机制,并记录在如下字段中 */
const struct eventop *evsel;
/* 指向IO复用机制真正存储的数据,它通过evsel成员的init函数来进行初始化 */
void *evbase; /* 事件变化队列,其用途是:如果一个文件描述符上注册的事件被多次修改,则可以使用缓冲区来避免重复的
* 系统调用(比如epoll_wait)。它仅能用于时间复杂度为O(1)的IO复用技术 */
struct event_changelist changelist; /* 指向信号的后端处理机制,目前仅在singal.h文件中定义了一种处理方法 */
const struct eventop *evsigsel;
/* 信号事件处理器使用的数据结构,其中封装了一个由socketpair创建的管道。它用于信号处理函数和
* 事件多路分发器之间的通信 */
struct evsig_info sig; /* 以下3个成员是添加到该event_base的虚拟事件、所有事件和激活事件的数量 */
int virtual_event_count;
int event_count;
int event_count_active; /* 是否执行完活动事件队列上的剩余的任务之后就退出事件处理 */
int event_gotterm;
/* 是否立即退出事件循环,而不管是否还有任务需要处理 */
int event_break;
/* 是否应该启动一个新的事件循环 */
int event_continue; /* 目前正在处理的活动事件队列的优先级 */
int event_running_priority; /* 事件循环是否启动 */
int running_loop; /* 活动事件队列数组,索引值越小的队列,优先级越高。高优先级的活动事件队列中的事件处理器将被优先处理 */
struct event_list *activequeues;
/* 活动事件队列数组的大小,即该event_base共有nactivequeues个不同优先级的活动事件队列 */
int nactivequeues; /* common timeout logic */ /* 以下3个成员用于管理通用定时器队列 */
struct common_timeout_list **common_timeout_queues;
int n_common_timeouts;
int n_common_timeouts_allocated; /* 存放延时回调函数的链表,事件循环每次成功处理完一个活动事件队列中的所有事件之后,
* 就调用一次延迟回调函数 */
struct deferred_cb_queue defer_queue; /* 文件描述符和IO事件之间的映射关系表 */
struct event_io_map io; /* 信号值和信号事件之间的映射关系表 */
struct event_signal_map sigmap; /* 注册事件队列,存放IO事件处理器和信号事件处理器 */
struct event_list eventqueue; struct timeval event_tv; /* 时间堆 */
struct min_heap timeheap; struct timeval tv_cache; #if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
/** Difference between internal time (maybe from clock_gettime) and
* gettimeofday. */
struct timeval tv_clock_diff;
/** Second in which we last updated tv_clock_diff, in monotonic time. */
time_t last_updated_clock_diff;
#endif /* 多线程支持 */
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
/* threading support */
/** The thread currently running the event_loop for this base */
/* 当前运行该event_base的事件循环的线程 */
unsigned long th_owner_id;
/** A lock to prevent conflicting accesses to this event_base */
void *th_base_lock; /* 锁变量 */
/** The event whose callback is executing right now */
/* 当前事件循环正在执行哪个事件处理器的回调函数 */
struct event *current_event;
/** A condition that gets signalled when we're done processing an
* event with waiters on it. */
/* 条件变量,用于唤醒正在等待某个事件处理完毕的线程 */
void *current_event_cond;
/** Number of threads blocking on current_event_cond. */
int current_event_waiters; /* 等待current_event_cond的线程数 */
#endif /** Flags that this base was configured with */
/* 该vent_base的一些配置参数 */
enum event_base_config_flag flags; /* 下面这些成员变量给工作线程唤醒主线程提供了方法(使用socketpair创建的管道) */
/* Notify main thread to wake up break, etc. */
/** True if the base already has a pending notify, and we don't need
* to add any more. */
int is_notify_pending;
/** A socketpair used by some th_notify functions to wake up the main
* thread. */
evutil_socket_t th_notify_fd[];
/** An event used by some th_notify functions to wake up the main
* thread. */
struct event th_notify;
/** A function used to wake up the main thread from another thread. */
int (*th_notify_fn)(struct event_base *base);
}
event_add()函数是往事件结构体中加入监听的一个事件,这里是定时事件,当定时事件到时,就会执行对应的回调函数。event_base_dispatch()函数开始执行事件监听,对应于epoll的话也就是调用epoll_wait了。最后,当程序执行完毕后,需要调用event_base_free()函数来执行资源的销毁操作,至此,整个定时器事件就执行完毕了。
简单的HTTP服务器
使用Libevent,我们可以用不超过50行代码实现一个简单的HTTP服务器程序,没有听错,就是几十行代码,不像Java那样,需要配置Tomcat,然后编写对应的Servet,配置web.xml等等(如果使用SSM或者SSH的话步骤或许更多一点呦 :( )。下面就是一个简单的HTTP服务器示例代码:
#include <iostream> #include <event2/event.h>
#include <event2/buffer.h>
#include <event2/http.h> using namespace std; #define INFO 1
#define ERR 3
static void log(int level, string info)
{
switch (level) {
case INFO:
cout << "[info] tid[" << pthread_self() << "]: " << info << endl;
break;
case ERR:
cout << "[err] tid[" << pthread_self() << "]: " << info << endl;
break;
default:
break;
}
} /**
* http callback function
*/
void httpHandler(struct evhttp_request *request, void *arg) {
struct evbuffer *buff = evbuffer_new();
if (!buff) {
log(INFO, "evbuffer_new error");
return;
} evbuffer_add_printf(buff, "Hello world</br>");
evbuffer_add_printf(buff, "Server Responsed.</br> Requested: %s<br/>", evhttp_request_get_uri(request));
evbuffer_add_printf(buff, " Host: %s<br/>", evhttp_request_get_host(request));
evbuffer_add_printf(buff, " Command: %d", evhttp_request_get_command(request));
evhttp_send_reply(request, HTTP_OK, "OK", buff);
evbuffer_free(buff);
} int main(int argc, char **argv)
{
struct event_base *base = event_base_new();
struct evhttp *httpServer = evhttp_new(base); int result = evhttp_bind_socket(httpServer, NULL, );
if (result != ) {
log(ERR, "evhttp_bind_socket error");
return -;
} /* 这是http回调函数 */
evhttp_set_gencb(httpServer, httpHandler, NULL);
cout << "Http server start OK..." << endl; event_base_dispatch(base); evhttp_free(httpServer);
event_base_free(base); return ;
}
访问页面如下,192.168.1.150主机是linux服务器。
看到这里,学习Java Web的小伙伴是不是觉得很熟悉,没错,就是像Servlet。LZ个人觉得,对于小型程序来说,使用C/C++的网络库编程程序更爽一点,因为更加"接地气 "一点,也就操作起来更加灵活,使用Java的话肯定要使用Servet容器了,比如Tomcat或者Jboss等,然后各种配置等。但是对于动态Web技术来说,使用Java更爽一点。
Libevent初探的更多相关文章
- 轻量级网络库libevent初探
本文是关于libevent库第一篇博文,主要由例子来说明如何利用该库.后续博文再深入研究该库原理. libevent库简介 就如libevent官网上所写的“libevent - an event n ...
- Libevent的IO复用技术和定时事件原理
Libevent 是一个用C语言编写的.轻量级的开源高性能网络库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大:源代码相当精炼.易 ...
- libevent之Reactor模式
通过前边的一篇博文轻量级网络库libevent初探,我们知道libevent实际上是封装了不同操作系统下的/dev/poll.kqueue.event ports.select.poll和epoll事 ...
- 利用epoll写一个"迷你"的网络事件库
epoll是linux下高性能的IO复用技术,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率.另一点原因就是获取 ...
- 初探领域驱动设计(2)Repository在DDD中的应用
概述 上一篇我们算是粗略的介绍了一下DDD,我们提到了实体.值类型和领域服务,也稍微讲到了DDD中的分层结构.但这只能算是一个很简单的介绍,并且我们在上篇的末尾还留下了一些问题,其中大家讨论比较多的, ...
- CSharpGL(8)使用3D纹理渲染体数据 (Volume Rendering) 初探
CSharpGL(8)使用3D纹理渲染体数据 (Volume Rendering) 初探 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码 ...
- 从273二手车的M站点初探js模块化编程
前言 这几天在看273M站点时被他们的页面交互方式所吸引,他们的首页是采用三次加载+分页的方式.也就说分为大分页和小分页两种交互.大分页就是通过分页按钮来操作,小分页是通过下拉(向下滑动)时异步加载数 ...
- JavaScript学习(一) —— 环境搭建与JavaScript初探
1.开发环境搭建 本系列教程的开发工具,我们采用HBuilder. 可以去网上下载最新的版本,然后解压一下就能直接用了.学习JavaScript,环境搭建是非常简单的,或者说,只要你有一个浏览器,一个 ...
- .NET文件并发与RabbitMQ(初探RabbitMQ)
本文版权归博客园和作者吴双本人共同所有.欢迎转载,转载和爬虫请注明原文地址:http://www.cnblogs.com/tdws/p/5860668.html 想必MQ这两个字母对于各位前辈们和老司 ...
随机推荐
- EC笔记,第二部分:10.让=返回指向*this的引用
Effective C++ 学习笔记 10 让=返回指向*this的引用 Table of Contents 1. 原因 2. 建议:在没有充分理由标新立异前,最好的做法是遵从传统. –by SkyF ...
- 零基础如何学习java更有效呢?
零基础学java,不知道该如何入手?也不知道学习的方向,很多人会问零基础怎么样学习,有没有什么入门的书籍推荐:只要方法正确,零基础学好java也是有机会的哦. 一.理解Java思想 Java是一门面向 ...
- Javascript:Javascript数据类型详解
要成为一个优秀的前端工程师,系统的学习Javascript,有夯实的Javascript基础,以及对语言本身的深刻的理解,是基本功.从Javascript数据类型开始,我将对Javascript知识体 ...
- keleyi菜单0.1.5版本发布了
keleyi菜单是一个让你轻松创建向上弹出菜单的jquery插件. 最新版本0.1.5增加了显示三角形的功能,当一级菜单包含有子菜单时,会在一级菜单的右侧显示一个小三角形.如图所示: 查看例子:htt ...
- CSS3D效果
效果如本博客中右边呢个浅色框框,来自webpack首页(IE绕路0_0) github地址:http://wjf444128852.github.io/demo02/css3/css3d/ 思路: 1 ...
- 用C#缩小照片上传到各种空间
中秋到了,首先祝各位猿友节日快乐!!! 本博文的原名称是“跟我一起用C#压缩照片上传到各种空间”,评论上有人开骂,没办法我这人就是自信霸气,但是既然有人提出来我还是改掉吧,如果文章写得不好的地方欢迎大 ...
- iOS 域名解析
如何在iOS下进行域名的解析? /** * 域名解析ip * * @param hostName 域名 * * @return ip */ +(NSString *) getIPWithHost ...
- Android 手机卫士--设置界面&功能列表界面跳转逻辑处理
在<Android 手机卫士--md5加密过程>中已经实现了加密类,这里接着实现手机防盗功能 本文地址:http://www.cnblogs.com/wuyudong/p/5941959. ...
- IOS开发基础知识--碎片14
1:ZIP文件压缩跟解压,使用ZipArchive 创建/添加一个zip包 ZipArchive* zipFile = [[ZipArchive alloc] init]; //次数得zipfilen ...
- android studio 和idea 导入library工程
idea 导入library方法 把工程Import成module后,具体的操作看图: 同样的,打开Project structure,点开你要作为library的module,然后点击android ...