libevent介绍

libevent 是一个轻量级的事件触发的网络库。它适用于windows、linux、bsd等多种平台,它是跨平台的。libevent是c语言编写的一个开源的网络库。

libevent 的设计目标是:

1、可移植性,它是跨平台的。
2、效率高。基于事件驱动(event-driven),采用非阻塞I/O,对事件异步处理;

       libevent API提供了一种当某事件的条件发生时再去调用回调函数去处理机制。事件条件的触发一般是文件描述符可读或可写,定时器时间到;回调函数一般是调用者事先编好的处理函数。这种异步机制类似于linux信号的异步处理机制。

3、可扩展性。既使活动的套接字达到成千上万,它也能很好的工作。(有关于讨论如果连接数上万会有某些瓶颈,我还没来得及深究,暂时没有能力对它评价)

4、轻量级,专注于底层网络库;

5、使用方便。用libevent编写程序的方式应该是稳定的、可移植的。

另外,libevent还有如下特点:

1、支持多种I/O多路复用技术;

几乎所有UNIX平台都支技的poll、select,Linux的epoll,以Solaris为主的/dev/pool,以BSD平台为主的kquque;libevent对这些接口进行了条件预编译封装,从而到了跨平台。
2、支持异步I/O,定时器和信号等事件,宏观上对这三种事件的处理集成在一起;
3、对注册好的事件进行了等待分类,就绪优先级调用;

当添加的是事件是I/O、信号事件时,它会分别加到这两个双链表中,如果是定时器事件,会加一个以时间作为key小根堆中,libevent 之前对定时事件的管理用的是红黑树,小根堆相对而言查找效率上高一些;调度就绪链表是分级的双向链表。

       4、支持多线程。

libevent1.4.7及以前版本已经弃用,1..3之前的版本有不少bug。libevent当前的最新稳定版是2.0.16。

libevent从1.2.* 版本开始支持轻量级的http server 开发,随后陆续还推出轻量级 DNS server、RPC server 开发。

libevent已经被广泛的应用,作为底层的网络库;目前有应用到libevent的应用有:

The usefulness of libevent API is demonstrated by the following applications:

  • Chromium – Google's open-source web browser (uses Libevent on Mac and Linux)
  • Memcached – a high-performance, distributed memory object caching system
  • Transmission – a fast, easy, and free BitTorrent client
  • NTP – the network time protocol that makes your clock right (uses Libevent in SNTP)
  • tmux – A clean, modern, BSD-licensed terminal multiplexer, similar to GNU screen
  • Tor – an anonymous Internet communication system.
  • libevhtp – A fast and flexible replacement for libevent's httpd API
  • Prosody – A Jabber/XMPP server written in Lua
  • PgBouncer – Lightweight connection pooler for PostgreSQL
  • redsocks – a simple transparent TCP -> Socks5/HTTPS proxy daemon.
  • Vomit – Voice Over Misconfigured Internet Telephones
  • Crawl – A Small and Efficient HTTP Crawler
  • Libio – an input/output abstraction library
  • Honeyd – a virtual honeynet daemon – can be used tofight Internet worms.
  • Fragroute – an IDS testing tool
  • Nylon – nested proxy server
  • Disconcert – a Distributed Computing Framework for Loosely-Coupled Workstations.
  • Trickle – a lightweight userspace bandwidth shaper.
  • watchcatd – software watchdog designed to take actions not as drastic as the usual solutions, which reset the machine.
  • ScanSSH – a fast SSH server and open proxy scanner.
  • Nttlscan – a network topology scanner for Honeyd.
  • NetChat – a combination of netcat and ppp's chat.
  • Io – a small programming language; uses libevent for network communication.
  • Systrace – a system call sandbox.
  • SpyBye – detect malware on web pages.
  • GreenSQL – an SQL database firewall.
  • dnsscan – a fast scanner for identifying open recursive dns resolvers
  • Kargo Event – a PHP extension for libevent.
  • wlmproxy – a transparent proxy server for the MSN Messenger protocol

 

Libevent的安装

1、先从官网www.libevent.org下载最新的稳定版本,我前两天下载最新的是libevent-2.0.15-stable,现在更新到libevent-2.0.16-stable了;

2、将软件包解压。如果是从windows平台通过软件连接到linux服务器的,可能要以解压的文件修改权限,通常添加一个可执行权限就可正常安装了;

3、在解压的文件夹目录下,./configure;

4、make;

5、sudo make install;

如果这几个步骤都没有出现错误,说明已经安装好,可以下面命令查看安装的库文件。

ls -al /usr/lib | grep libevent(或 ls -al /usr/local/lib | grep libevent,取决于你安装在哪个目录下面),输出一些库文件信息,说明真的安装好了。

