1 libevent简介
Libevent是一个用C语言编写的、轻量级的开源高性能事件通知库,主要有以下几个特点:事件驱动,高性能;轻量级,专注于网络,不像ACE那么臃肿庞大;源代码相当精炼、易读;跨平台,支持Windows、Linux、*BSD和MacOs;支持多种I/O多路复用技术,如epoll,poll,dev/poll,select和kqueue等;支持I/O,定时器和信号等事件;注册事件优先级。
Libevent是一个事件通知库,内部使用select、poll、kqueue、IOCP等系统调用管理事件机制。Libevent是用C语言编写的,而且几乎是无处不用函数指针。Libevent支持多线程编程。Libevent已经被广泛应用,作为不少知名软件的底层网络库,比如memcached、Vomit、Nylon、Netchat等。
2 libevent安装
- 下载libevent安装包,下载地址:http://libevent.org/
- 解压安装包,并进入到解压后的目录
tar -zxvf libevent-2.1.12-stable
cd libevent-2.1.12-stable/
- 检测安装环境
./configure
- 编译源文件,生成.o文件和可执行文件
make
- 安装
make install
3 libevent相关函数介绍
libevent的地基–event_base
struct event_base* event_base_new(void)
函数说明:获得event_base结构
参数:无
返回值
- 成功返回event_base结构体指针
- 失败返回NULL
void event_base_free(struct event_base*) //event.h
函数说明:释放event_base指针
int event_reinit(struct event_base* base) //event.h
函数说明:如果有子进程,且子进程也要使用base,则子进程需要对event_base重新初始化,此时需要调用event_reinit函数
函数参数:由event_base_new返回的执行event_base结构的指针
返回值:成功返回0,失败返回-1
等待事件产生–循环等待 event_loop
libevent在地基打好之后,需要等待事件的产生,也就是等待事件被激活,所以程序不能退出,对于epoll来说,我们需要自己控制循环,而在 libevent中也给我们提供了API接口,类似while(1)的功能。
函数如下:
int event_base_dispatch(struct event_base* base)
函数说明:进入循环等待事件
函数说明:由event_base_new函数返回的指向event_base结构的指针
使用libevent库的步骤:
-
创建根节点–event_base_new
-
设置监听事件和数据可读可写的事件的回调函数
设置了事件对应的回调函数以后,当事件产生的时候会自动调用回调函数
-
事件循环–event_base_dispatch
相当于while(1),在循环内部等待事件的发生,若有事件发生则会触发事件对应的回调函数。
-
释放根节点–event_base_free
释放由event_base_new 和event_new创建的资源,分别调用event_base_free和event_free函数。
4 基于libevent实现tcp协议的简单socket服务端通信
#include<iostream>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<event2/event.h>
#include<unistd.h>
#include<map>
std::map<int, struct event*> collection;
void readcb(evutil_socket_t fd, short events, void* arg) {
char buf[BUFSIZ];
int n;
while (true)
{
n = read(fd, buf, sizeof(buf));
if (n <= 0) {
close(fd);
//将通信文件描述符对应的事件从base上删除
event_del(collection[fd]);
std::map<int, struct event*> ::iterator it = collection.find(fd);
collection.erase(it);
printf("client exit,the current number of connection is %d\n", collection.size());
break;
}
for (int i = 0; i < n; i++) {
buf[i] = toupper(buf[i]);
}
printf("recv:%s", buf);
write(fd, buf, n);
if (n < BUFSIZ) {
break;
}
}
}
void conncb(evutil_socket_t fd, short events, void* arg) {
struct event_base* base = (struct event_base*)arg;
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
//接收新的客户端连接
int cfd = accept(fd, (struct sockaddr*)&client_addr, &len);
printf("a new connection is estistabled,ip:%s port:%d\n", inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port));
if (cfd > 0) {
struct event* connev = event_new(base, cfd, EV_READ | EV_PERSIST, readcb, NULL);
if (connev == NULL) {
//退出循环
event_base_loopexit(base, NULL);
exit(0);
}
collection[cfd] = connev;
printf("the current number of connection is %d\n", collection.size());
//将通信文件描述符对应的事件event_base
event_add(connev, NULL);
}
}
int main() {
int lfd = socket(AF_INET, SOL_SOCKET, 0);
if (lfd == -1) {
perror("socket create fail!");
}
//设置端口复用
int opt = 1;
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
struct sockaddr_in serv;
serv.sin_family = AF_INET;
serv.sin_addr.s_addr = htonl(INADDR_ANY);
serv.sin_port = htons(8888);
bind(lfd, (struct sockaddr*)&serv, sizeof(serv));
listen(lfd, 1024);
//创建地基
struct event_base* base = event_base_new();
if (base == NULL) {
perror("event base create error!");
}
//创建监听文件描述符事件
struct event* ev = event_new(base, lfd, EV_READ | EV_PERSIST, conncb, base);
if (ev == NULL) {
perror("event new error!\n");
return -1;
}
//将新的事件节点上base地基
event_add(ev, NULL);
//进入事件循环等待
event_base_dispatch(base);
//释放资源
event_base_free(base);
event_free(ev);
return 0;
}