Libevent初探

Libevent 是一个用C语言编写的、轻量级的开源高性能网络库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大;源代码相当精炼、易读;跨平台,支持 Windows、 Linux、 *BSD 和 Mac Os;支持多种 I/O 多路复用技术, epoll、 poll、 dev/poll、 select 和 kqueue 等;支持 I/O,定时器和信号等事件;注册事件优先级。
Libevent 已经被广泛的应用,作为底层的网络库;比如 memcached、 Vomit、 Nylon、 Netchat等等。Libevent之于C语言网络编程,类似于Nettty之于Java Web编程。学习Netty的小伙伴,不防看下Libevent的实现,会加深对Netty框架的理解~
Libevent的安装教程网上较多,LZ在此就不再赘述,下面直接来点干货-Libevent如何使用。

检查Libevent支持的IO复用方法

Libevent作为一个高性能网络库,内部封装了多种IO复用技术,如果想看下Libevent在当前系统下支持哪些IO复用技术呢?
int main(int argc, char **argv)
{// 版本信息cout << event_get_version() << endl;// 所支持的IO复用方法const char **methods = event_get_supported_methods();for (int i = 0; methods[i] != NULL; i++) {cout << methods[i] << endl;}return 0;
}

输出结果为:(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,
#endifNULL
};

Libevent是如何打日志的