编译时加上选项     -levent。如gcc test.c –o test –levent.

我在编写一个小程序测试的时候,发现了一个有意思的小问题。我的小程序是输出libevent的版本号,我安装的是libevent-2.0.15-stable,结果它却输出了1.4.12-stable,怎么会输出这么老掉牙的版本号?我就纳闷了,去/usr/lib/目录查看,发现里面的库文件确实是1.4.12,我再去/usr/local/lib/下面查看,发现我的libevent装在这个目录下了。原来,那个老版本是同事之前装memcache的时候装上去的,系统优先找到这个目录下面的库文件。那我怎样使用的我最近安装的新版本库呢?其实很简单,不需要卸载旧版本,只要在编译时加上选项-L/usr/local/lib/来告编译器你要使用的库文件在这个目录就可以了。如,我用gcc test.c –o test -L/usr/local/lib/ -levent,一切OK,输出了新版本号2.0.15-stable。

顺便说一下编写程序添加libevent头文件的问题。老版本是include <event.h>,新版本是用include <event2/event.h>,至于从哪个版本开始使用include <event2/event.h>我就没有考证了,当然老版本是兼容旧版本的。新版本改进了老版本的一些接口,也增加了一些新的接口。

 

Libevent整体结构

      libevent分成下面几个组件:

1、  evutil

其功能是抽象出不同平台网络实现差异的通用性。

2、  event and event_base

这这两者libevent的核心。它提供了支持不同平台特有的,基于事件驱动的非阻塞IO的抽象接口。它可以通知你文件描述符在何时可读或可写,处理基本的定时事件以及检测系统信号,然后调用相应的回调函数进行处理。

Event是libevent的基本操作单元。每个event都有一组触发事件的条件,包括:

·         A file descriptor being ready to read from or write to.

·         A file descriptor becoming ready to read from or write to (Edge-triggered IO only).

·         A timeout expiring.

·         A signal occurring.

·         A user-triggered event.

  Events have similar lifecycles. Once you call a Libevent function to set up an event and associate it with an event base, it becomesinitialized. At this point, you canadd, which makes itpending in the base. When the event is pending, if the conditions that would trigger an event occur (e.g., its file descriptor changes state or its timeout expires), the event becomesactive, and its (user-provided) callback function is run. If the event is configuredpersistent, it remains pending. If it is not persistent, it stops being pending when its callback runs. You can make a pending event non-pending bydeleting it, and you can add a non-pending event to make it pending again.

         这里提到了event的几种状态,我在啰嗦一遍:

★初始化状态

    创建event,配置event的基本信息,将event与event_base邦定,完成这个过程event就被初始化了,调用event_new()就可以完成这个过程。

★未决状态

         Event初始化后,再调用event_add()将event添加到监视事件集中,event就变成的未决状态。

★激活状态

         当事件的触发条件发生时,event就变成了激活状态,然后会调用回调函数做相应的操作。

 

3、  bufferevent

为libevent基于事件的核心提供使用更方便的封装。除了通知程序套接字已经准备好读写之外,还让程序可以请求缓冲的读写操作,可以知道何时IO已经真正发生。(bufferevent接口有多个后端,可以采用系统能够提供的更快的非阻塞IO方式,如Windows中的IOCP。)

4、 evbuffer

在bufferevent层之下实现了缓冲功能,并且提供了方便有效的访问函数。

5、 evhttp

一个简单的HTTP客户端/服务器实现。

6、 evdns

一个简单的DNS客户端/服务器实现。

7、 evrpc

一个简单的RPC实现。

      libevent安装后的库文件

    安装libevent时,默认安装下列库:

v libevent_core:所有核心的事件和缓冲功能,包含了所有的event_base、evbuffer、bufferevent和工具函数。

v libevent_extra:定义了程序可能需要,也可能不需要的协议特定功能,包括HTTP、DNS和RPC。

v libevent:这个库因为历史原因而存在,它包含libevent_core和libevent_extra的内容。不应该使用这个库,未来版本的libevent可能去掉这个库。

某些平台上可能安装下列库:

v libevent_pthreads:添加基于pthread可移植线程库的线程和锁定实现。它独立于libevent_core,这样程序使用libevent时就不需要链接到pthread,除非是以多线程方式使用libevent。

v libevent_openssl:这个库为使用bufferevent和OpenSSL进行加密的通信提供支持。它独立于libevent_core,这样程序使用libevent时就不需要链接到OpenSSL,除非是进行加密通信。

 

      libevent的头文件

libevent介绍_第1张图片

libevent公用头文件都安装在event2目录中,分为三类:

v API头文件:定义libevent公用接口。这类头文件没有特定后缀。

