LIBEVENT 框架
LAMP
从LAMP说起: 是一个缩写,它指一组通常一起使用来运行动态网站或者服务器的自由软件
- Linux - 操作系统
- Apache - 网页服务器
- MySQL - 数据库管理系统(或者数据库服务器)
- PHP Perl 或 Python - 脚本语言
C10K 问题:并发能力突破不了1万连接
libevent
libevent是一个轻量级的开源的高性能的事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。
它被众多的开源项目使用,例如大名鼎鼎的memcached等。
特点:
- 事件驱动,高性能;
- 轻量级,专注于网络(相对于ACE);
- 开放源码,代码相当精炼、易读;
- 跨平台,支持Windows、Linux、BSD和Mac OS;
- 支持多种I/O多路复用技术(epoll、poll、dev/poll、select和kqueue等),在不同的操作系统下,做了多路复用模型的抽象,可以选择使用不同的模型,通过事件函数提供服务;
- 支持I/O,定时器和信号等事件;
- 采用Reactor模式;
libevent是一个典型的reactor模式的实现。
普通的函数调用机制:程序调用某个函数,函数执行,程序等待,函数将结果返回给调用程序(如果含有函数返回值的话),也就是顺序执行的。
Reactor模式的基本流程:应用程序需要提供相应的接口并且注册到reactor反应器上,如果相应的事件发生的话,那么reactor将自动调用相应的注册的接口函数(类似于回调函数)通知你,所以libevent是事件触发的网络库。
libevent的功能
Libevent提供了事件通知,io缓存事件,定时器,超时,异步解析dns,事件驱动的http server以及一个rpc框架。
- 事件通知:当文件描述符可读可写时将执行回调函数。
- IO缓存:缓存事件提供了输入输出缓存,能自动的读入和写入,用户不必直接操作io。
- 定时器:libevent提供了定时器的机制,能够在一定的时间间隔之后调用回调函数。
- 信号:触发信号,执行回调。
- 异步的dns解析:libevent提供了异步解析dns服务器的dns解析函数集。
- 事件驱动的http服务器:libevent提供了一个简单的,可集成到应用程序中的HTTP服务器。
- RPC客户端服务器框架:libevent为创建RPC服务器和客户端创建了一个RPC框架,能自动的封装和解封数据结构。
libevent官网
http://libevent.org/
安装步骤
注意:以root用户身份操作
1、下载源码包
wget https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz
2、解压
tar zxvf libevent-2.1.12-stable.tar.gz
3、配置安装路径
cd libevent-2.1.12-stable
./configure --disable-openssl
4、编译并安装
make
make install
5、测试libevent是否安装成功:
# ls -la /usr/local/include | grep event
6、如果编译后的程序提示找不到libvent的so,则创建库的链接和缓存文件
ldconfig
编译选项 -levent
Linux下libevent主要API介绍
创建事件集
struct event_base *event_base_new(void);
创建事件
struct event event_new
(struct event_base ,evutil_socket_t ,short ,event_callback_fn, void*)
添加事件
int event_add(struct event * ev,const struct timeval* timeout)
删除事件
int event_del(struct event *)
事件循环
int event_base_loop(struct event_base *base, int flags)
int event_base_dispatch(struct event_base *event_base)
libevent使用步骤
1.创建socket
2.创建事件集event_base
3.创建event(socket, EV_READ, callback1) / event(socket, EV_WRITE, callback2)
4.把event添加到事件集event_base
5.event_base_dispatch(evnet_base); event_base_loop();
注 意:
程序的最后调用event_base_dispatch(base);实现事件的循环处理
while()
{
//调用多路复用
event_base_dispatch(base);
……
}
libevent 编程案例
1.使用libevent 改造epoll 范例
//main.c
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <event.h>
#include <event2/event.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
typedef struct _ConnectStat ConnectStat;
#define BUFLEN 1024
struct _ConnectStat {
struct event* ev;
int fd;
char send_buf[BUFLEN];
//PF *handler;//不同页面的处理函数
};
//echo 服务实现相关代码
ConnectStat * stat_init(int fd);
//void accept_connection(int fd, void *data);
void accept_connection(int fd, short events, void* arg);
void do_welcome_handler(int fd, short events, void* arg);
//void do_echo_handler(int fd, void *data);
void do_echo_handler(int fd, short events, void *arg);
void do_echo_response(int fd, short events, void *arg);
struct event_base * ev_base;
void usage(const char* argv)
{
printf("%s:[ip][port]\n", argv);
}
void set_nonblock(int fd)
{
int fl = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}
int startup(char* _ip, int _port) //创建一个套接字,绑定,检测服务器
{
//sock
//1.创建套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror("sock");
exit(2);
}
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
//2.填充本地 sockaddr_in 结构体(设置本地的IP地址和端口)
struct sockaddr_in local;
local.sin_port = htons(_port);
local.sin_family = AF_INET;
local.sin_addr.s_addr = inet_addr(_ip);
//3.bind()绑定
if (bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
{
perror("bind");
exit(3);
}
//4.listen()监听 检测服务器
if (listen(sock, 5) < 0)
{
perror("listen");
exit(4);
}
return sock; //这样的套接字返回
}
ConnectStat * stat_init(int fd) {
ConnectStat * temp = NULL;
temp = (ConnectStat *)malloc(sizeof(ConnectStat));
if (!temp) {
fprintf(stderr, "malloc failed. reason: %m\n");
return NULL;
}
memset(temp, '\0', sizeof(ConnectStat));
temp->fd = fd;
//temp->status = 0;
}
void do_welcome_handler(int fd, short events, void* arg){
const char * WELCOME= "Welcome.\n";
int wlen = strlen(WELCOME);
int n ;
ConnectStat * stat = (ConnectStat *)(arg);
if( (n = write(fd, "Welcome.\n",wlen)) != wlen ){
if(n<=0){
fprintf(stderr, "write failed[len:%d], reason: %s\n",n,strerror(errno));
}else fprintf(stderr, "send %d bytes only ,need to send %d bytes.\n",n,wlen);
}else {
//commUpdateReadHandler(fd, do_echo_handler,(void *)stat);
event_set(stat->ev, fd, EV_READ, do_echo_handler, (void *)stat);
stat->ev->ev_base = ev_base;//必须重置事件集合
event_add(stat->ev, NULL);
}
}
void do_echo_handler(int fd, short events, void *arg) {
ConnectStat * stat = (ConnectStat *)(arg);
char * p = NULL;
assert(stat!=NULL);
p = stat->send_buf;
*p++ = '-';
*p++ = '>';
ssize_t _s = read(fd, p, BUFLEN-(p-stat->send_buf)-1); //2字节"->" +字符结束符.
if (_s > 0)
{
*(p+_s) = '\0';
printf("receive from client: %s\n", p);
//_s--;
//while( _s>=0 && ( stat->send_buf[_s]=='\r' || stat->send_buf[_s]=='\n' ) ) stat->send_buf[_s]='\0';
if(!strncasecmp(p, "quit", 4)){
//退出.
//comm_close(fd);
event_free<