Linux网络编程-7.libevent库

7.1 libevent基本概念

libevent是干什么的

  • 开源的库,提高开发效率
    • 封装了socket通信
    • 封装了IO多路转接
  • 精简,专注于网络,高性能
  • 事件驱动

libevent库的安装

  • 官方网站:http://libevent.org
  • 源码下载:
    • 1.4.x – 适合源码学习
    • 2.x
  • 源码包的安装:
    • ./configure
      • 检测安装环境
      • 生成makefile
    • make
      • 编译源代码
      • 生成库、可执行文件
    • sudo make install
      • 将数据拷贝到系统中
      • 默认目录
        • /usr/local
          /usr/local/include
          /usr/local/bin
          /usr/local/lib

libevent库的使用

  • 编译程序的时候指定-levent即可
  • 常用头文件:
    • #include <event2/event.h>
    • #include <event2/listener.h>

动态库和找不到:
1.找到xxx.so放到/usr/lib /lib – 不推荐

  • sudo find /usr/local -name "libevent.so"

2.将xxx.so放到环境变量中

  • LD_LIBRARY_PATH
    • export LD_LIBARY_PATH=xxxx
  • 重新加载资源
    .~/.bashrc
    ./etc/profile

7.2 使用流程

  1. 创建一个事件处理框架
  2. 创建一个事件
  3. 事件添加到事件处理框架上
  4. 开始事件循环
  5. 释放资源给

7.3 事件处理框架event_base

使用 libevent 函数之前需要分配一个或者多个event_base结构体。每个event_base结构体持有一个事件集合,可以检测以确定哪个事件是激活的。

struct eventop {
	const char *name;
	void *(*init)(struct event_base *); // 初始化
	int (*add)(void *, struct event *); // 注册事件
	int (*del)(void *, struct event *); // 删除事件
	......
};
  • 创建event_base
    • struct event_base* event_base_new(void);
    • 失败返回NULL
  • 释放event_base
    • event_base_free(struct event_base* base);
  • 循环监听base对应的事件, 等待条件满足
    • event_base_dispatch();
  • 查看event_base封装的后端
    • const char **event_get_supported_methods(void);
    • const char *event_base_get_method(const structevent_base *base);
  • event_base和fork
    • 子进程创建成功之后, 父进程可以继续使用event_base
    • 子进程中需要继续使用event_base需要重新进程初始化
      • int event_reinit(struct event_base* base);

7.4 事件创建 - event

7.4.1 创建新事件

#define EV_TIMEOUT	0x01 // 废弃
#define EV_READ		0x02
#define EV_WRITE	0x04
#define EV_SIGNAL	0x08
#define EV_PERSIST	0x10 // 持续触发
#define EV_ET		0x20 // 边沿模式

typedef void (*event_callback_fn)(evutil_socket_t, short, void *);

/* 
 * 创建事件 - 没有缓冲区
 */
struct event *event_new(
	struct event_base	*base,
	evutil_socket_t		fd,		// 文件描述符 - int
	short				what,
	event_callback_fn	cb,		// 事件的处理动作
	void				*arg	// 回调函数的参数
);
  • 调用event_new()函数之后, 新事件处于已初始化和非未决状态

7.4.2 释放事件

void event_free(struct event *event);

7.4.3 设置未决事件

构造事件之后,在将其添加到 event_base 之前实际上是不能对其做任何操作的。使用event_add()将事件添加到event_base, 非未决事件 -> 未决事件.

/*
 * 函数功能:
 * 		将事件添加到event_base中
 * 参数:
 * 	tv:
 * 		NULL: 事件被触发, 对应的回调被调用
 * 	 	tv = {0, 100}, 如果设置时间,在改时间段内检测的事件没被触发, 时间到达之后, 回调函数还是会被调用
 * 返回值:
 * 	成功:0
 * 	失败:-1
 */
int event_add(
	struct event			*ev,
	const struct timeval	*tv
);

7.4.4 设置非未决

/*
 * 对已经初始化的事件调用 event_del()
 * 将使其成为非未决和非激活的。
 * 如果事件不是未决的或者激活的,调用将没有效果。
 * 成功时函数返回 0,失败时返回-1。
 */
int event_del(struct event *ev);

7.5 事件循环

一旦有了一个已经注册了某些事件的 event_base, 就需要让libevent 等待事件并且通知事件的发生。

/*
 * 事件只会被触发一次
 * 事件没有被触发, 阻塞等待
 */
#define EVLOOP_ONCE				0x01

