概述
文章目录
- libevent是什么
- 特点
- 组件
- 功能
- Reactor(反应器)模式
- 下载
- 编译
- Visual Studio 2019 on Windows
- 64位静态库
- 示例
- 示例一
- 示例二
libevent是什么
- libevent是一个轻量级的开源的高性能的事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。它被众多的开源项目使用,例如大名鼎鼎的memcached等。
- 基本的socket编程是阻塞/同步的,每个操作除非已经完成或者出错才会返回,这样对于每一个请求,要使用一个线程或者单独的进程去处理,系统资源没法支撑大量的请求(c10k problem),例如内存:默认情况下每个线程需要占用2~8M的栈空间。posix定义了可以使用异步的select系统调用,但是因为其采用了轮询的方式来判断某个fd是否变成active,效率不高[O(n)],连接数一多,也还是撑不住。于是各系统分别提出了基于异步/callback的系统调用,例如Linux的epoll,BSD的kqueue,Windows的IOCP。由于在内核层面做了支持,所以可以用O(1)的效率查找到active的fd。基本上,libevent就是对这些高效IO的封装,提供统一的API,简化开发。
特点
- 事件驱动,高性能;
- 轻量级,专注于网络(相对于ACE);
- 开放源码,代码相当精炼、易读;
- 跨平台,支持Windows、Linux、BSD和Mac OS;
- 支持多种I/O多路复用技术(epoll、poll、dev/poll、select和kqueue等),在不同的操作系统下,做了多路复用模型的抽象,可以选择使用不同的模型,通过事件函数提供服务;
- 支持I/O,定时器和信号等事件;
- 采用Reactor模式;
组件
- libevent_core
所有核心的事件和缓冲功能,包含了所有的event_base、evbuffer、bufferevent和工具函数。 - libevent_extra
定义了程序可能需要,也可能不需要的协议特定功能,包括HTTP、DNS和RPC。 - libevent
这个库因为历史原因而存在,它包含libevent_core和libevent_extra的内容。不应该使用这个库,未来版本的libevent可能去掉这个库。 - libevent_pthreads
添加基于pthread可移植线程库的线程和锁定实现。它独立于libevent_core,这样程序使用libevent时就不需要链接到pthread,除非是以多线程方式使用libevent。
功能
Libevent提供了事件通知,IO缓存事件,定时器,超时,异步解析DNS,事件驱动的HTTP Server以及一个RPC框架。
- 事件通知:当文件描述符可读可写时将执行回调函数。
- IO缓存:缓存事件提供了输入输出缓存,能自动的读入和写入,用户不必直接操作IO。
- 定时器:libevent提供了定时器的机制,能够在一定的时间间隔之后调用回调函数。
- 信号:触发信号,执行回调。
- 异步的dns解析:libevent提供了异步解析dns服务器的dns解析函数集。
- 事件驱动的HTTP服务器:libevent提供了一个简单的,可集成到应用程序中的HTTP服务器。
- RPC客户端服务器框架:libevent为创建RPC服务器和客户端创建了一个RPC框架,能自动的封装和解封数据结构。
Reactor(反应器)模式
libevent是一个典型的reactor模式的实现。Reactor 模型的几个组件:事件源(文件描述符,Socket或Handle)、Reactor 框架、多路复用机制和事件处理程序。
- 普通的函数调用机制:程序调用某个函数,函数执行,程序等待,函数将结果返回给调用程序(如果含有函数返回值的话),也就是顺序执行的。
- Reactor模式的基本流程:应用程序需要提供相应的接口并且注册到reactor反应器上,如果相应的事件发生的话,那么reactor将自动调用相应的注册的接口函数(类似于回调函数)通知你,所以libevent是事件触发的网络库。
下载
lievent
编译
Visual Studio 2019 on Windows
- 修改libevent-2.1.11WIN32-Codenmakeevent2event-config.h
/* Define to 1 if you have the <stdint.h> header file. */
/ * #define EVENT__HAVE_STDINT_H 1 */
改为
/* Define to 1 if you have the <stdint.h> header file. */
#define EVENT__HAVE_STDINT_H 1
- 环境
**************************************************************
** Visual Studio 2019 Developer Command Prompt v16.5.2
** Copyright (c) 2019 Microsoft Corporation
*************************************************************
C:Program Files (x86)Microsoft Visual Studio2019Enterprise>
- 命令
nmake /f Makefile.nmake
CORE_OBJS=event.obj buffer.obj bufferevent.obj bufferevent_sock.obj
bufferevent_pair.obj listener.obj evmap.obj log.obj evutil.obj
strlcpy.obj signal.obj bufferevent_filter.obj evthread.obj
bufferevent_ratelim.obj evutil_rand.obj evutil_time.obj
WIN_OBJS=win32select.obj evthread_win32.obj buffer_iocp.obj
event_iocp.obj bufferevent_async.obj
EXTRA_OBJS=event_tagging.obj http.obj evdns.obj evrpc.obj
!IFDEF OPENSSL_DIR
SSL_OBJS=bufferevent_openssl.obj
SSL_LIBS=libevent_openssl.lib
!ELSE
SSL_OBJS=
SSL_LIBS=
!ENDIF
ALL_OBJS=$(CORE_OBJS) $(WIN_OBJS) $(EXTRA_OBJS) $(SSL_OBJS)
STATIC_LIBS=libevent_core.lib libevent_extras.lib libevent.lib $(SSL_LIBS)
libevent_core.lib: $(CORE_OBJS) $(WIN_OBJS)
lib $(LIBFLAGS) $(CORE_OBJS) $(WIN_OBJS) /out:libevent_core.lib
libevent_extras.lib: $(EXTRA_OBJS)
lib $(LIBFLAGS) $(EXTRA_OBJS) /out:libevent_extras.lib
libevent.lib: $(CORE_OBJS) $(WIN_OBJS) $(EXTRA_OBJS)
lib $(LIBFLAGS) $(CORE_OBJS) $(EXTRA_OBJS) $(WIN_OBJS) /out:libevent.lib
libevent_openssl.lib: $(SSL_OBJS)
lib $(LIBFLAGS) $(SSL_OBJS) /out:libevent_openssl.lib
- 静态库
- libevent.lib
- libevent_core.lib
- libevent_extras.lib
64位静态库
示例
示例一
#include <iostream>
#include <WinSock2.h>
#include "event.h"
#pragma comment(lib,"ws2_32.lib")
//#pragma comment(lib,"libevent.lib")
#pragma comment(lib,"libevent_core.lib")
//#pragma comment(lib,"libevent_extras.lib")
void OnTime(int sock, short event_value, void* arg)
{
std::cout << "Hello World!n";
timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
event_add((struct event*)arg, &tv);
}
int main()
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
return -1;
}
event_init();
struct event ev_time;
evtimer_set(&ev_time, OnTime, &ev_time);
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
event_add(&ev_time, &tv);
event_dispatch();
WSACleanup();
return 0;
}
示例二
#include <iostream>
#include <WinSock2.h>
#include "event.h"
using namespace std;
#pragma comment(lib, "ws2_32.lib")
//#pragma comment(lib, "libevent.lib")
#pragma comment(lib, "libevent_core.lib")
//#pragma comment(lib, "libevent_extras.lib")
// 回调函数声明
void do_accept(evutil_socket_t listener, short event, void* arg);
void read_cb(struct bufferevent* bev, void* arg);
void error_cb(struct bufferevent* bev, short event, void* arg);
void write_cb(struct bufferevent* bev, void* arg);
int main()
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
return -1;
}
// 创建SOCKET
int ret = 0;
evutil_socket_t listener;
listener = socket(AF_INET, SOCK_STREAM, 0);
evutil_make_listen_socket_reuseable(listener);
// 绑定本地地址
SOCKADDR_IN sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ADDR_ANY;
sin.sin_port = htons(6789);
if (bind(listener, (SOCKADDR*)&sin, sizeof(sin)) < 0)
{
cout << "bind出错!" << endl;
return -1;
}
if (listen(listener, 32) < 0)
{
cout << "listen出错!" << endl;
return -1;
}
cout << "正在监听……" << endl;
// <A>设置SOCKET无阻塞模式
evutil_make_socket_nonblocking(listener);
// (1)创建“事件管理器”
struct event_base* base = event_base_new();
if (NULL == base)
{
cout << "event_base_new出错!" << endl;
return -1;
}
// (2)创建事件
struct event* listen_event = event_new(base, listener, EV_READ | EV_PERSIST, do_accept, (void*)base);
// (3)添加事件
event_add(listen_event, NULL);
// (4)启动事件管理循环
event_base_dispatch(base);
cout << "Done!" << endl;
return 0;
}
void do_accept(evutil_socket_t listener, short event, void* arg)
{
struct event_base* base = (struct event_base*)arg;
SOCKADDR_IN sin;
int slen = sizeof sin;
evutil_socket_t fd = accept(listener, (SOCKADDR*)&sin, &slen);
if (fd < 0)
{
cout << "accept出错!" << endl;
return;
}
//if (fd > FD_SETSIZE)
//{
// cout << "accept返回fd超出FD_SETSIZE限制" << endl;
// return;
//}
cout << "accept:fd=" << fd << endl;
// <B>创建“读写事件管理器”,自libevent2之后才有的
struct bufferevent* bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
// <C>设置“读写事件管理器”的回调函数
bufferevent_setcb(bev, read_cb, NULL, error_cb, arg);
// <D>启动“读写事件管理器”
bufferevent_enable(bev, EV_READ | EV_WRITE | EV_PERSIST);
}
void read_cb(struct bufferevent* bev, void* arg)
{
#define MAX_LINE 256
char szLine[MAX_LINE + 1];
evutil_socket_t fd = bufferevent_getfd(bev);
int n = 0;
while (n = bufferevent_read(bev, szLine, MAX_LINE), n > 0)
{
szLine[n] = '