To browse the complete documentation of the libevent API, click on any of the following links.

event2/event.h The primary libevent header

event2/thread.h Functions for use by multithreaded programs

event2/buffer.h andevent2/bufferevent.h Buffer management for network reading and writing

event2/util.h Utility functions for portable nonblocking network code

event2/dns.h Asynchronous DNS resolution

event2/http.h An embedded libevent-based HTTP server

event2/rpc.h A framework for creating RPC servers and clients

 

v 兼容头文件:文件名有后缀compat,为已废弃的函数提供兼容的头部包含定义。不应该使用这类头文件,除非是在移植使用较老版本libevent的程序时。

v 结构头文件:这类头文件以相对不稳定的布局定义各种结构体。这些结构体中的一些是为了提供快速访问而暴露;一些是因为历史原因而暴露。直接依赖这类头文件中的任何结构体都会破坏程序对其他版本libevent的二进制兼容性,有时候是以非常难以调试的方式出现。这类头文件具有后缀“_struct.h”。

(还存在 一些在../event2目录中的较老版本libevent的头文件,请参考下节:如果需要使用老版本libevent)

libevent 2.0.*以更合理的、不易出错的方式修正了API。如果可能,编写新程序时应该使用libevent 2.0。但是有时候可能需要使用较老的API,例如在升级已存的应用时,或者支持因为某些原因不能安装2.0或者更新版本libevent的环境时。

较老版本的libevent头文件较少,也不安装在event2目录中。在2.0以及以后版本的libevent中,老的头文件仍然会作为新头文件的封装而存在。

其他关于使用较老版本的提示:

v 1.4版之前只有一个库libevent,它包含现在分散到libevent_core和libevent_extra中的所有功能。

v 2.0版之前不支持锁定:只有确定不同时在多个线程中使用同一个结构体时,libevent才是线程安全的。

下面的节还将讨论特定代码区域可能遇到的已经废弃的API。

libevent源代码文件的说明

         ps:此节引用于“libevent源码深度剖析”

 

Libevent的源代码虽然都在一层文件夹下面,但是其代码分类还是相当清晰的,主要可分为头文件、内部使用的头文件、辅助功能函数、日志、libevent框架、对系统I/O多路复用机制的封装、信号管理、定时事件管理、缓冲区管理、基本数据结构和基于libevent的两个实用库等几个部分。源代码中的test和sample文件夹下面就是一些测试的例子了,可以拿来学习试手。
1)头文件
主要就是event.h:事件宏定义、接口函数声明,主要结构体event的声明;
2)内部头文件
xxx-internal.h:内部数据结构和函数,对外不可见,以达到信息隐藏的目的;
3)libevent框架
event.c:event整体框架的代码实现;
4)对系统I/O多路复用机制的封装
epoll.c:对epoll的封装;

select.c:对select的封装;
devpoll.c:对dev/poll的封装;
kqueue.c:对kqueue的封装;
5)定时事件管理
min-heap.h:其实就是一个以时间作为key的小根堆结构;
6)信号管理
signal.c:对信号事件的处理;
7)辅助功能函数
evutil.h 和evutil.c:一些辅助功能函数,包括创建socket pair和一些时间操作函数:加、减和比较等。
8)日志
log.h和log.c:log日志函数
9)缓冲区管理
evbuffer.c和buffer.c:libevent对缓冲区的封装;
10)基本数据结构
compat/sys下的两个源文件:queue.h是libevent基本数据结构的实现,包括链表,双向链表,队列等;_libevent_time.h:一些用于时间操作的结构体定义、函数和宏定义;
11)实用网络库
http和evdns:是基于libevent实现的http服务器和异步dns查询库;

 

libevent事件处理流程

ps:此节引用于“libevent源码深度剖析”

 

当应用程序向libevent 注册一个事件后,libevent 内部是怎么样进行处理的呢?下面的

图就给出了这一基本流程。

1 )  首先应用程序准备并初始化event,设置好事件类型和回调函数;这对应于前面第步骤

2 和3 ;

2 )  向libevent 添加该事件 event。对于定时事件,libevent 使用一个小根堆管理,key 为超

时时间;对于 Signal 和I/O 事件,libevent 将其放入到等待链表(wait list)中,这是一

个双向链表结构;

3 )  程序调用event_base_dispatch() 系列函数进入无限循环,等待事件,以select() 函数为例;

每次循环前libevent 会检查定时事件的最小超时时间 tv ,根据 tv 设置select() 的最大等

待时间,以便于后面及时处理超时事件;

当select() 返回后,首先检查超时事件,然后检查I/O 事件;

  11

Libevent 将所有的就绪事件,放入到激活链表中;