/*
 * 非阻塞 等方式去做事件检测
 * 不关心事件是否被触发了
 */
#define EVLOOP_NONBLOCK			0x02

/*
 * 没有事件的时候, 也不退出轮询检测
 */
#define EVLOOP_NO_EXIT_ON_EMPTY	0x04

/*
 * 正常退出返回0, 失败返回-1
 */
int event_base_loop(struct event_base *base, int flags);

/*
 * 等同于没有设置标志的 event_base_loop ( )
 * 将一直运行,直到没有已经注册的事件了,或者调用了
 * event_base_loopbreak()或者 event_base_loopexit()为止。
 */
int event_base_dispatch(struct event_base* 
base);

/*
 * 函数功能:
 * 	循环停止,如果 event_base 当前正在执行激活事件的回调 ,它将在执行完当前正在处理的事件后立即退出
 * 参数:
 * 	base:
 * 返回值:
 * 	成功:0
 * 	失败:-1
 */
int event_base_loopexit(struct event_base *base, const struct timeval *tv);

/*
 * 让event_base 立即退出循环
 */
int event_base_loopbreak(struct event_base *base);

struct timeval {
	long tv_sec;
	long tv_usec;
};

示例:使用event读管道

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <event2/event.h>

void read_cb(evutil_socket_t fd, short what,void *arg)
{
	char buf[1024] = { 0 };
	int len = read(fd, buf, sizeof(buf));
	printf("data:%s\n", buf);
}

int main(int argc, const char *argv[])
{
	// 创建有名管道
	unlink("myfifo");
	mkfifo("myfifo", 0664);
	
	int fd = open("myfifo", O_RDONLY | O_NONBLOCK);
	
	// 创建事件处理框架
	struct event_base *base = event_base_new();
	// 创建事件
	struct event *ev = event_new(base, fd, EV_READ | EV_PERSIST, read_cb, NULL);
	// 添加事件
	event_add(ev, NULL);
	// 开始事件循环
	event_base_dispatch(base);
	// 释放资源
	event_free(ev);
	event_base_free(base);
	close(fd);
	return 0;
}

示例:使用event写管道

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <event2/event.h>

void write_cb(evutil_socket_t fd, short what,void *arg)
{
	char buf[1024] = { 0 };
	static int num = 0;
	sprintf(buf, "num:%d", num++);
	int len = write(fd, buf, strlen(buf)+1);
	printf("data:%s\n", buf);
}

int main(int argc, const char *argv[])
{
	int fd = open("myfifo", O_WRONLY | O_NONBLOCK);
	
	// 创建事件处理框架
	struct event_base *base = event_base_new();
	// 创建事件 检测的是写缓冲区是否有空间写
	struct event *ev = event_new(base, fd, EV_WRITE | EV_PERSIST, write_cb, NULL);
	// 添加事件
	event_add(ev, NULL);
	// 开始事件循环
	event_base_dispatch(base);
	// 释放资源
	event_free(ev);
	event_base_free(base);
	close(fd);
	return 0;
}

7.6 使用bufferevent

7.6.1 创建使用基于套接字的bufferevent

使用bufferevent_socket_new()创建基于套接字的bufferevent

/*
 * 函数功能:
 * 	创建基于套接字的bufferevent
 * 参数:
 * 	base:event_base
 * 	fd:文件描述符
 * 	options:常用BEV_OPT_CLOSE_ON_FREE
 * 返回值:
 * 	成功:返回一个bufferevent
 * 	失败:NULL
 */
struct bufferevent *bufferevent_socket_new(
	struct event_base			*base,
	evutil_socket_t				fd,
	enum bufferevent_options	options
);

7.6.2 在bufferevent上启动链接

/*
 * 函数功能:
 * 	在bufferevent上启动链接
 * 参数:
 * 	bev:
 * 	address:server端的IP和端口
 * 	addrlen:address大小
 * 返回值:
 * 	成功:
 * 	失败:
 */
int bufferevent_socket_connect(
	struct bufferevent	*bev,
	struct sockaddr		*address,
	int					addrlen
);

7.6.3 释放bufferevent操作

/*
 * 函数功能:
 * 	释放 bufferevent
 * 参数:
 * 	bev:
 */
 void bufferevent_free(struct bufferevent *bev);

7.6.4 bufferevent读写缓冲区回调操作

typedef void (*bufferevent_data_cb)(
	struct bufferevent	*bev,
	void				*ctx
);
typedef void (*bufferevent_event_cb)(
	struct bufferevent	*bev,
	short				events,
	void				*ctx
);

