【Linux】Libevent库---bufferevent

服务器端框架


1. 创建event_base_new(); 

2. 使用 evconnlistener_new_bind 创建监听服务器,设置其回调函数listner_cb(),当有客户端成功连接时回调函数被调用。

3. 封装 listner_cb() ,在函数内部,完成: 

4. 创建bufferevent事件对象,bufferevent_socket_new();

5. 使用bufferevent_setcb() 函数给bufferevent的 read、write、event 设置回调函数。 

6. 设置读缓冲、写缓冲的使能状态

7. 启动循环 event_base_dispath(); 

8. 当监听的事件满足时,read_cb会被调用。

9. 释放连接 evconnlistener_free(listener); event_base_free(base);

// server.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<event2/event.h>
#include<event2/listener.h>
#include<event2/bufferevent.h>
#include<sys/socket.h>


void read_cb(struct bufferevent *bev, void *arg){

	// 每次读之前,给缓冲区清0
	char buf[1024] = {0};
	int read_size = 0;

	// 从 bev 绑定的 cfd 中读数据 
	read_size = bufferevent_read(bev, buf, sizeof(buf));	// read(cfd, buf, sizeof(buf));没有文件描述符cfd,不能用read(),只能用包含文件描述符cfd 的结构体对象bev。


	// 向屏幕打印客户端发来的数据
	printf("client say: %s\n", buf);
	write(STDOUT_FILENO, buf, read_size);


    // // 发数据给客户端
	// char* msg = "我是服务器,我已经收到了你发送的数据!";
	// bufferevent_write(bev, msg, strlen(msg)+1);		// 写缓冲一有数据,就会自动把数据刷给对端; 然后调用 write_cb 回调函数。
	// sleep(2);
}



// 写缓冲区回调
void write_cb(struct bufferevent *bev, void *arg)
{
    printf("I'm服务器, 成功写数据给客户端,写缓冲区回调函数被回调...\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);    
    printf("buffevent 资源已经被释放...\n");
}



// 普通事件的回调函数
void send_cb(int fd, short events, void *arg){
	// fd = STDIN_FILENO
	// events = EV_READ | EV_PERSIST;
	struct bufferevent* bev = (struct bufferevent*)arg;


	// 从键盘读之前,清空缓冲区
	char buf[1024] = {0}; 
    read(fd, buf, sizeof(buf));


	// 将从键盘读到的数据,传给对端
    bufferevent_write(bev, buf, strlen(buf)+1);

}



// 一旦被回调,说明在其内部应该与客户端建立了连接, 并且已经有cfd,可以直接用。
void listener_cb(                
		struct evconnlistener *listener, 
		evutil_socket_t cfd,				// 与客户端通信的socket的文件描述符cfd。
		struct sockaddr *addr, int len,		// 客户端的地址结构
		void *ptr)
{
   	printf("connect new client\n");

	struct event_base * base = (struct event_base *)ptr;

	// 创建套接字bufferevent
	struct bufferevent * bev = NULL;
	bev = bufferevent_socket_new(base, cfd, BEV_OPT_CLOSE_ON_FREE);	// 把 cfd 封装到 struct bufferevent *bev 中。


	// 将默认关闭的读事件开启
	bufferevent_enable(bev, EV_READ);
	

	// 对bufferevent读写缓冲区 设置回调函数	
	bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL); // NULL 是传给三个回调的参数。



	// 创建一个普通事件
	struct event * ev = NULL;
	ev = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, 	// 从键盘持续 读,
					send_cb, bev);				// 一旦从键盘读到数据,就触发 ev 的 send_cb 回调函数。

	event_add(ev, NULL);

}



int main(int argc, char* argv[])
{
	// 服务器的地址结构
	struct sockaddr_in serv_addr;

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(9999);
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);


	// 创建底座
	struct event_base *base = NULL;
	base = event_base_new();


	// 创建事件连接监听器
	struct evconnlistener *listener;
	// 创建套接字 --- lfd
    // 绑定服务器的地址结构 ---serv,   bind(lfd, serv, sizeof(serv))
    // 设置监听上限 --- 36,          listen(lfd, 36)
    // 接收连接请求,等着客户端建立连接请求。一旦与客户端成功建立了连接,就走 cb_listener 回调函数。 accept(lfd, ...)
	listener = evconnlistener_new_bind(
				base, 
				listener_cb, base, 	    // 回调函数与其参数
				LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 		// 释放连接监听器会关闭底层套接字,设置端口复用
				5, 
				(struct sockaddr *)&serv_addr, sizeof(serv_addr)
				);
	

	// 循环监听事件
	event_base_dispatch(base);

	evconnlistener_free(listener);
	event_base_free(base);


	printf("server over!\n");
	return 0;
}

