Libevent网络编程

总述

介绍

Libevent是一个用于开发可伸缩网络服务器的事件通知库。libevent API提供了一种机制,用于在文件描述符上发生特定事件或达到超时后执行回调函数。此外,libevent还支持由于信号或常规超时引起的回调。

Libevent旨在取代事件驱动网络服务器中的事件循环。应用程序只需要调用event_dispatch(),然后动态地添加或删除事件,而不必更改事件循环。目前libevent支持/dev/poll、kqueue(2)、select(2)、poll(2)和epoll(4)。它还具有实时信号的实验支持。内部事件机制完全独立于公开的事件API,对libevent进行简单更新就可以提供新功能,而无需重新设计应用程序。因此,Libevent支持可移植的应用程序开发,并提供了操作系统上可用的最可伸缩的事件通知机制。Libevent也可以用于多线程应用程序。Libevent可以在Linux, *BSD, Mac OS X, Solaris和Windows上编译。

标准的使用

每个使用libevent的程序都必须包含标头,并将 -levent 标志传递给链接器。在使用库中的任何函数之前,必须调用event_init()或event_base_new()来执行libevent库的一次性初始化。

事件通知

对于希望监视的每个文件描述符,必须声明一个事件结构并调用event_set()来初始化结构的成员。要启用通知,可以通过调用event_add()将结构添加到监视事件列表中。只要事件结构处于活动状态,它就必须保持分配状态,因此应该在堆上分配它。最后,调用event_dispatch()来循环和分派事件。

I / O缓冲区

Libevent提供了常规事件回调之上的抽象。这种抽象称为缓冲事件。缓冲事件提供了自动填充和清空的输入和输出缓冲区。缓冲事件的用户不再直接处理I/O,而是从输入缓冲区读取并写入输出缓冲区。

一旦通过buffer使用event_new()初始化,bufferevent结构就可以通过bufferevent_enable()和bufferevent_disable()重复使用。不是直接读取和写入套接字,而是调用bufferevent_read()和bufferevent_write()。

当读取启用时,bufferevent将尝试从文件描述符读取并调用读取回调。每当输出缓冲区被耗尽到写入低水位(默认为0)以下时,就会执行写回调。

计时器

除了简单的计时器之外,libevent还可以将超时事件分配给文件描述符,当文件描述符上没有任何活动而经过一定时间时,就会触发这些事件。
timeout_set()函数的作用是:初始化一个事件结构以用作超时。
初始化后,必须使用timeout_add()激活事件。
要取消超时,调用timeout_del()。

异步DNS解析

libevent提供了一个异步DNS解析器,应该使用它来代替标准的DNS解析器函数。可以通过在程序中包含头文件来导入这些函数。在使用任何解析器函数之前,必须调用evdns_init()来初始化库。要将主机名转换为IP地址,可以调用evdns_resolve_ipv4()函数。要执行反向查找,需要调用evdns_resolve_reverse()函数。所有这些函数都使用回调来避免在执行查找时阻塞。

事件驱动的HTTP服务器

libevent提供了一个非常简单的事件驱动的HTTP服务器,可以嵌入到程序中,用于服务HTTP请求。

要使用此功能,需要在程序中包含标头。通过调用evhttp_new()创建服务器。
使用evhttp_bind_socket()添加要侦听的地址和端口。
然后注册一个或多个回调来处理传入的请求。
每个URI都可以通过evhttp_set_cb()函数被分配回调。
通用回调函数也可以通过evhttp_set_gencb()注册;如果没有为给定URI注册其他回调,则调用此回调。

RPC服务器和客户端的框架

libevents为创建RPC服务器和客户端提供了一个框架。它负责对所有数据结构进行封送和解封送。

API参考

要浏览libevent API的完整文档,见reference

event2/event.h主libevent头文件
event2/ Buffer .h网络读写缓冲区管理
event2/ DNS .h异步DNS解析
event2/ HTTP .h嵌入式基于libevent的HTTP服务器
evrpc.h用于创建RPC服务器和客户端的框架

流程参考

API及调用顺序为:

event_base()初始化event_base
event_set()初始化event
event_base_set()将event绑定到指定的event_base上
event_add()将event添加到事件链表上,注册事件
event_base_dispatch()循环、检测、分发事件

事件基初始化

使用事件前需要先初始化。设置current_base全局变量,表示与事件没有关联的缺省基数。
函数已弃用,与当前的事件基相关联,导致多线程不安全。替换的安全函数:event_base_new

	
struct event_base* event_init	(	void 		 ) 
//Initialize the event API.

event_base_set(), event_base_new()可替换
event_base_new()做的工作主要就是对结构体event_base的初始化的作用,设置一些参数这类,event_init实际上就是对event_base_new的封装


int event_base_set	(struct event_base * ,struct event * )	

事件初始化

根据初始化参数,初始化

void 	event_set (struct event *, evutil_socket_t,short, void(*)(evutil_socket_t, short, void *), void*)
 	//Prepare an event structure to be added.

事件绑定

将event_base_set()将event绑定到指定的event_base上

int event_base_set	(	struct event_base * ,struct event * )			
//Associate a different event base with an event.
//Parameters:
//eb 	the event base
//ev 	the event

事件添加

Parameters:
ev an event struct initialized via event_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

根据时间类型添加到不同的列表中;
1.将event注册到event_base的I/O多路复用要监听的事件链表中
2.将event注册到event_base的已注册事件链表中
3.如果传入了超时时间,则删除旧的超时时间,重新设置,并将event添加到event_base的小根堆中;

如果没有传入超时时间,则不会添加到小根堆中。
函数内添加到I/O多路复用监听事件链表、已注册事件链表、小根堆中都是通过event_queue_insert()完成的,相应的删除工作都是通过event_queue_remove()完成的。

int event_add	(	struct event * ,const struct timeval * );			
//Add an event to the set of monitored events.

函数event_add()在event_set()中指定的事件发生时或至少在tv中指定的时间内调度ev事件的执行。

如果tv为NULL,则不会发生超时,只有在文件描述符上发生匹配事件时才会调用该函数。

ev参数中的事件必须已经被event_set()初始化,并且不能在调用event_set()时使用,直到它超时或被event_del()删除。

如果ev参数中的事件已经有一个预定的超时,旧的超时将被新的超时取代。

事件分发

Parameters:
eb the event_base structure returned by event_init()

int event_base_dispatch	(	struct event_base * 		 ) 	
//Threadsafe event dispatching loop.

调用了event_base_loop函数
event_base_loop()主要就是循环、检测、分发事件

按照代码流程;
1.信号标记被设置,则调用信号的回调函数
2.根据定时器最小时间,设置I/O多路复用的最大等待时间,这样即使没有I/O事件发生,也能在最小定时器超时时返回。
3.调用I/O多路复用,监听事件,将活跃事件添加到活跃事件链表中
4.检查定时事件,将就绪的定时事件从小根堆中删除,插入到活跃事件链表中

libevent的核心就event_base_loop();在这其中检测和分发通过I/O多路复用来完成,比如我们经常使用的poll和epoll,通过epoll.c就可以看到源码;其实原理与我们之前学习到的epoll编程是很类似的,只是多了一部分的处理方式,达到与整合系统互相呼应的效果;

Reference

https://www.monkey.org/~provos/libevent/doxygen-2.0.1/include_2event2_2event_8h.html#2efd0ac7e54428d1941f212bc16ed9ce

你可能感兴趣的:(网络,服务器,linux,c语言)