/*
 * 函数功能:
 * 	给读写缓冲区设置回调函数
 * 参数:
 * 	bufev:创建的bufferevent
 * 	readcb:读缓冲区的回调函数
 * 	writecb:写缓冲区的回调函数
 * 	eventcb:事件(异常、断开链接等)回调函数
 * 	cbarg:回调函数参数
 * 返回值:
 * 	成功:
 * 	失败:
 */
void bufferevent_setcb(
	struct bufferevent		*bufev,
	bufferevent_data_cb		readcb,
	bufferevent_data_cb		writecb,
	bufferevent_event_cb	eventcb,
	void					*cbarg
);
events参数说明
EV_EVENT_READING读取操作时发生某事件,具体是哪种事件请看其他标志
BEV_EVENT_WRITING写入操作时发生某事件,具体是哪种事件请看其他标志
BEV_EVENT_ERROR操作时发生错误。关于错误的更多信息,请调用EVUTIL_SOCKET_ERROR()
BEV_EVENT_TIMEOUT发生超时
BEV_EVENT_EOF遇到文件结束指示
BEV_EVENT_CONNECTED请求的连接过程已经完成

7.6.5 启用、禁用缓冲区

/*
 * 可以启用或者禁用 bufferevent 上的 EV_READ、EV_WRITE
 * 或者 EV_READ | EV_WRITE 事件。
 * 没有启用读取或者写入事件时,
 * bufferevent 将不会试图进行数据读取或者写入。
 * 禁用之后对应的回调就不会被调用。
 */
void bufferevent_enable(
	struct bufferevent *bufev,
	short 				events
);
void bufferevent_disable(
	struct bufferevent *bufev,
	short 				events
);
short bufferevent_get_enabled(
	struct bufferevent *bufev
);
/*
 * 默认EV_WRITE是enable
 * EV_READ是关闭的
 */

7.6.7 操作bufferevent中的数据

/* 向bufferevent的输出缓冲区添加数据 */
int bufferevent_write(
	struct bufferevent	*bufev,
	const void			*data,
	size_t				size
);

/* 从bufferevent的输入缓冲区移除数据 */
size_t bufferevent_read(
	struct bufferevent	*bufev,
	void				*data,
	size_t				size
);

7.7 链接监听

7.7.1 创建和释放evconnlistener

typedef void (*evconnlistener_cb)(
	struct evconnlistener *listener, //evconnlistener_new_bind的返回值
	evutil_socket_t sock, // 用于通信的文件描述符
	struct sockaddr *addr, // client地址结构
	int len, // addr大小
	void *ptr // 外部参数
);

struct evconnlistener * evconnlistener_new(
	struct event_base *base,
	evconnlistener_cb cb,
	void *ptr,
	unsigned flags,
	int backlog,
	evutil_socket_t fd
);
/*
 * 函数功能:
 * 	创建监听socket,绑定,监听,等待并接受连接
 * 参数:
 * 	base:事件处理框架
 * 	cb:回调函数,接受连接之后,用户要做什么
 * 	ptr:回调函数的参数
 * 	flags:常用
 * 		LEV_OPT_CLOSE_ON_FREE:
 * 			如果设置了这个选项,释放连接监听器会关闭底层套接字
 * 		LEV_OPT_REUSEABLE:
 * 			某些平台在默认情况下,关闭某监听套接字后,
 * 			要过一会儿其他套接字才可以绑定到同一个端
 * 			口。设置这个标志会让 libevent 标记套接
 * 			字是可重用的,这样一旦关闭,可以立即打开其
 * 			他套接字,在相同端口进行监听。
 * 	backlog:-1:使用默认的最大值
 * 	sa:服务器的IPheport信息
 * 	socklen:sa的大小
 * 返回值:
 * 	成功:
 * 	失败:
 */
struct evconnlistener *evconnlistener_new_bind(
	struct event_base *base,
	evconnlistener_cb cb,
	void *ptr,
	unsigned flags,
	int backlog,
	const struct sockaddr *sa,
	int socklen
);
/*
 * 两个 evconnlistener_new*()函数都分配和返
 * 回一个新的连接监听器对象。连接监听器使
 * 用 event_base 来得知什么时候在给定的监听
 * 套接字上有新的 TCP 连接。
 * 新连接到达时,监听 器调用你给出的回调函数
 * 参数 flags :参考手册-page99-100(可识别的标志)
 */
void evconnlistener_free(struct evconnlistener *lev);

7.7.2 启用和禁用evconnlistener