然后对激活链表中的事件,调用事件的回调函数执行事件处理;

libevent介绍_第2张图片

libevent对event 的管理

从event 结构体中的 3 个链表节点指针和一个堆索引出发,大体上也能窥出 libevent 对

event的管理方法了,可以参见下面的示意图。每次当有事件event转变为就绪状态时,libevent 就会把它移入到active event list[priority]中,其中priority是event的优先级;

接着libevent 会根据自己的调度策略选择就绪事件,调用其cb_callback()函数执行事件

处理;并根据就绪的句柄和事件类型填充cb_callback 函数的参数。

 

libevent的一般编程思路

创建event_base实例

(1) struct event_base *event_base_new(void);方法,创建默认的event_base

        注意:老版本是用struct event_base *event_init(void),这个函数在新版本中已经弃用,因为它会初始化”当前”的event_base,在使用多线程的时候是不安全的。

(2) event_base_new_with_config(),创建带配置的event_base.

调用接口:

struct event_config *event_config_new(void);

struct event_base *event_base_new_with_config(conststruct event_config *cfg);

void event_config_free(struct event_config *cfg);

        

event_base是libevent的中心,每个应用必须有一个,它监视事件未决和活动的事件,并且通知应用程序活动的事件,好让应用程序在事件发生时调用回调函数对它们进行处理。

 

 

 

 

创建event对象

 

对于一个我们想监视的文件描述符,首先得建一个event结构体,然后将fd与event的fd成员邦定在一起。建一个event结构体方法可以是:

(1)event_new()方法

接口:

#define EV_TIMEOUT     0x01

#define EV_READ        0x02

#define EV_WRITE       0x04

#define EV_SIGNAL      0x08

#define EV_PERSIST     0x10

#define EV_ET          0x20  // edge-triggered

 

typedefvoid (*event_callback_fn)(evutil_socket_t,short,void *);

 

struct event *event_new(struct event_base *base, evutil_socket_t fd,

   short what, event_callback_fn cb,

   void *arg);

 

void event_free(struct event *event);

 

The event_new() function tries to allocate and construct a new event for use withbase. The what argument is a set of the flags listed above. (Their semantics are described below.) Iffd is nonnegative, it is the file that we’ll observe for read or write events. When the event is active, Libevent will invoke the providedcb function, passing it as arguments: the file descriptorfd,a bitfield ofall the events that triggered, and the value that was passed in forarg when the function was constructed.

All new events are initialized and non-pending. To make an event pending, call event_add() (documented below).

To deallocate an event, call event_free().It is safe to call event_free() on an event that is pending or active: doing so makes the event non-pending and inactive before deallocating it.

函数说明:

struct event *event_new(struct event_base *base, evutil_socket_t fd,

   short what, event_callback_fn cb,

   void *arg)

Parameters:

 

base 

the event base to which the event should be attached.

 

fd 

the file descriptor or signal to be monitored, or -1.

 

what 

desired events to monitor: bitfield of EV_READ, EV_WRITE, EV_SIGNAL, EV_PERSIST, EV_ET.

 

cb 

callback function to be invoked when the event occurs

 

arg 

an argument to be passed to the callback function

     

Returns:

a newly allocated struct event that must later be freed withevent_free().

        Or,   on an internal error, or invalid arguments, event_new() will return NULL

注:当参数fd-1时,事件只能被定时器触发或用event_active()手动活激事件。

The event flags

EV_TIMEOUT

This flag indicates an event that becomes active after a timeout elapses.

The EV_TIMEOUT flag is ignored when constructing an event: you
can either set a timeout when you add the event, or not.  It is
set in the 'what' argument to the callback function when a timeout
has occurred.

EV_READ

This flag indicates an event that becomes active when the provided file descriptor is ready for reading.

EV_WRITE

This flag indicates an event that becomes active when the provided file descriptor is ready for writing.

EV_SIGNAL

Used to implement signal detection. See "Constructing signal events" below.

EV_PERSIST

Indicates that the event ispersistent. See "About Event Persistence" below.

EV_ET

Indicates that the event should be edge-triggered, if the underlying event_base backend supports edge-triggered events. This affects the semantics of EV_READ and EV_WRITE.

Since Libevent 2.0.1-alpha, any number of events may be pending for the same conditions at the same time. For example, you may have two events that will become active if a given fd becomes ready to read. The order in which their callbacks are run is undefined.

These flags are defined in <event2/event.h>. All have existed since before Libevent 1.0, except for EV_ET, which was introduced in Libevent 2.0.1-alpha.

About Event Persistence

By default, whenever a pending event becomes active (because its fd is ready to read or write, or because its timeout expires), it becomes non-pending right before its callback is executed. Thus, if you want to make the event pending again, you can call event_add() on it again from inside the callback function.

