装饰网站设计模板,wordpress淘宝商城模板,竹中建设官方网站,深圳展览设计网站建设目录 参考什么是libevent应用核心实现libevent的地基event_base等待事件产生#xff0c;循环监听event_loop退出循环监听event_base_loopexit创建事件工作流程 安装一#xff08;源码安装#xff0c;推荐#xff09;现在源码配置编译安装验证安装 安装二#xff08;可能因… 目录 参考什么是libevent应用核心实现libevent的地基event_base等待事件产生循环监听event_loop退出循环监听event_base_loopexit创建事件工作流程 安装一源码安装推荐现在源码配置编译安装验证安装 安装二可能因为openssl报错参考下载安装包配置编译安装测试libevent是否安装成功 samples测试method.c代码编译执行 服务端程序 参考
视频教程 libevent的基本使用
什么是libevent
Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库主要有以下几个亮点事件驱动 event-driven高性能;轻量级专注于网络不如 ACE 那么臃肿庞大源代码相当精炼、易读跨平台支持 Windows、 Linux、 *BSD 和 Mac Os支持多种 I/O 多路复用技术 epoll、 poll、 dev/poll、 select 和 kqueue 等支持 I/O定时器和信号等事件注册事件优先级。 Libevent 已经被广泛的应用作为底层的网络库比如 memcached、 Vomit、 Nylon、 Netchat等等。
应用
Chromium、Memcached、NTP、HTTPSQS等著名的开源程序都使用libevent库足见libevent的稳定。更多使用libevent的程序可以到libevent的官网查看。
核心实现
Reactor反应堆模式是libevent的核心框架libevent以事件驱动自动触发回调功能。之前介绍的epoll反应堆源码就是从libevent中抽取出来的。
libevent的地基event_base
在使用libevent这个库的时候就像我们盖房子一样需要一个地基我们这个接口就是来完成这个工作的在他的基础上会有的一个事件集合去检查哪一个事件是激活的
// 通过以下函数获得event_base结构
struct event_base * event_base_new(void);
// 申请到的指针可以通过event_base_free释放
event_base_free(struct event_base *)// 如果fork出子进程想在子进程继续使用event_base那么子进程需要对event_base重新初始化函数如下
int event_reinit(struct event_base *base);等待事件产生循环监听event_loop
他类似于while(1)的功能去循环监视事件的发生 int event_base_dispatch(struct event_base *base); 调用该函数相当于while(1) {epoll_wait}相当于没有设置标志位的event_base_loop。程序将会一直运行直到没有需要检测的事件了或者被结束循环的api终止。
int event_base_dispatch(struct event_base *base);
调用该函数相当于没有设置标志位的event_base_loop。程序将会一直运行直到
没有需要检测的事件了或者被结束循环的api终止。int event_base_loopexit(struct event_base *base, const struct timeval *tv);
int event_base_loopbreak(struct event_base *base);
struct timeval {long tv_sec; long tv_usec;两个函数的区别是如果正在执行激活事件的回调函数那么event_base_loopexit将在事件回调执行结束后终止循环如果tv时间非NULL那么将等待tv设置的时间后立即结束循环而event_base_loopbreak会立即终止循环。
退出循环监听event_base_loopexit
int event_base_loopexit(struct event_base *base, const struct timeval *tv);
int event_base_loopbreak(struct event_base *base);
struct timeval {long tv_sec; long tv_usec;两个函数的区别是如果正在执行激活事件的回调函数那么event_base_loopexit将在事件回调执行结束后终止循环如果tv时间非NULL那么将等待tv设置的时间后立即结束循环而event_base_loopbreak会立即终止循环。
创建事件
struct event *event_new(struct event_base *base, evutil_socket_t fd,short events, event_callback_fn cb, void *arg);
event_new负责新创建event结构指针同时指定对应的地基base还有对应的文件描述符
事件以及回调函数和回调函数的参数。参数说明
base 对应的根节点
fd 要监听的文件描述符
events 要监听的事件
#define EV_TIMEOUT 0x01 //超时事件
#define EV_READ 0x02 //读事件
#define EV_WRITE 0x04 //写事件
#define EV_SIGNAL 0x08 //信号事件
#define EV_PERSIST 0x10 //周期性触发
#define EV_ET 0x20 //边缘触发如果底层模型支持
cb 回调函数原型如下
typedef void (*event_callback_fn)(evutil_socket_t fd, short events, void *arg);
arg 回调函数的参数
7. int event_add(struct event *ev, const struct timeval *timeout);
将非未决态事件转为未决态相当于调用epoll_ctl函数开始监听事件是否产生。
参数说明Ev 就是前面event_new创建的事件
Timeout 限时等待事件的产生也可以设置为NULL没有限时。
8. int event_del(struct event *ev);
将事件从未决态变为非未决态相当于epoll的下树epoll_ctl调用EPOLL_CTL_DEL操作操作。
9. void event_free(struct event *ev);
释放event_new申请的event节点。
工作流程 安装一源码安装推荐
现在源码
cd /usr/local/clib/libevent/
git clone https://github.com/nmathewson/Libevent.git配置
sh autogen.sh
./configure --prefix/usr/local/clib/libevent/libevent-2.0.12
编译
make -j4
安装
make install
验证安装
make verify //验证安装
安装二可能因为openssl报错
参考
libevent下载与安装学习整理
下载安装包
官网http://www.monkey.org/~provos… 下载libevent-2.1.12-stable.tar.gz 解压 cd /usr/local/clib/libevent
# 下载
wget https://www.monkey.org/~provos/libevent-2.0.12-stable.tar.gz
tar zxvf libevent-2.0.12-stable.tar.gz
# 配置
./configure --prefix/usr/local/clib/libevent/libevent-2.0.12 --with-openssl/usr/local/clib/openssl/1.1.1o编译
make -j4安装 make install测试libevent是否安装成功
# ls -al /usr/lib | grep libeventsamples cd /usr/local/clib/libevent/Libevent/sample测试method.c
打印支持的方法
代码
#includeevent.h
#includestdio.hint main()
{// 获取libevent后端支持的方法const char **methods event_get_supported_methods();for(int i 0;methods[i] ! NULL;i){printf(method:%s\n, methods[i]);}struct event_base* base event_base_new();printf(%s\n, event_base_get_method(base));return 0;
}编译执行 # 编译
gcc -I/usr/local/clib/libevent/libevent-2.0.12/include -L/usr/local/clib/libevent/libevent-2.0.12/lib hello-world.c -levent
# 运行
export LD_LIBRARY_PATH$LD_LIBRARY_PATH:/usr/local/clib/libevent/libevent-2.0.12/lib
./a.out服务端程序
#includeevent.h
#include sys/socket.h // socket依赖
#include arpa/inet.h // socket依赖
#includestdio.h
#include fcntl.h
#include stdlib.h
#include unistd.h // close依赖
#include errno.h
#include string.h#define MAX_LISTEN_SOCKET 1 /*监听上限*/
#define MAX_LISTEN_EVENTS 100 /*监听上限*/
#define SOCKET_PORT 8000 /*端口号*/
#define BUF_SIZE 1024 /*缓存区大小*/
#define READ_BUF_SIZE 4 /*缓存区大小*/typedef struct T_EVENT
{int fd; // 要监听的文件描述符struct event_base * base; // 对应的监听事件EPOLLIN和EPLLOUTstruct event *ev; long last_active; // 记录每次加入红黑树 g_efd 的时间值
}T_EVENT, *PT_EVENT;/*** brief * 客户端事件* param fd * param event * param arg */
void clientFun(int fd, short events, void* arg){// 参数转换PT_EVENT e (PT_EVENT)arg;struct event_base * base e-base;int len -1;// 开始循环度int buf_len 0;char buf[BUF_SIZE] {0};struct event *ev e-ev;// 先下树event_del(ev);int is_close 0;while (1){len recv(fd, bufbuf_len, READ_BUF_SIZE, 0); // 读取客户端发过来的数据if (len 0){buf_len len;printf(C[%d] say: len:%d, content:%s\n, fd, len,buf);}else if (len 0){is_close 1;printf([fd%d] closed\n, fd);break;}else{// 如果是读缓冲区读干净了这个时候应该跳出while循环if (errno EAGAIN){break;}is_close 1;printf(recv[fd%d] error[%d]:%s\n, fd, errno, strerror(errno));break;}}printf(客户端已经循环读完,is_close:%d\n,is_close);if(is_close){close(fd);// 释放free(e);}else{len send(fd, buf, buf_len, 0); // 直接将数据回射给客户端// 再次上树event_add(ev, NULL);}
}void serverFun(int fd, short event, void* arg){// 参数转换struct event_base * base (struct event_base *)arg;struct sockaddr_in cin;socklen_t len sizeof(cin);// 提取新的cfdint cfd -1;if ((cfd accept(fd, (struct sockaddr *)cin, len)) -1){if (errno ! EAGAIN errno ! EINTR){sleep(1);}printf(%s:accept,%s\n, __func__, strerror(errno));return;}printf(new connect[%s:%d]\n, inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));int flag 0;// 设置客户端为非阻塞int flags fcntl(cfd, F_GETFL); // 获取的cfd的标志位flags | O_NONBLOCK;if ((flag fcntl(cfd, F_SETFL, flags)) 0) // 将cfd也设置为非阻塞{printf(%s: fcntl nonblocking failed, %s\n, __func__, strerror(errno));}// 初始化上树节点和事件PT_EVENT e malloc(sizeof(T_EVENT));e-fd cfd;e-base base;struct event *ev event_new(base, cfd, EV_READ | EV_ET | EV_PERSIST, clientFun, e);e-ev ev;// 上树event_add(ev, NULL);
}int main()
{int sockfd 0;int ret -1;sockfd socket(AF_INET, SOCK_STREAM, 0);printf(监听套接字文件描述符%d\n, sockfd);struct sockaddr_in addr;addr.sin_family AF_INET;addr.sin_port htons(SOCKET_PORT);// addr.sin_addr.s_addr inet_addr(127.0.0.1);addr.sin_addr.s_addr htons(INADDR_ANY);// 一般在一个端口释放后需要等一段时间才能重新启用因此需要借助SO_REUSEADDR来使端口重新启用。解决服务端异常退出之后再次启动服务端客户端无法使用同一个端口连接socket的问题// 设置端口复用int opt 1;ret setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt));if (ret -1){perror(设置端口复用失败\n);return -1;}ret bind(sockfd, (struct sockaddr *)addr, sizeof(addr));if (ret -1){perror(绑定失败\n);return -1;}ret listen(sockfd, 5);if (ret -1){perror(监听失败\n);return -1;}// 注册event_base根节点相当于epoll_craetestruct event_base * base event_base_new();// 初始化sockfd上树节点struct event *ev event_new(base, sockfd, EV_READ | EV_ET | EV_PERSIST, serverFun, base);// 上树event_add(ev, NULL);// 循环监听, 阻塞event_base_dispatch(base);// 收尾close(sockfd);event_base_free(base);}