libevent最初由Nick Mathewson在2000年创建, 是一个用C语言编写的、轻量级的开源高性能事件通知库,主要有以下几个亮点:事件驱动高性能;轻量级,专注于网络;源代码相当精炼、易读;跨平台,支持 Windows、 Linux、 *BSD 和 Mac Os;支持多种 I/O 多路复用技术, epoll、 poll、 dev/poll、 select 和 kqueue 等;支持 I/O,定时器和信号等事件;注册事件优先级。
Libevent 已经被广泛的应用,作为底层的网络库;比如 memcached、 Vomit、 Nylon、 Netchat等等。
libevent的不足:
1、全局变量的使用,让libevent很难在多线程环境中使用。
2、HTTP和DNS服务器等组件实现较差,安全性低。
3、定时器不精确,无法很好地处理时间跳变。
4、主结构体很大,包含了I/O,定时器和信号处理器等。
libev是libevent的进化版,移除了http/dns组件,优化了结构,单windows平台实现较差。
nodejs最初基于libev实现,为了满足windows需求创建了libuv。
libevent库编译
linux平台使用cmake编译
git clone git@github.com:libevent/libevent.git
cd libevent
mkdir build && cd build
cmake .. # Default to Unix Makefiles.
make
make verify # (optional)
include头文件功能
include/event2/buffer_compat.h
buffer.h 中的过时和不用的版本:仅提供向后兼容。
include/event2/buffer.h
用于缓冲网络发送或接收数据的功能。
include/event2/bufferevent_compat.h
提供兼容性,包含被弃用的 bufferevent_new 等函数。
include/event2/bufferevent_ssl.h
bufferevent 的 OpenSSL 支持。
include/event2/bufferevent_struct.h
bufferevent_struct.h 的使用已完全弃用;这些结构只公开,以便与使用它们的Libevent 2.0之前编写的程序向后兼容。
include/event2/bufferevent.h
用于缓冲网络发送或接收数据的功能。Bufferevents 比 evbuffers 级别更高:每个 Bufferevent 都有一个底层用于读取的 evbuffer 和一个用于写入的 evbuffer ,以及在某些情况下被调用的回调
include/event2/dns_compat.h
dns.h中函数的潜在非线程安全版本:仅为向后兼容性而提供。
include/event2/dns_struct.h
dns的数据结构。使用这些结构可能会损害与更高版本的Libevent的前向兼容性:小心!
include/event2/dns.h
异步DNS解析
include/event2/event_compat.h
event.h中函数的潜在非线程安全版本:仅为向后兼容性而提供。
include/event2/event_struct.h
event.h使用的结构。定义了 event event_callback 结构体。
include/event2/event.h
libevent库的核心功能,等待和接收事件以及使用事件库的核心功能。用于与各种平台特
定的、基于事件的非阻塞 IO 后端进行交互。它可以让你知道套接字什么时候是可读或可写的,
执行基本的超时功能,以及检测操作系统信号。
include/event2/http_compat.h
http.h中函数的潜在非线程安全版本仅用于向后兼容性。
include/event2/http_struct.h
http的数据结构。使用这些结构可能会损害与更高版本的Libevent的前向兼容性:小心!
include/event2/http.h
基于libevent的嵌入式HTTP服务器。
include/event2/keyvalq_struct.h
修复了人们不必使用<sys/queue.h>运行的问题,此代码与event_struct.h重复。
include/event2/listener.h
TCP监听器,监听接入的tcp链接。
include/event2/rpc_compat.h
rpc.h中函数的弃用版本:仅为向后兼容性而提供。
include/event2/rpc_struct.h
rpc.h使用的结构。直接使用这些结构可能会损害前向兼容性:小心!
include/event2/rpc.h
为RPC服务器和客户端提供基本支持。
include/event2/tag_compat.h
tag.h中的过时/弃用函数;仅提供向后兼容性。
include/event2/tag.h
用于将标记数据读取和写入缓冲区的辅助函数。
include/event2/thread.h
使用Libevent的多线程应用程序的函数。
include/event2/util.h
跨平台可移植性,相关套接字操作的常见便利功能,生成随机数。
include/event2/visibility.h
符号导出相关。
include/evdns.h
在Libevent 2.0及更高版本中,<evdns.h>标头已被弃用;请改用<event2/evdns.h>。
include/event.h
在Libevent 2.0及更高版本中,<event.h>标头已被弃用;请改用<event2/event.h>。
include/evhttp.h
在Libevent 2.0及更高版本中,<evhttp.h>标头已被弃用;请改用<event2/http.h>。
include/evrpc.h
在Libevent 2.0及更高版本中,<evrpc.h>标头已被弃用;请改用<event2/rpc.h>。
include/evutil.h
在Libevent 2.0及更高版本中,<evutil.h>标头已被弃用;请改用<event2/util.h>。
libevent库实现tcp服务端
sample/hello-world.c
此示例程序提供了一个简单的tcp服务器程序,用于侦听端口9995上的TCP连接。当有tcp客户端接入时,会向客户端发送一条消息,发送完成后关闭链接。
使用ctrl-c可以优雅地退出程序。
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifndef _WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
# include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
static const char MESSAGE[] = "Hello, World!\n";//发送给客户端的消息
static const int PORT = 9995;//监听的端口号
static void listener_cb(struct evconnlistener *, evutil_socket_t,
struct sockaddr *, int socklen, void *);
static void conn_writecb(struct bufferevent *, void *);
static void conn_eventcb(struct bufferevent *, short, void *);
static void signal_cb(evutil_socket_t, short, void *);
int
main(int argc, char **argv)
{
struct event_base *base;
struct evconnlistener *listener;
struct event *signal_event;
struct sockaddr_in sin = {0};
#ifdef _WIN32
WSADATA wsa_data;
WSAStartup(0x0201, &wsa_data);
#endif
base = event_base_new();//1.创建 event_base(include/event2/event.h)
if (!base) {
fprintf(stderr, "Could not initialize libevent!\n");
return 1;
}
sin.sin_family = AF_INET;//IPv4 网络协议的套接字类型
sin.sin_port = htons(PORT);//端口号
//2.分配一个新的 evconnlistener 对象来监听给定地址上的传入TCP连接。(include/event2/listener.h)
//LEV_OPT_CLOSE_ON_FREE:当连接监听器释放时,会自动关闭底层的socket。
//LEV_OPT_REUSEABLE:关闭该socket后,其他socket可以马上使用同一个端口。
listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
(struct sockaddr*)&sin,
sizeof(sin));
if (!listener) {
fprintf(stderr, "Could not create a listener!\n");
return 1;
}
//3.创建一个信号事件,当执行ctrl-c时响应SIGINT事件,执行signal_cb回调,底层是执行了 event_new(include/event2/event.h)
signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);
//4.将事件添加到待处理事件集中。(include/event2/event.h)
if (!signal_event || event_add(signal_event, NULL)<0) {
fprintf(stderr, "Could not create/add a signal event!\n");
return 1;
}
//5.事件调度循环,此循环将运行事件库,底层运行 event_base_loop(include/event2/event.h)
event_base_dispatch(base);
evconnlistener_free(listener);//6.禁用并取消分配的 evconnlistener(include/event2/listener.h)
event_free(signal_event);//7.释放 event_new 分配返回的结构体event*。(include/event2/event.h)
event_base_free(base);//8.释放分配与事件库event_base关联的所有内存,并释放该库。(include/event2/event.h)
printf("done\n");
return 0;
}
//有tcp链接接入时执行的回调
//接口定义在(include/event2/bufferevent.h)
static void
listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *user_data)
{
struct event_base *base = user_data;
struct bufferevent *bev;
//在现有的套接字上创建一个新的套接字缓冲区。
//BEV_OPT_CLOSE_ON_FREE:当释放此缓冲区,我们关闭底层文件描述符
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
fprintf(stderr, "Error constructing bufferevent!");
event_base_loopbreak(base);//立即中止活动的 event_base_loop
return;
}
bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);//更改缓冲器的回调。
bufferevent_enable(bev, EV_WRITE);//启用bufferevent写。
bufferevent_disable(bev, EV_READ);//禁用bufferevent读。
bufferevent_write(bev, MESSAGE, strlen(MESSAGE));//将数据写入缓冲区buffer。
}
//已经写入足够的数据(输出缓冲数据低于阈值,由bufferevent_setwatermark设置)执行的回调
static void
conn_writecb(struct bufferevent *bev, void *user_data)
{
struct evbuffer *output = bufferevent_get_output(bev);// 返回输出缓冲区。
if (evbuffer_get_length(output) == 0) {// 返回存储在事件缓冲区中的总字节数
printf("flushed answer\n");
bufferevent_free(bev);//释放分配与缓冲器结构相关联的存储空间。
}
}
//在文件描述符上有事件时调用的回调,主要是socket监听到错误时的回调
static void
conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
if (events & BEV_EVENT_EOF) {
printf("Connection closed.\n");
} else if (events & BEV_EVENT_ERROR) {
printf("Got an error on the connection: %s\n",
strerror(errno));/*XXX win32*/
}
/* None of the other events can happen here, since we haven't enabled
* timeouts 其他事件都不会在这里发生,因为我们没有启用超时 */
bufferevent_free(bev);//释放分配与缓冲器结构相关联的存储空间。
}
//监听到某个信号时执行的回调(SIGINT)
static void
signal_cb(evutil_socket_t sig, short events, void *user_data)
{
struct event_base *base = user_data;
struct timeval delay = { 2, 0 };
printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");
event_base_loopexit(base, &delay);//在指定时间后退出事件循环
}