If the EV_PERSIST flag is set on an event, however, the event ispersistent. This means that event remains pending even when its callback is activated. If you want to make it non-pending from within its callback, you can call event_del() on it.

The timeout on a persistent event resets whenever the event’s callback runs. Thus, if you have an event with flags EV_READ|EV_PERSIST and a timeout of five seconds, the event will become active:

·        Whenever the socket is ready for reading.

·        Whenever five seconds have passed since the event last became active.

 

 

See also:

event_free(),event_add(),event_del(),event_assign()

 

(1)    也可以先申明一个结构体,然后调用event_assign()来初始化结构体成员。

创建好event后,用event_add()添加事件通知。这个结构最好分配在堆上,因为event_base要一直对它监视,直到你把它撤销。

 

int event_assign

(

structevent

,

   

structevent_base

,

   

evutil_socket_t 

,

   

short 

,

   

event_callback_fn 

,

   

void * 

 

 

 

)

     

Parameters:

 

ev 

an event struct to be modified

 

base 

the event base to which ev should be attached.

 

fd 

the file descriptor to be monitored

 

events 

desired events to monitor; can be EV_READ and/or EV_WRITE

 

callback 

callback function to be invoked when the event occurs

 

callback_arg 

an argument to be passed to the callback function

Returns:

0 if success, or -1 on invalid arguments.

 

int event_add

(

structevent

ev,

   

const struct timeval * 

timeout

 

 

)

     

Add an event to the set of pending events.

The functionevent_add() schedules the execution of the ev event when the event specified in event_assign()/event_new() occurs, or when the time specified in timeout has elapesed. If atimeout is NULL, no timeout occurs and the function will only be called if a matching event occurs. The event in the ev argument must be already initialized byevent_assign() or event_new() and may not be used in calls to event_assign() until it is no longer pending.

   If the event in the ev argument already has a scheduled timeout, callingevent_add() replaces the old timeout with the new one, or clears the old timeout if the timeout argument is NULL.

If you call event_add() on an event that isalready pending, it will leave it pending, and reschedule it with the provided timeout

 

Note

Do not settv to the time at which you want the timeout to run. If you say "tv→tv_sec = time(NULL)+10;" on 1 January 2010, your timeout will wait 40 years, not 10 seconds

 

Parameters:

 

ev 

an event struct initialized viaevent_set()

 

timeout 

the maximum amount of time to wait for the event, or NULL to wait forever

Returns:

0 if successful, or -1 if an error occurred

 

 

 

 

老版本使用下面的的接口,创建event ,将event和event_base邦定,现在已弃用:

void event_set(struct event *event, evutil_socket_t fd, short what,

       void(*callback)(evutil_socket_t,short,void *),void *arg);

int event_base_set(struct event_base *base, struct event *event);

Deprecated:

event_set() is not recommended for new code, because it requires a subsequent call to event_base_set() to be safe under most circumstances. Use event_assign() or event_new() instead.

 

 

相关函数:

1.判断event的是否存在指定的未决事件

int event_pending

(

const structevent

ev,

   

short 

events,

   

struct timeval * 

tv

 

 

)

     

Checks if a specific event is pending or scheduled.

Parameters:

 

ev 

an event struct previously passed toevent_add()

 

events 

the requested event type; any of EV_TIMEOUT|EV_READ| EV_WRITE|EV_SIGNAL

 

tv 

if this field is not NULL, and the event has a timeout, this field is set to hold the time at which the timeout will expire.

Returns:

true if the event is pending on any of the events in 'what', (that is to say, it has been added), or 0 if the event is not added.

2.设置event的优先级

int event_priority_set

(

structevent

,

   

int 

 

 

 

)

     

Assign a priority to an event.

Parameters:

 

ev 

an event struct

 

priority 

the new priority to be assigned

Returns:

0 if successful, or -1 if an error occurred

See also:

event_priority_init()

3.重新初始化event,与fork结合使用

int event_reinit

(

structevent_base

base

 ) 

 

Reinitialize the event base after a fork.

Some event mechanisms do not survive across fork. The event base needs to be reinitialized with theevent_reinit() function.

Parameters:

 

base 

the event base that needs to be re-initialized

Returns:

0 if successful, or -1 if some events could not be re-added.

See also:

event_base_new()

 

更多参考信息,请查看官网推建的资料

http://www.wangafu.net/~nickm/libevent-book/

 

调用event_base_dispatch()

让它一直循环,调度事件。

 

        还有一个与它功能相似但比它复杂的函数event_base_loop(),它带有两个运行标志。

Event dispatching loop.

