1. 简介
libevent是一个事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue、IOCP等系统调用管理事件机制。著名分布式缓存软件memcached也是基于libevent,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。
主要特点如下:
- 事件驱动,高性能;
- 轻量级,专注于网络;
- 跨平台,支持 Windows、Linux、Mac Os等;
- 支持多种 I/O多路复用技术, epoll、poll、dev/poll、select 和kqueue 等;
- 支持 I/O,定时器和信号等事件;
2. 安装libevent
本文默认的安装环境为Linux。
1). 可以到libevent官网下载安装包解压到本地目录,也可以从Github上clone到本地目录,然后进行安装。git clone https://github.com/libevent/libevent
。
2). 在libevent的目录下,输入一下命令进行安装。
$ ./configure
$ make
$ make verify # (optional)
$ sudo make install
3. libevent 在socket异步编程示例
创建 libevent 服务器的基本方法是,注册当发生某一操作(比如接受来自客户端的连接)时应该执行的函数,然后调用主事件循环 event_dispatch() 或者 event_base_dispatch()。执行过程的控制现在由 libevent 系统处理。注册事件和将调用的函数之后,事件系统开始自治;在应用程序运行时,可以在事件队列中添加(注册)或删除(取消注册)事件。事件注册非常方便,可以通过它添加新事件以处理新打开的连接,从而构建灵活的网络处理系统。
下例是用libevent库写了一个socket服务端程序,简单回显客户端发来的内容。
socket_event_test.h文件:
#include <iostream>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <event.h>
#define MAX_SIZE 1024 //接收和发送缓冲区大小
define PORT 33288 //绑定端口
using namespace std;
struct event_base *base;
struct sock_event{
struct event read_ev;
struct event write_ev;
char buffer[MAX_SIZE];
sock_event(){}
~sock_event(){}
};
//响应事件函数
void on_write(int sock, short event, void *args);
void on_read(int sock, short event, void *args);
void on_accept(int sock, short event, void *args);
socket_event_test.cc文件:
#include "socket_event_test.h"
#include <cstring>
void on_write(int sock, short event, void *args){
char *buf = (char *)args;
send(sock, buf, strlen(buf)+1, 0);
}
void on_read(int sock, short event, void *args){
struct event write;
int i_size;
struct sock_event sv;
i_size = recv(sock, sv.buffer,MAX_SIZE,0);
if(i_size == 0) return ;
printf("Received from client: %s\n",sv.buffer);
char buf[MAX_SIZE+20];
sprintf(buf,"You say :%s",sv.buffer);
event_set(&sv.write_ev, sock, EV_WRITE, on_write, buf);
event_base_set(base, &sv.write_ev);
event_add(&sv.write_ev,NULL);
}
void on_accept(int sock, short event, void *args){
struct sockaddr_in addr_in;
struct sock_event sv;
int fd;
socklen_t addr_size;
printf("Waiting for accept...\n");
addr_size = sizeof(struct sockaddr_in);
fd = accept(sock, (struct sockaddr*)&addr_in, &addr_size);
if(fd < 0) {
printf("Accept failed!\n");
return ;
}
event_set(&sv.read_ev, fd, EV_READ|EV_PERSIST, on_read, &sv);
event_base_set(base, &sv.read_ev);
event_add(&sv.read_ev,NULL);
}
int main(){
struct sockaddr_in addr_in;
int sock, yes = 1,i_result;
sock = socket(AF_INET, SOCK_STREAM, 0);
//setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
memset(&addr_in, 0, sizeof(addr_in));
printf("Init socket ...\n");
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(PORT);
addr_in.sin_addr.s_addr = INADDR_ANY;
i_result = bind(sock, (struct sockaddr*)&addr_in, sizeof(struct sockaddr));
if(i_result < 0) {
printf("Bind error!\n");
return -1;
}
listen(sock, 5);
printf("Listening...\n");
struct event listen_ev;
base = event_base_new();
event_init();
event_set(&listen_ev, sock, EV_READ|EV_PERSIST, on_accept, NULL);
event_base_set(base, &listen_ev);
event_add(&listen_ev, NULL);
event_base_dispatch(base);
event_dispatch();
event_base_free(base);
return 0;
}
使用如下命令进行编译,要加上-levent
选项:
c++ socket_event_test.c -o socket_event_test -levent
运行socket服务端程序,然后用Telnet命令进行测试,结果如下:
socket服务端:
Init socket …
Listening…
Waiting for accept…
Received from client: hello,world
Received from client: Nice, it worked!
Telnet端:
Trying 127.0.0.1…
Connected to localhost.
Escape character is ‘^]’.
hello,world
You say :hello,world
Nice, it worked!
You say :Nice, it worked!
4. 参考资料
http://www.wangafu.net/~nickm/libevent-book/
http://popozhu.github.io/2013/06/11/libevent_r2_%E5%88%9B%E5%BB%BAevent_base/
https://www.ibm.com/developerworks/cn/aix/library/au-libev/
http://www.cnblogs.com/cnspace/archive/2011/07/19/2110891.html