int evconnlistener_disable(struct evconnlistener *lev);
int evconnlistener_enable(struct evconnlistener *lev);
/* 这两个函数暂时禁止或者重新允许监听新连接。 */

7.7.3 调整evconnlistener的回调函数

void evconnlistener_set_cb(
	struct evconnlistener *lev,
	evconnlistener_cb cb,
	void *arg
);
/* 函数调整 evconnlistener 的回调函数和其参数 */

示例:

// server.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/listener.h>

void read_cb(struct bufferevent* bev, void *arg)
{
	// read data from buffer
	char buf[1024] = { 0 };
	int len = bufferevent_read(bev, buf, sizeof(buf));
	printf("recv buf:%s\n", buf);
	char *pt = "recived";
	bufferevent_write(bev, pt, strlen(pt)+1);
}

void write_cb(struct bufferevent* bev, void *arg)
{
	printf("recived successed\n");
}

void event_cb(struct bufferevent* bev,short events, void *arg)
{
	if(events & BEV_EVENT_EOF)
	{
		printf("connection closed\n");
	}
	else if(events & BEV_EVENT_ERROR)
	{
		printf("some other error\n");
	}
	// 释放资源
	bufferevent_free(bev);
}

// 连接完成之后对应的通信操作
void listen_cb(
	struct evconnlistener *listener, 
	evutil_socket_t fd, 
	struct sockaddr *addr, 
	int len, 
	void *ptr)
{
	// 将fd封装为带缓冲区的事件
	struct event_base* base = (struct event_base*)ptr;
	struct bufferevent *bev = NULL;
	bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
	// 给bufferevent设置回调函数
	bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
	// 设置读缓冲区的回调函数可用
	bufferevent_enable(bev, EV_READ);
}

int main(int argc, const char *argv[])
{
	// 创建事件处理框架
	struct event_base *base = event_base_new();
	// init server info
	struct sockaddr_in serv;
	memset(&serv, 0x00, sizeof(serv));
	serv.sin_family = AF_INET;
	serv.sin_port = htons(9876);
	serv.sin_addr.s_addr = htonl(INADDR_ANY);
	// 创建监听套接字、绑定、监听、等待并接受连接请求
	struct evconnlistener* listen = NULL;
	listen = evconnlistener_new_bind(
				base, 
				listen_cb, 	// 有新连接是该函数被调用
				base, 
				LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
				-1,
				(struct sockaddr*)&serv,
				sizeof(serv)
	);
	// 开始事件循环
	event_base_dispatch(base);
	// 释放资源
	evconnlistener_free(listen);
	event_base_free(base);
	return 0;
}
// client.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <arpa/inet.h>

void read_cb(struct bufferevent* bev, void *arg)
{
	// read data from buffer
	char buf[1024] = { 0 };
	int len = bufferevent_read(bev, buf, sizeof(buf));
	printf("serv send:%s\n", buf);
	char *pt = "recived";
}

void write_cb(struct bufferevent* bev, void *arg)
{
	printf("send successed\n");
}

void event_cb(struct bufferevent* bev,short events, void *arg)
{
	if(events & BEV_EVENT_EOF)
	{
		printf("connection closed\n");
	}
	else if(events & BEV_EVENT_ERROR)
	{
		printf("some other error\n");
	}
	else if(events & BEV_EVENT_CONNECTED)
	{
		printf("connected succussed\n");
		return;
	}
	// 释放资源
	bufferevent_free(bev);
}

// 终端接收数据并将数据发送给server
void read_terminal_cb(int fd, short what, void *arg)
{
	char buf[1024] = { 0 };
	int len = read(fd, buf, sizeof(buf));
	
	struct bufferevent* bev = (struct bufferevent*)arg;
	bufferevent_write(bev, buf, len+1);
}

int main(int argc, const char *argv[])
{
	// 创建事件处理框架
	struct event_base *base = event_base_new();
	// 创建事件
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
	// 连接服务器
	struct sockaddr_in serv;
	serv.sin_family = AF_INET;
	serv.sin_port = htons(9876);
	inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
	bufferevent_socket_connect(bev, (struct sockaddr*)&serv, sizeof(serv));
	// 给缓冲区设置回调函数
	bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
	bufferevent_enable(bev, EV_READ);
	// 接收键盘输入
	// 创建事件
	struct event* ev = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, read_terminal_cb, bev);
	// 添加事件
	event_add(ev, NULL);
	// 开始事件循环
	event_base_dispatch(base);
	// 释放资源
	bufferevent_free(bev);
	event_base_free(base);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT灰猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值