This loop will run the event base until either there are no more added events, or until something callsevent_base_loopbreak() or event_base_loopexit().

Parameters:

 

base 

theevent_base structure returned by event_base_new() or event_base_new_with_config()

Returns:

0 if successful, -1 if an error occurred, or 1 if no events were registered.

See also:

event_base_loop()

 

 

int event_base_loop

(

structevent_base

,

   

int 

 

 

 

)

     

Wait for events to become active, and run their callbacks.

This is a more flexible version ofevent_base_dispatch().

By default, this loop will run the event base until either there are no more added events, or until something callsevent_base_loopbreak() or evenet_base_loopexit(). You can override this behavior with the 'flags' argument.

Parameters:

 

eb 

theevent_base structure returned by event_base_new() or event_base_new_with_config()

 

flags 

any combination of EVLOOP_ONCE | EVLOOP_NONBLOCK

Returns:

0 if successful, -1 if an error occurred, or 1 if no events were registered.

See also:

event_base_loopexit(),event_base_dispatch(),EVLOOP_ONCE,EVLOOP_NONBLOCK

 

 

 

 

libevent连接监听器:接收TCP的连接

        Ps:此节翻译于http://www.wangafu.net/~nickm/libevent-book/Ref8_listener.html

 

libevent基于事件的连接监听机制,给了我们一种监听并且接收到达的TCP连接。

这个模块的所有函数和类型都定义在event2/listener.h当中。除非特别注明,它们第一次出现在Libevent 2.0.2-alpha版本中。

创建和释放一个连接监听器对象

调用接口:

struct evconnlistener *evconnlistener_new(struct event_base *base,

   evconnlistener_cb cb,void *ptr,unsigned flags,int backlog,

   evutil_socket_t fd);

struct evconnlistener *evconnlistener_new_bind(struct event_base *base,

   evconnlistener_cb cb,void *ptr,unsigned flags,int backlog,

   conststruct sockaddr *sa,int socklen);

void evconnlistener_free(struct evconnlistener *lev);

 

前面两个函数都会返回在内存分配好的监听器对象。监听器利用event_base通知何时在一个指定的监听套接字上会有一个新的TCP连接。当新连接到达时,它会调用给定的回调函数cb。

参数说明:

Base                  监听新连接的到达

Cb             新连接的到达时调用的回调函数

Ptr            将传递给回调函数

Flags        控制监听器的行为。下面会细讲。

Backlog   最大未决连接数。未决连接:还没被接受的连接(原文:Thebacklog parameter controls the maximum number of pending connections that the network stack should allow to wait in a not-yet-accepted state at any time)。如果blacklog为负,libevent会尽量给一个合适的值。如果它为0,libevent认为你已经在给定的socket上调用的listen()。

前面两个函数的不同点,如果之前已经邦定了socket的端口,则调用evconnlistener_new(),fd就是已经邦定了端口的socket。如果你想让libevent分配和邦定一个socket,则调用evconnlistener_new_bind()。

释放连接监听器用evconnlistener_free().

 

Flags细讲:它是一个位或参数,可是下面的宏。

LEV_OPT_LEAVE_SOCKETS_BLOCKING

      默认,当监听器收到一个新的socke,会把设为非阻塞。

LEV_OPT_LEAVE_SOCKETS_BLOCKING

      当释放一个监听器对象时,会把下面的所有监听的socket关闭掉。

      LEV_OPT_CLOSE_ON_EXEC

             在监听的socket上设置close-on-exec。

      LEV_OPT_REUSEABLE

             在一些平台上,当一个socket被关闭时,它的端口要过一会儿才能被其它socket邦定。LEV_OPT_REUSEABLE使得这个廷时消失。

LEV_OPT_THREADSAFE

      为监听器加锁,出于多线程方面的安全。

 

连接监听器的回调函数

调用接口:

typedefvoid (*evconnlistener_cb)(struct evconnlistener *listener,

   evutil_socket_t sock,struct sockaddr *addr,int len,void *ptr);

        当新连接到达时,回调函数将会被调用。

参数说明:

Listener            接收连接的监听器。

Sock                   一个新socket

Addr和len      接收连接的地址和它的长度

Ptr                     来自于evconnlistener_new()的参数ptr

 

 

启用和禁用监听器对象

        用来临时地启用和禁用监听器对象监听新连接。

调用接口:

int evconnlistener_disable(struct evconnlistener *lev);

int evconnlistener_enable(struct evconnlistener *lev);

 

 

更改监听器对象的回调函数

调用接口:

void evconnlistener_set_cb(struct evconnlistener *lev,

   evconnlistener_cb cb,void *arg);

 

 

查询监听器对象的socket和event_base

调用接口:

