Linux网络编程day9 libevent库
libevent库
开源。精简。跨平台(Windows,Linux,maxos)。专注于网络通信。
源码包安装:参考README
1、./configure 检查安装环境 生成makefile
2、make 生成.o和执行文件
3、sudo make install 将必要的资源拷贝至系统指定目录
4、进入sample目录进行验证,运行demo验证库安装使用情况
5、编译使用库的.c时,需要加 -levent
/usr/local/lib中可以查看到
特性
基于“事件”异步通信模型。----回调机制。
libevent框架
1、创建event_base(底座)
struct event_base *event_base_new(void);struct event_base *base = event_base_new();
2、创建事件event
常规事件event
struct event *ev;
struct event* event_new(struct event_base* base , evutil_socket_t fd ,
short what , event_callback_fn cb , void*arg);base:event_base_new()返回值
fd:绑定到event上的文件描述符
what:对应监听的事件(读写异常) EV_READ(一次读) EV_WRITE(一次写) EV_PERSIST(持续触发,结合循环生效使用)
cb:一旦事件满足监听条件,回调函数typedef void(*event_callback_fn)(evutil_socket_t fd , short , void*)
arg:回调函数参数
返回值:成功创建的事件
event_new();
bufferevent
bufferevent_socket_new();
3、将事件添加到base上
int event_add(struct event *ev , const struct timeval *tv);ev:event_new()函数返回值
tv:NULL不超时 回调函数一定被调用非0,等待时间返回值成功0失败-1
从base上摘下事件(了解)
int event_del(struct event *ev)
4、循环监听事件满足
int event_base_dispatch(struct event_base *base);
5、释放event_base
int event_base_free(base);
6、不常用一些函数
const char ** event_get_supported_methods(); // 查看支持的多路IO方法char *buf[10];buf = event_get_supported_methods();char buf[1024];
buf = event_base_get_method(base); // 查看目前的多路IO方法
事件的未决和非未决
未决:有资格被处理,但是还没有被处理
非未决:没有资格被处理
解释:事件被创建出来时是处于非未决状态,因为现在没有在base上,所以没有资格被处理,当调用event_add函数的时候,事件被插在base上,有资格被处理,故为未决态,当对应事件触发并且进入循环,事件会变为激活态,等回调函数被调用,事件被处理之后,事件又重新变为非未决态,如果设置了EV_PERSIST,事件会持续触发,变为未决态。
带缓冲区的事件bufferevent
#include<event2/bufferevent.h>
bufferevent有两个缓冲区:也是队列实现的,只能读一次,先进先出(FIFO)。
创建销毁
struct bufferevent *ev
struct bufferevent *bufferevent_socket_new(struct event_base*base , evutil_socket_t fd , enum bufferevent_options options);
base:event_base
fd:封装到bufferevent内的fd
options:BEV_OPT_CLOSE_ON_FREE 返回:成功创建的bufferevent对象
void bufferevent_socket_free(struct bufferevent *ev)
给读写缓冲区设置回调
对比event:event_new(base , fd , cb , void*arg); event_add----挂到base
bufferevent_socket_new(fd) bufferevent_setcb(call_back);
void bufferevent_setcb(struct bufferevent* bufev , bufferevent_data_cb readcb,bufferevent_data_cb writecb,bufferevent_event_cb eventcb,void*cbarg);
bufev:bufferevent_socket_new()函数返回值readcb:设置bufferevent读缓冲,对应回调read_cb(){ bufferevent_read()读数据 }writecb:设置bufferevent写缓冲,对应回调write_cb(){ } --给调用者发送写成功通知可以直接传NULLeventcb:设置事件回调,也可以传NULLcbarg:上述回调函数的参数read回调函数
typedef void(*bufferevent_data_cb)(struct bufferevent* bufev , void*ctx);
eg. void read_cb(struct bufferevent* bufev , void*arg){bufferevent_read() //读数据}size_t bufferevent_read(struct bufferevent* bufev , void*data , size_t size);
在read_cb中代替read函数!!!
event回调函数
typedef void(*bufferevent_data_cb)(struct bufferevent* bufev , shot events , void*ctx);
void event_cb(struct bufferevent* bufev , shot events , void*arg)
{.......
}
events:BEV_EVENT_CONNECTED;
错误事件回调函数void event_cb(struct bufferevent *bev, short events, void *ctx) {if (events & BEV_EVENT_CONNECTED) {printf("✅ Connected to server.\n");} else if (events & BEV_EVENT_ERROR) {perror("❌ Error from bufferevent");} else if (events & BEV_EVENT_EOF) {printf("⚠️ Connection closed by server.\n");}
}
禁用、启用bufferevent缓冲区
默认新建的bufferevent写缓冲是enable,而读缓冲是disable。
void bufferevent_enable(struct bufferevent* bufev , short events);events:EV_READ , EV_WRITE , EV_READ | EV_WRITE.void bufferevent_disable(struct bufferevent* bufev , short events);
网络通信
连接客户端
socket()、connect()
int bufferevent_socket_connect(struct bufferevent* bev , struct sockaddr*address , int addrlen);
服务器创建监听器服务器
socket()、bind()、listen()、accept()
下面这一个函数代替了上述四个函数的作用。
#include<event2/listener.h>struct evconnlistener* listener;struct evconnlistener* evconnlistener_new_bind(struct event_base*base , evconnlistener_cb cb, void* ptr , unsigned flags , int backlog , const struct sockaddr*sa , int socklen)flags:可识别的标志 |LEV_OPT_CLOSE_ON_FREE: 释放bufferevent时关闭底层传输端口(关闭套接字、释放底层)LEV_OPT_REUSEABLE : 端口复用backlog:-1默认使用最大值 listen参2返回值:成功创建的监听器。回调函数:一旦被回调,说明在其内部应该与客户端建立完成,数据读写操作,进行通信
typedef void(*evconnlistener_cb)(struct evconnlistener* listener , evutil_socket_t sock , struct sockaddr*addr , int len , void* ptr);sock:用于通信的文件描述符
addr:客户端的地址结构
回调函数调用成功表示:一旦被回调,说明在其内部应该与客户端建立完成,数据读写操作,进行通信。
服务器端libevent创建TCP连接
1.创建event_base
2.创建一个bufferevent事件对象 bufferevent_socket_new();
3.使用bufferevent_setcb()给bufferevent的read,write,event设置回调函数
4.当监听的事件满足时,read_cb会被调用,在其内部bufferevent_read()读操作
5.使用evconnlistener_new_bind创建一个监听服务器,设置其回调函数,当客户端成功连接时,回调函数会被调用
6.封装listener_cb回调函数。在函数内部完成和客户端通信
7.设置读缓冲和写缓冲的使能状态
8.启动循环监听event_base_dispatch();
9.释放连接
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<event2/bufferevent.h>
#include<ctype.h>
#include<event2/listener.h>
#define SER_PORT 9004void read_cb(struct bufferevent* bufev , void*arg)
{char buf[BUFSIZ];int n , i;n = bufferevent_read(bufev , buf , sizeof(buf));for(i = 0 ; i < n ; i++)buf[i] = toupper(buf[i]);bufferevent_write(bufev , buf , n);return;
}void write_cb(struct bufferevent* bufev , void*arg)
{printf("recived form client\n");return;
}void event_cb(struct bufferevent *bev, short events, void *ctx) {if (events & BEV_EVENT_CONNECTED) {printf("✅ Connected to server.\n");} else if (events & BEV_EVENT_ERROR) {perror("❌ Error from bufferevent");} else if (events & BEV_EVENT_EOF) {printf("⚠️ Connection closed by server.\n");}
}void listen_cb(struct evconnlistener* listener ,evutil_socket_t sock , struct sockaddr*addr , int len , void* ptr)
{struct event_base*base = (struct event_base*)ptr;struct bufferevent*bev = bufferevent_socket_new(base , sock , BEV_OPT_CLOSE_ON_FREE);bufferevent_setcb(bev , read_cb , write_cb , event_cb , NULL);bufferevent_enable(bev , EV_READ);//默认读缓冲是不可的,所以要设置成enablereturn;
}int main(int argc , char *argv[])
{struct event_base*base = event_base_new();struct sockaddr_in serv_addr;bzero(&serv_addr , sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SER_PORT);serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);struct evconnlistener*listener = evconnlistener_new_bind(base , listen_cb , base ,LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE , -1 , (struct sockaddr*)&serv_addr,sizeof(serv_addr));event_base_dispatch(base);evconnlistener_free(listener);event_base_free(base);return 0 ;
}
实现TCP客户端流程
1.创建event_base
2.创建一个bufferevent事件对象 bufferevent_socket_new();
3.使用bufferevent_setcb()给bufferevent的read,write,event设置回调函数
4.当监听的事件满足时,read_cb会被调用,在其内部bufferevent_read()读操作
6.使用bufferevent_socket_connect回调函数。建立连接
7.可以添加一个事件,用来用用户和终端交互,监听终端的写事件,就可以不用使用dup2
8.将监听事件挂在base上 event_add
8.启动循环监听event_base_dispatch();
9.释放连接
注意:先建立bufferevent_socket_new,创建出来缓冲区----bufferevent_setcb()----设置读缓冲使能bufferevent_enable()----建立连接bufferevent_socket_connect。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<event2/bufferevent.h>
#include<event2/event.h>
#include<fcntl.h>
#include<arpa/inet.h>
#define SER_PORT 9004void read_cb(struct bufferevent* bufev , void*arg)
{char buf[BUFSIZ];int n = bufferevent_read(bufev , buf , sizeof(buf)-1);if(n > 0){buf[n] = '\0';printf("recived from server:%s\n" , buf);}else{printf("no data");}//bufferevent_write(bufev , buf , strlen(buf)+1);sleep(1);
}void write_cb(struct bufferevent* bufev , void*arg)
{printf("I am client , writing over\n");
}void event_cb(struct bufferevent *bev, short events, void *ctx) {if (events & BEV_EVENT_CONNECTED) {printf("✅ Connected to server.\n");} else if (events & BEV_EVENT_ERROR) {perror("❌ Error from bufferevent");} else if (events & BEV_EVENT_EOF) {printf("⚠️ Connection closed by server.\n");}
}void read_terminal(evutil_socket_t fd , short what , void*arg)
{struct bufferevent*bufev = (struct bufferevent*)arg;char buf[1024] = {0};int len = read(fd , buf , sizeof(buf));bufferevent_write(bufev , buf , len);
}int main(int argc , char *argv[])
{int fd = socket(AF_INET , SOCK_STREAM , 0);struct sockaddr_in serv_addr;bzero(&serv_addr , sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SER_PORT);inet_pton(AF_INET , "10.11.1.211" , &serv_addr.sin_addr);struct event_base*base = event_base_new();struct bufferevent*bufev;bufev = bufferevent_socket_new(base , fd , BEV_OPT_CLOSE_ON_FREE);bufferevent_setcb(bufev , read_cb , write_cb , event_cb , NULL);bufferevent_enable(bufev , EV_READ | EV_WRITE);bufferevent_socket_connect(bufev , (struct sockaddr*)&serv_addr , sizeof(serv_addr));struct event*ev = event_new(base , STDIN_FILENO , EV_READ | EV_PERSIST ,read_terminal , bufev);event_add(ev , NULL);event_base_dispatch(base);event_base_free(base);return 0 ;
}