libevent网络库

发布时间 2023-06-07 17:20:47作者: koodu

libevent动态库的编译链接

安装过程:

  1. 对.tar.gz包 解压安装
  2. 切换到源码对应的文件夹 cd/lib.... -stable
  3. 可能需要yum install libssl-dev
  4. ./configure 会生成一个Makefile文件
  5. make
  6. make install
  7. 注意看libevent的include lib等文件放在哪

使用gcc编译:

gcc -std=gnu99 -o libo1_test lib01_test.c -I/usr/local/include/ -L /usr/local/lib/ -levent
要-I链接include/头文件目录 ,-L 链接lib文件  libevent相关文件放在这些位置
在这些弄好后,还是会出错
需要在/usr/lib64下创建libevent的链接
ln -s /usr/local/lib/libevent-2.1.so.7 /usr/lib64/libevent-2.1.so.7  即可

为了更简便,输入命令:

export LD_LIBRARY_PATH=/usr/local/lib   #具体文件路径看情况
也可以:/etc/profile  将命令加入到该文件
后面即可直接:
gcc -o bufferevent_client bufferevent_client.c -levent

ibevent的 event_base 事件处理框架

event_base结构体是一个事件的集合,在这个集合里检测那个事件被激活,然后执行事件

事件处理框架通过使用epoll poll select 检测事件

类似boost的io_content的run()

使用:

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

event_base结构体类似epoll句柄

 #include<stdio.h>
 #include<unistd.h>
 #include<event2/event.h>
 #include<event2/util.h> 
 int main(int argc,char* argv[])
 {
  struct event_base* base=event_base_new();//创建事件集合
  const char** str=event_get_supported_methods();
  for(int i=0;str[i]!=NULL;i++)
  {
   puts(str[i]);
  }                                                                                                      event_base_free(base); //释放event_base        event_base_dispatch(base);//事件循环                                             
  return 0;
 }
子进程中event_base

event_base初始化后,fork()创建子进程后,子进程复制父进程的代码资源,同样在子进程中也有struct event_base* base;但是在子进程中要初始化该base event_reinit(base);

创建事件 struct event 没有缓冲区

what主要对应上面的宏定义 事件创建默认水平模式,事件触发调用回调函数处理 void*arg作为回调函数的参数

event_new()创建的事件为非未决状态,通过加入到event_base()为未决状态

非未决:没有资格被处理 未决:有资格但还没被处理

调用事件创建函数创建事件;然后将事件加入到事件处理event_base上

所有新创建的事件都是初始化的,并且非阻塞,调用event_add可以让一个事件变为阻塞。
调用event_free释放event,在事件阻塞或者激活状态下调用event_free是安全的,这个函数会在释放event之前将事件变为非阻塞并且非激活状态。

event_del(struct event* ev);//从event_base中删除事件

当一个事件被触发,在事件对应的回调函数调用前该事件就会变为非阻塞的

事件循环

event_base_dispatch(struct event_base base);* event_base循环处理其事件集

事件在event_base上等待触发,事件被触发后调用回调函数执行,若是事件只触发一次,则被触发后,事件移出event_base。可通过在事件event_new()时,将事件的short what赋值为持续触发:EV_PERSIST

持续触发,只要触发就调用回调函数,处理后有进入event_base处理集

退出事件循环

event_base_loopexit(...base,...tv)可设定多长时间退出事件循环,若是在设定时间退出时,正在执行回调函数,则执行完在退出。

事件在循环中被触发,进入激活状态 调用回调函数,调用后事件又变为非未决状态

event结合管道读写的例子

读管道:

#include<fcntl.h>
#include<stdlib.h>
#include<event2/event.h>
#include<event2/util.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<memory.h>
//管道通信       在读写端有事件触发时回调函数处理
//回调函数,有三个参数
void read_cb(evutil_socket_t fd,short what,void* arg)//会持续读,写端关闭后还是持续读
{
     char buff[1024];
     memset(buff,0,sizeof(buff));
     if(read(fd,buff,sizeof(buff))<0)
     {
       perror("read error");
       exit(1);
     }
     printf("read:%s\n",buff);
 }
 int main(int argc,char* argv[])
 {
     unlink("myfifo");                                                                                                                                                  
     mkfifo("myfifo",0777);//创建管道通信的管道文件
 
     int fd=open("myfifo",O_RDONLY|O_NONBLOCK);//打开管道文件,读端
     if(fd==-1)
     {
        perror("open error");
        exit(1);
     }
     //已经打开管道文件,获得了读端的fd;接下来是把读端的事件加到event_base里,有读端数据触发则处理
     struct event_base* base=event_base_new();
     //创建读端事件       触发读事件后,调用回调函数read_cb
     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;
 }

管道写端:

 #include<stdio.h>
 #include<unistd.h>
 #include<sys/stat.h>
 #include<sys/types.h>
 #include<fcntl.h>
 #include<stdlib.h>
 #include<memory.h>
 #include<event2/event.h>
 #include<event2/util.h>
 
 //管道通信       在写端能触发时回调函数处理
 //回调函数,有三个参数
 void write_cb(evutil_socket_t fd,short what,void* arg)
 {
     char*buff="write to fifo";
     size_t size=strlen(buff);
     if(write(fd,buff,size)!=size)                                                                                      
     {
       perror("read error");
       exit(1);
     }
 }
 int main(int argc,char* argv[])
 {
     //先打开读端,再打开写端
     int fd=open("myfifo",O_WRONLY|O_NONBLOCK);//打开管道文件,写端
     if(fd==-1)
     {
        perror("open error");
        exit(1);
     }
     //已经打开管道文件,获得了写端的fd;接下来是把写端的事件加到event_base里,可以写触发则处理
     struct event_base* base=event_base_new();
     //创建写端事件       触发写事件后,调用回调函数write_cb
     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;
 }

bufferevent

读缓冲区有数据,触发读事件,在读回调中使用bufferevent_read()读出,写缓冲区有数据会自动发送,触发写回调函数

bufferevent有读写缓冲区,当读缓冲区有数据时,读缓冲区对应的回调函数会被调用,在回调函数中读缓冲区数据

bufferevent_read()

当有数据要发送,调用bufferevent_write()往写缓冲区写入数据,写缓冲区有数据,会自动发送数据,发生数据后,写回调才被调用

bufferevent主要使用在服务端与客户端进行交互通信的fd上

//释放bufferevent:void bufferevent_free(struct bufferevent* bev);
//读写缓冲区的回调函数:
typedef void (*bufferevent_data_cb) (struct bufferevent* bev,void* ctx);
typedef void (*bufferevent_event_cb) (struct bufferevent* bev,short events,void* ctx);

  1. 创建bufferevent事件 bufferevent_socket_new()
  2. bufferevent_setcb()设置读写回调函数
  3. 定义读写回调函数
  4. 在读写回调函数使用bufferevent_read() bufferevent_write()读写

buffer_socket_connect()主要用在客户端要与服务端建立连接

默认ev_write是enable

默认ev_read是关闭的,要使用ev_read先enable开启

server使用bufferevent流程:要监听,bind等(创建前缀的流程)

绑定加监听 evconnlistener_new_bind()返回监听器对象,有连接到来调用回调函数 (listen()函数)

回调函数 类似accept()函数