evutil_socket_t evconnlistener_get_fd(struct evconnlistener *lev);

struct event_base *evconnlistener_get_base(struct evconnlistener *lev);

查看错误

调用接口:

typedefvoid (*evconnlistener_errorcb)(struct evconnlistener *lis,void *ptr);

void evconnlistener_set_error_cb(struct evconnlistener *lev,

   evconnlistener_errorcb errorcb);

你可以设置一个检测错误的回调函数,用来检测监听器对象调用accept()失败的错误。检测错误的回调函数都会在每次发生错误时被调用。这个回调函数的第一个参数是监听器对象,第二个参数是evconnlistener_new()的ptr参数。

 

Bufferevents:概念与基础

Ps:本节译自http://www.wangafu.net/~nickm/libevent-book/Ref6_bufferevent.html

 

Libevent提供了一个读写缓冲机制,并且不同的缓冲事件类型可以共用同一个接口。

有以下几种缓冲事件类型:

socket-based bufferevents

        socket流的接收和发送缓冲。

asynchronous-IO bufferevents

      利用Windows IOCP接口向一个socket流接收和发送数据的缓冲,只能用于Windows。

filtering bufferevents

        在把数据传给bufferevent的使用对象之前,可以对接收和发送的数据进行处理——比如,压缩和转化数据——的缓冲。

paired bufferevents

        可以把一个bufferevents把数据传到另一个bufferevents的bufferevents对。

 

注意:截至Libevent 2.0.2-alpha版本,并不是下面每一个描述的接口都适用于所有buffervent类型,不过这个缺点可能会在以后的版本中改进。Bufferevents目前只支持面向流的TCP,在以后版本中可能会去持面向数据报的UDP

 

      每一个bufferevent都有一个输入缓冲和一个输出缓冲。这些都定义在struct evbuffer当中。当有数据写时,就把它写入输出缓冲;当有数据读时,就从输入缓冲读出。

      每一个bufferevent都有一个与数据相关的回调函数:读回调函数和写回调函数。默认地,当传输层有任何数据可读时会调用读回调函数;当输出缓冲有足够的数据写入传输层时,将调用写回调函数“清空”(写入传输层)输出缓冲。你可以调节bufferevent的读/写“水印标记”(上/下限)来改变回调函数的调用次数。

      每一个bufferevent都有四个“水印标记”:

Read low-water mark——输入缓冲的下限

      当读事件发现使得bufferevent的输入缓冲大于等于这个值,读回调函数将会被调用。默认为0,以便每次读都会调用读回调函数。

Read high-water mark——输入缓冲的上限

      当bufferevent的输入缓冲的数据量达到这个值时,bufferevent就会停止读,直到足够的数据从输入缓冲取走使得输入缓冲的数据量低于这个值。默认为无限大,以便bufferevent不会因为输入缓冲的大小面停止读。

Write low-water mark——输出缓冲下限

      当一次写操作发生使得输出缓冲的数据量少于等于这个值时,写回调函数将被调用。默认为0,以便只有当输出缓冲为空时才调用写回调函数。

Write high-water mark——输出缓冲上限

      这个值不是被bufferevent直接使用,在bufferevent被用作另一个bufferevent的传输层时,它有特别的意义,可以查看filtering bufferevents对它的使用。

socket-based bufferevents

创建一个socket-based bufferevent

调用接口:

struct bufferevent *bufferevent_socket_new(

   struct event_base *base,

   evutil_socket_t fd,

   enum bufferevent_options options);

Thebase is an event_base, andoptions is a bitmask of bufferevent options (BEV_OPT_CLOSE_ON_FREE, etc). Thefd argument is an optional file descriptor for a socket. You can set fd to -1 if you want to set the file descriptor later.

This function returns a bufferevent on success, and NULL on failure.

The bufferevent_socket_new() function was introduced in Libevent 2.0.1-alpha.

Options说明:

BEV_OPT_CLOSE_ON_FREE

When the bufferevent is freed, close the underlying transport. This will close an underlying socket, free an underlying bufferevent, etc.

BEV_OPT_THREADSAFE

Automatically allocate locks for the bufferevent, so that it’s safe to use from multiple threads.

BEV_OPT_DEFER_CALLBACKS

When this flag is set, the bufferevent defers all of its callbacks, as described above.

BEV_OPT_UNLOCK_CALLBACKS

By default, when the bufferevent is set up to be threadsafe, the bufferevent’s locks are held whenever the any user-provided callback is invoked. Setting this option makes Libevent release the bufferevent’s lock when it’s invoking your callbacks.

(Libevent 2.0.5-beta introduced BEV_OPT_UNLOCK_CALLBACKS. The other options above were new in Libevent 2.0.1-alpha.)

 