不能先创建bufferevent,因为需要封装进去的是cfd,而不是lfd。

lfd创建的过程应该是直接封装在evconnlister_new_bind函数中的,外部不可见,因为根本不需要我们操作lfd。

创建buffrtevent事件对象时,需要有一个客户端对应的文件描述符,而这个文件描述符只有在先监听了后才会有。

// client.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<event2/event.h>
#include<event2/listener.h>
#include<event2/bufferevent.h>
#include<sys/socket.h>
#include<arpa/inet.h>


void read_cb(struct bufferevent *bev, void *arg){

	// 每次读之前,给缓冲区清0
	char buf[1024] = {0};

	// 从 bev 绑定的 cfd 中读数据 
	bufferevent_read(bev, buf, sizeof(buf));	// read(cfd, buf, sizeof(buf));没有文件描述符cfd,不能用read(),只能用包含文件描述符cfd 的结构体对象bev。


	// 向屏幕打印客户端发来的数据
	printf("server say: %s\n", buf);
}


// 写缓冲区回调
void write_cb(struct bufferevent *bev, void *arg)
{
    printf("I'm服务器, 成功写数据给客户端,写缓冲区回调函数被回调...\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("已经连接服务器...\\(^o^)/...\n");
        return;
    }
    
    // 释放资源
    bufferevent_free(bev);
}


// 普通事件的回调函数,客户端与用户交互,从终端读取数据写给服务器
void read_terminal(int fd, short events, void *arg){
	// fd = STDIN_FILENO
	// events = EV_READ | EV_PERSIST;
	struct bufferevent* bev = (struct bufferevent*)arg;


	// 从键盘读之前,清空缓冲区
	char buf[1024] = {0}; 
    read(fd, buf, sizeof(buf));


	// 将从键盘读到的数据,传给对端
    bufferevent_write(bev, buf, strlen(buf)+1);
}


int main(int argc, char* argv[])
{
	// 创建底座
	struct event_base *base = NULL;
	base = event_base_new();


	// 创建套接字
	int cfd = socket(AF_INET, SOCK_STREAM, 0);


	// 创建套接字bufferevent
	struct bufferevent * bev;
	bev = bufferevent_socket_new(base, cfd, BEV_OPT_CLOSE_ON_FREE);		// BEV_OPT_CLOSE_ON_FREE 释放 bufferevent 时关闭底层传输端口


	// 服务器的地址结构
	struct sockaddr_in serv_addr;

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(9999);
	inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr);


	// 连接服务器
	bufferevent_socket_connect(bev, (struct sockaddr *)&serv_addr, sizeof(serv_addr));


	// 给bev设置回调函数
	bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
	bufferevent_enable(bev, EV_READ | EV_PERSIST);


	// 创建一个普通事件, 客户端与用户交互,从终端读取数据写给服务器
	struct event * ev = NULL;
	ev = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, 	// 从键盘持续 读,
					read_terminal, bev);				// 一旦从键盘读到数据,就触发 ev 的 send_cb 回调函数。

	event_add(ev, NULL);


	// 循环监听事件
    event_base_dispatch(base);
	
    event_free(ev);
    event_base_free(base);


	printf("client over!\n");
	return 0;
}

客户端发送 "asas",

服务器回:

client say: asas

asas

然后,服务器发送 “aaaaaaaaaaaaaaaaaaaaaa”

客户端接收:“
server say: aaaaaaaaaaaaaaaaaaaaaa

正常结果图:

错误写法,如果,将客户端的下列代码注释掉:

    else if(events & BEV_EVENT_CONNECTED)
    {
        printf("已经连接服务器...\\(^o^)/...\n");
        return;
    }

那么,客户端刚连上服务器,就断开连接。

错误结果图:

参考链接:

详解libevent网络库(二)---即时聊天通讯

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值