服务端事件示例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<event2/util.h>
#include<event2/bufferevent.h>
#include<event2/listener.h>
#include<string.h>
//使用bufferevent实现服务端
//读回调
void read_cb(struct bufferevent* bev,void* arg)
{
     char buff[1024]={0};
     bufferevent_read(bev,buff,sizeof(buff));
     printf("接收到:%s\n",buff);
     char* pt="服务端已接收到数据";
     bufferevent_write(bev,pt,strlen(pt)+1);
     puts("服务端已发送数据给客户端");
 }
 //写回调,数据发送出去写回调才会被调用
 void write_cb(struct bufferevent* bev,void* arg)
 {
     puts("数据已发送");
 }
 //事件回调,主要看发生了什么事件
 void event_cb(struct bufferevent* bev,short events,void* arg)
 {
     if(events&BEV_EVENT_EOF)
     {
         puts("连接断开");
     }
 }
 //有连接则调用回调函数处理连接,服务端连接客户端后的fd在回调函数形参上
 //处理连接,将连接间的通信初始化为事件
 void listen_fd(struct evconnlistener*listener,evutil_socket_t fd,struct sockaddr* addr,int len,void* ptr)
 {//把连接通信封装成bufferevent事件
     struct event_base* base=(struct event_base*)ptr;
     //连接通信事件,bufferevent事件关联服务端与客户端连接的fd;在base上
     struct bufferevent* 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,char* argv[])
 {
     struct event_base* base=event_base_new();//事件处理框架
     struct sockaddr_in serveraddr;//服务器IP和端口
     memset(&serveraddr,0,sizeof(serveraddr));
     serveraddr.sin_family=AF_INET;
     serveraddr.sin_port=htons(atoi(argv[1]));
     serveraddr.sin_addr.s_addr=INADDR_ANY;
 
     //绑定加监听接受,调用回调时就有与客户端连接的fd,有连接 则回调,第三个参数是给回调函数传参,传给回调函数的void* ptr
     struct evconnlistener* listen=evconnlistener_new_bind(base,listen_fd,base,
                                   LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,-1,
                                  (struct sockaddr*)&serveraddr,sizeof(serveraddr));
     event_base_dispatch(base);//循环
     evconnlistener_free(listen);
     event_base_free(base);//释放资源
     return 0;
 }
客户端,客户端通过事件event读终端数据,用bufferevent_write发送出去
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<event2/event.h>
#include<event2/bufferevent.h>
#include<string.h>
#include<arpa/inet.h>
 
void read_cb(struct bufferevent* bev,void* arg)
 {
     char buff[1024]={0};
     bufferevent_read(bev,buff,sizeof(buff));
     printf("recv:%s\n",buff);
 }
 void event_cb(struct bufferevent* bev,short events,void* arg)
 {
     if(events&BEV_EVENT_EOF)
     {
         puts("连接断开");
     }else if(events&BEV_EVENT_CONNECTED)
     {
         puts("已经连上");
     }
 }
 void read_terminal(int fd,short what,void* arg) 
 {
     char buf[1024]={0};//event事件读取终端的数据
     int len=read(fd,buf,sizeof(buf));
     //读取到的数据通过bufferevent事件发生出去 
     struct bufferevent* bev=(struct bufferevent*)arg;
     bufferevent_write(bev,buf,len+1);//借用bufferevent将终端数据写到客户端缓冲区
 }
 int main(int argc,char* argv[])
 {
     struct sockaddr_in serveraddr;//服务端IP和port
     memset(&serveraddr,0,sizeof(serveraddr));
     serveraddr.sin_family=AF_INET;
     serveraddr.sin_port=htons(atoi(argv[2]));
     inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);
 
     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);
     //将事件连接到服务器
     bufferevent_socket_connect(bev,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
     //给事件设置回调
     bufferevent_setcb(bev,read_cb,NULL,event_cb,NULL);
     bufferevent_enable(bev,EV_READ);
 
     //在事件循环前,可以接收终端输入,输入为event事件,STDIN_FILENO是终端读的文件描述符,监听终端输入,触发读事件
     //把与服务端通信的事件bufferevent传给event的回调函数,通过bufferevent传给服务端
     struct event* ev=event_new(base,STDIN_FILENO,EV_READ|EV_PERSIST,read_terminal,bev);
     event_add(ev,NULL);
 
     //事件循环
     event_base_dispatch(base);
     event_base_free(base);
     return 0;
 }

信号事件

struct event* sig=evsignal_new(base,SIGINT,sigfunc,NULL);//NULL为信号回调函数的void* arg参数

void sig_cb(evutil_socket_t fd,short event,void *arg)
{
    printf("In signal event callback\r\n");
    struct event_base * base = (struct event_base *)arg;
    event_base_loopbreak(base);

}

    struct event_base* base=event_base_new();
    struct event* ev=evsignal_new(base,
        (evutil_socket_t)SIGALRM,
        sig_cb,
        base);
   evsignal_add(ev,NULL);//添加到事件处理