在socket-based bufferevents上建立连接

If the bufferevent’s socket is not yet connected, you can launch a new connection.

调用接口:

int bufferevent_socket_connect(struct bufferevent *bev,

   struct sockaddr *address,int addrlen);

The address and addrlen arguments are as for the standard call connect(). If the bufferevent does not already have a socket set, calling this function allocates a new stream socket for it, and makes it nonblocking.

If the buffereventdoes have a socket already, calling bufferevent_socket_connect() tells Libevent that the socket is not connected, and no reads or writes should be done on the socket until the connect operation has succeeded.

It is okay to add data to the output buffer before the connect is done.

This function returns 0 if the connect was successfully launched, and -1 if an error occurred.

 

通用的bufferevent操作

(1)Freeing a bufferevent

接口:

void bufferevent_free(struct bufferevent *bev);

如果bufferevente有未决的延时调用,则会在这些调用完成后才会删除。

 

(2)操作回调函数,读写“水印标记”的设置,启动操作

接口:

typedefvoid (*bufferevent_data_cb)(struct bufferevent *bev,void *ctx);

typedefvoid (*bufferevent_event_cb)(struct bufferevent *bev,

   short events,void *ctx);

 

void bufferevent_setcb(struct bufferevent *bufev,

   bufferevent_data_cb readcb, bufferevent_data_cb writecb,

   bufferevent_event_cb eventcb,void *cbarg);

The bufferevent_setcb() function changes one or more of the callbacks of a bufferevent. The readcb, writecb, and eventcb functions are called (respectively) when enough data is read, when enough data is written, or when an event occurs. The first argument of each is the bufferevent that has had the event happen. The last argument is the value provided by the user in the cbarg parameter of bufferevent_callcb(): You can use this to pass data to your callbacks. Theevents argument of the event callback is a bitmask of event flags: see "callbacks and watermarks" above.

You can disable a callback by passing NULL instead of the callback function. Note the all callback functions on a bufferevent share a single cbarg value, so changing it will affect all of them.

This function was introduced in Libevent 1.4.4. The type names "bufferevent_data_cb" and "bufferevent_event_cb" are new in Libevent 2.0.2-alpha.

 

禁用/启用bufferevent

void bufferevent_enable(struct bufferevent *bufev, short events);

void bufferevent_disable(struct bufferevent *bufev, short events);

 

short bufferevent_get_enabled(struct bufferevent *bufev);

You can enable or disable the events EV_READ, EV_WRITE, or EV_READ|EV_WRITE on a bufferevent. When reading or writing is not enabled, the bufferevent will not try to read or write data.

There is no need to disable writing when the output buffer is empty: the bufferevent automatically stops writing, and restarts again when there is data to write.

Similarly, there is no need to disable reading when the input buffer is up to its high-water mark: the bufferevent automatically stops reading, and restarts again when there is space to read.

By default, a newly created bufferevent has writing enabled, but not reading.

You can call bufferevent_get_enabled() to see which events are currently enabled on the bufferevent.

These functions were introduced in Libevent 0.8, except for bufferevent_get_enabled(), which was introduced in version 2.0.3-alpha.

 

设置读/写“水印标记”

void bufferevent_setwatermark(struct bufferevent *bufev, short events,

   size_t lowmark, size_t highmark);

The bufferevent_setwatermark() function adjusts the read watermarks, the write watermarks, or both, of a single bufferevent. (If EV_READ is set in the events field, the read watermarks are adjusted. If EV_WRITE is set in the events field, the write watermarks are adjusted.)

A high-water mark of 0 is equivalent to "unlimited".

This function was first exposed in Libevent 1.4.4.

 

还不一些接口就没一一列出来了,请参考上面给的网站。

 

 

注意事项

Never call event_assign() on an event that is already pending in an event base. Doing so can lead to extremely hard-to-diagnose errors. If the event is already initialized and pending, call event_del() on it before you call event_assign() on it again.

 

Don’t set a timeout on a signal event. It might not be supported.

Caveats when working with signals

With current versions of Libevent, with most backends, only one event_base per process at a time can be listening for signals. If you add signal events to two event_bases at once ---even if the signals are different!--- only one event_base will receive signals.

The kqueue backend does not have this limitation.

 

拓展

  •  The C10K problem,讨论连线数破万时会遇到的瓶颈。
  •  Libevent 2.0 book, libevent 2.0参考书籍
  •  liboop - another asynchronous event notification library,另一个异步事件库
  •  A benchmark by the libev author comparing libevent with libev, a similar library,libevent与一个类似的库libev之间比较的基准

 

参考:
libevent源码深度解剖

www.libevent.org

你可能感兴趣的:(libevent介绍)