libevent的错误处理底层调用的是va_start/va_end等相关宏,它们所在的头文件是<stdarg.h>,使用C函数库提供的这些函数,我们也可以实现一个自己的打日志程序,以下是一个使用va_start/va_end的测试程序:
void log(const char *fmt, ...)
{char buff[512];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", 23);

  更多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 = 0;cout << "Game Over! " << cnt++ << endl;struct timeval tv;tv.tv_sec = 1;tv.tv_usec = 0;if (cnt < 5) {// Add timer eventevent_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 = 1;timeevent.tv_usec = 0;event_add(&ev, &timeevent);// Start event loopevent_base_dispatch(base);event_base_free(base);return 0;
}

输出结果如下:

  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[2];/** 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, 8080);if (result != 0) {log(ERR, "evhttp_bind_socket error");return -1;}/* 这是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 0;
}

  访问页面如下,192.168.1.150主机是linux服务器。

  看到这里,学习Java Web的小伙伴是不是觉得很熟悉,没错,就是像Servlet。LZ个人觉得,对于小型程序来说,使用C/C++的网络库编程程序更爽一点,因为更加"接地气 "一点,也就操作起来更加灵活,使用Java的话肯定要使用Servet容器了,比如Tomcat或者Jboss等,然后各种配置等。但是对于动态Web技术来说,使用Java更爽一点。

参考资料
1、libevent-百度百科
2、Libevent部分源码

转载于:https://www.cnblogs.com/luoxn28/p/5813860.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/457342.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

特别慢_背什么都特别慢,该怎么提高记忆力?

考研是一项全方位的比拼&#xff0c;除了每天进行知识点的复习&#xff0c;还要做题、总结&#xff0c;最后还得进行背诵记忆&#xff0c;其实什么科目都需要背的&#xff0c;就算数学&#xff0c;该记的概念和公式也是要记忆的&#xff0c;因为会做题更快&#xff0c;提高效率…

第三章 中间件,3.1 万亿级数据洪峰下的分布式消息引擎(作者:冯嘉、誓嘉、尘央、牟羽)...

3.1 万亿级数据洪峰下的分布式消息引擎 前言 通过简单回顾阿里中间件(Aliware)消息引擎的发展史&#xff0c;本文开篇于双11消息引擎面临的低延迟挑战&#xff0c;通过经典的应用场景阐述可能会面临的问题 - 响应慢&#xff0c;雪崩&#xff0c;用户体验差&#xff0c;继而交易…

Linux目录结构和常用命令

一、Linux目录结构 你想知道为什么某些程序位于/bin下&#xff0c;或者/sbin&#xff0c;或者/usr/bin&#xff0c;或/usr/sbin目录下吗&#xff1f;例如&#xff0c;less命令位于/usr/bin目录下。为什么没在/bin中&#xff0c;或/sbin&#xff0c;或/usr/sbin目录中&#xff1…

挂载nfs文件系统_综合架构-day38-NFS服务补充

1.如何让nfs永久挂载-2种方法开机自启动文件1.vim /etc/rc.d/rc.local需要修改执行权限chmod x /etc/rc.d/rc/localmount -t nfs 172.16.1.31:/upload/ /mnt/2.vim /etc/fstab172.16.1.31:/upload /mnt nfs defaults 0 02.exportfs 加载配置生效&#xff0c;等价于优雅重启[15:…

A Neural Algorithm of Artistic Style

本系列文章由 yhl_leo 出品&#xff0c;转载请注明出处。 文章链接&#xff1a; http://blog.csdn.net/yhl_leo/article/details/53931536 1. 资源 Paper: A Neural Algorithm of Artistic StyleTensorFlow version in GitHub: anishathalye/neural-styleCaffe version in GitH…

CSS布局奇淫技巧之--各种居中

居中是我们使用css来布局时常遇到的情况。使用css来进行居中时&#xff0c;有时一个属性就能搞定&#xff0c;有时则需要一定的技巧才能兼容到所有浏览器&#xff0c;本文就居中的一些常用方法做个简单的介绍。 注&#xff1a;本文所讲方法除了特别说明外&#xff0c;都是兼容I…

手写数字识别中多元分类原理_广告行业中那些趣事系列:从理论到实战BERT知识蒸馏...

导读&#xff1a;本文将介绍在广告行业中自然语言处理和推荐系统实践。本文主要分享从理论到实战知识蒸馏&#xff0c;对知识蒸馏感兴趣的小伙伴可以一起沟通交流。摘要&#xff1a;本篇主要分享从理论到实战知识蒸馏。首先讲了下为什么要学习知识蒸馏。一切源于业务需求&#…

linux zip/unzip命令

2019独角兽企业重金招聘Python工程师标准>>> 语  法&#xff1a;zip [-AcdDfFghjJKlLmoqrSTuvVwXyz$][-b <工 作目录>][-ll][-n <字 尾字符串>][-t <日 期时间>][-<压 缩效率>][压 缩文件][文件...][-i <范本样式>][-x <范本样式…

在Windows下不使用密码远程登陆Linux

在登陆Linux进行管理的时候我们通常会使用用户名和密码进行登陆&#xff0c;这样一来是比较麻烦&#xff0c;二来是不安全&#xff0c;为了解决这个问题&#xff0c;我们可以使用公私钥 (public keys和private keys)进行认证。简单来说公钥存放在服务器上&#xff0c;私钥存放在…

Core Data

简介 Core Data是iOS5之后才出现的一个框架&#xff0c;它提供了对象-关系映射(ORM)的功能&#xff0c;即能够将OC对象转化成数据&#xff0c;保存在SQLite数据库文件中&#xff0c;也能够将保存在数据库中的数据还原成OC对象。在此数据操作期间&#xff0c;我们不需要编写任何…

2017将转行进行到底

2016 年说着转行&#xff0c;最后还是在匆匆中找了一份老本行&#xff0c;此刻的心情还是无爱&#xff0c;毕竟螺丝一直分不清啊&#xff0c;不喜欢就是不喜欢。看了django的教程&#xff0c;不得不感叹国外的书写的相对优秀一点&#xff0c;《learning django web development…

mysql非主键索引_主键索引和非主键索引的区别

1. 什么是最左前缀原则&#xff1f;以下回答全部是基于MySQL的InnoDB引擎例如对于下面这一张表如果我们按照 name 字段来建立索引的话&#xff0c;采用B树的结构&#xff0c;大概的索引结构如下如果我们要进行模糊查找&#xff0c;查找name 以“张"开头的所有人的ID&#…

优美的配色方案设计

2019独角兽企业重金招聘Python工程师标准>>> 怎么做好设计配色一直是个难题&#xff0c;虽然网站上有各种各样的色库&#xff0c;但配色仍然至关重要&#xff0c;不得已的话可以亲自动手&#xff0c;况且乐趣满满。 这个没有一套标准&#xff0c;所以看自己怎么喜欢…

mysql死锁释放时间参数_【Mysql】mysql 事务未提交导致死锁 Lock wait timeout exceeded; try restarting transaction 解决办法...

问题场景问题出现环境&#xff1a;1、在同一事务内先后对同一条数据进行插入和更新操作&#xff1b;2、多台服务器操作同一数据库&#xff1b;3、瞬时出现高并发现象&#xff1b;不断的有一下异常抛出&#xff0c;异常信息&#xff1a;org.springframework.dao.CannotAcquireLo…

springmvc视图解析器_SpringMVC视图及REST风格

什么是视图解析器&#xff1f;springMVC用于处理视图最重要的两个接口是ViewResolver和View。ViewResolver的主要作用是把一个逻辑上的视图名称解析成一个真的的视图&#xff0c;而SpringMVC中用于把View对象呈现给客户端的是View对象本身&#xff0c;而ViewResolver只是把逻辑…

mysql5.7.x 1251_MySql-8.0.x免安装版下载与配置,Navicat打开数据库链接报错1251的解决办法...

概述MySQL从5.7一下子跳到了MySQL8.0, 其中的变化必然是很大的, 这里就不说了, 本文主要讲解最新版MySQL安装的事情.实际上5.7版本后的mysql免安装版都是没有data文件和my.ini文件的&#xff0c;下面再具体说明怎么生成&#xff0c;注意不能自己手动新建.下载下载程序必然去官网…

To install 64-bit ODBC drivers

为了更充分的利用硬件资源&#xff0c;我想很多人都开使用64位操作系统了&#xff0c;同时你可以也发现了在64位操作系统上ODBC的驱动找不到了&#xff0c;所以ODBC的东西都没法用了。 因为2007以前版本的Office只有32位版本&#xff0c;所以我们不能在64位系统上使用ODBC。使用…

【Qt开发】QTableWidget设置根据内容调整列宽和行高

QTableWidget要调整表格行宽主要涉及以下一个函数 1.resizeColumnsToContents(); 根据内容调整列宽 2.resizeColumnToContents(int col); 根据内容自动调整给定列宽 3.horizontalHeader()->setResizeMode 把给定列…

深入浅出mysql数据开发_深入浅出MySQL数据库开发、优化与管理维护 PDF扫描版[513KB]...

深入浅出MySQL数据库开发、优化与管理维护 内容介绍&#xff1a;本书从数据库的基础、开发、优化、管理维护4个方面对MySQL进行了详细的介绍&#xff0c;其中每一部分都独立成篇。本书内容实用&#xff0c;覆盖广泛&#xff0c;讲解由浅入深&#xff0c;适合于各个层次的读者。…

Understand Lambda Expressions in 3 minutes(翻译)

本文翻译自CodeProject上的一篇简单解释Lambda表达式的文章&#xff0c;适合新手理解。译文后面我补充了一点对Lambda表达式的说明。 1.什么是Lambda表达式&#xff1f; Lambda表达式是一种匿名方法&#xff0c;多数情况下用来在LINQ中快速创建委托。简单地说&#xff0c;它代表…