【智能音箱项目】-libvent库

1.事件和集合

      libvent的核心是个集合,把所有的需要监听的事件放在集合中,如果有满足条件的事件,就会触发回调函数去处理!

画个图,先搞清楚TCP的C/S架构

图中有三个文件描述符,sockfd和fd1、fd2

三个文件描述符可以分为两类:

socfd用于监听是否有客户端发起连接请求,fd1和fd2用于监听客户端是否向服务器发送数据。

每一个文件描述符可以理解成一个事件。事实上,libevent就是把文件描述符封装成了结构体bufferevent。把三个bufferevent同时放入集合中监听,就成了高并发服务器的雏形!

2.使用过程

(1)创建集合


struct event_base *event_base_new();

这行代码非常简单,创建一个空的集合。没有集合,libevent啥也不是。

用完之后,需要手动释放。


void event_base_free(struct event_base *);

(2)创建监听对象


struct evconnlistener *evconnlistener_new_bind(struct event_base *base,
     evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
     const struct sockaddr *sa, int socklen);

/*智能音箱原始代码*/
PlayerServer::PlayerServer(const char *ip, int port)
{
	base = event_base_new();               //创建事件集合

	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	server_addr.sin_addr.s_addr = inet_addr(ip);

	listener = evconnlistener_new_bind(base, listener_cb, base, 
		LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 10, (struct sockaddr *)&server_addr, sizeof(server_addr));
	if (NULL == listener)
	{
		std::cout << "evconnlistener_new_bind error" << std::endl;
	}

	event_base_dispatch(base);      //监听集合
}

可以理解成,这一函数完成了socket、bind、listen、accept四个函数功能,并且把得到的sockfd(监听对象)添加到集合中。

参数的意义:

base:刚才创建的集合。

cb:回调函数。一旦有客户端连接服务器,就会触发该回调函数。

ptr:给回调函数传的参数。

flags:LEV_OPT_开头的宏定义,指定了监听对象的属性,比如释放监听对象的时候关闭socket连接、地址可以复用、多线程安全等等。

backlog:监听队列的大小。

sa:存放服务器的信息,用于绑定使用。

socklen:结构体的长度。

其中回调函数原型如下:


void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *arg)

/*智能音箱回调函数原始代码*/
void PlayerServer::listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *arg)
{
	struct event_base *base = (struct event_base *)arg;
	std::cout << "有客户端连接 " << fd << std::endl;

	//创建bufferevent事件
	struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
	if (NULL == bev)
	{
		std::cout << "bufferevent_socket_new error" << std::endl;
	}

	bufferevent_setcb(bev, read_cb, NULL, event_cb, base);
	bufferevent_enable(bev, EV_READ);
}

回调函数里面主要用于创建bufferevent对象,可以理解为一个bufferevent对应一个客户端。代码如下:

void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *arg)
{
    struct event_base *base = arg;
    
    struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    if (NULL == bev)
    {   
        printf("bufferevent_socket_new error!\n");
        exit(1);
    }   

    bufferevent_setcb(bev, read_cb, NULL, event_cb, NULL);
    bufferevent_enable(bev, EV_READ);
}

(3)开始监听

int event_base_dispatch(struct event_base *);

dispatch函数可以理解成一个循环,负责监听集合中的事件。如果没有这个函数,主函数会立即返回。

(4)设置回调函数

libevent里面用到的回调函数的地方特别多。

有客户连接请求,会触发回调函数;客户端发送数据会触发回调函数;客户端有异常时会触发回调函数。

对bufferevent事件设置回调函数需要用到两个函数:

//设置回调函数
void bufferevent_setcb(struct bufferevent *bufev, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb eventcb, void *cbarg);
//使能回调函数
int bufferevent_enable(struct bufferevent *bufev, short event);

其中,readcb最常使用,一旦有客户端发送数据,就会触发readcb回调函数。在智能音箱项目中,我们使用了两个回调函数,分别是服务器收到客户端消息时的回调函数readcb、客户端异常时回调函数event_cb;

(5)读取数据

libevent封装了两个函数分别用于读取和发送。

size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值