Libevent:安装到实现

本文详细介绍了Libevent库的安装过程,包括下载、编译和安装步骤。接着阐述了Libevent的使用流程,从创建事件处理框架、设置事件到启动事件循环。文章特别强调了事件的状态转换和数据缓冲区Bufferevent的创建与管理,包括如何设置读写回调以及如何创建基于套接字的bufferevent。最后,文中提到了Libevent在实现客户端代码中的应用。
摘要由CSDN通过智能技术生成

Libevent

libevent是跨平台开源库,专注网络处理高并发,封装了socket通信和I/O多路转接,是一个开源框架,代码精简性能高(使用大量回调函数),事件驱动
处理高并发时,epoll只能在Linux下 ,不想封装epoll可以用Libevent实现,windows下可以用select,也可以用Libevent实现

Libevent安装

  • 在官网下载Libevent安装包

  • 在DownLoads下解压

      tar -zxvf libevent-2.1.8-stable.tzr.gz 
      cd libevent-2.1.8-stable 
    
  • 源码安装

     ./configure            //检测安装环境,生成makefie
    
  • make编译安装Libevent库

      make
    
  • 拷贝数据到对应目录

      sudo make install
          //目录不存在则创建对应目录,一般安装在默认目录/usr/lcoal
          //   /usr/local/include 头文件 
          //  /usr/local/bin 可执行二进制文件
          // /usr/local/lib   库文件											
    
  • 检测安装成功

       cd sample
       ./ hello-world   //sample中的实例程序
       nc 127.1 9995    //成功通信
    
  • 编译程序

      gcc hello-world,c -o -levent
    

Libevent使用流程

  • 创建一个事件处理框架
  • 创建一事件
  • 事件添加到事件处理框架
  • 开始事件循环
  • 释放资源

创建事件处理框架

事件处理框架,其中有消息循环,需要自己启动

  • 创建event_base

      struct event_base* event_base_new(void); 
      //失败返回NULL
    

创建事件(不带缓冲区)

一般可以用于管道,普通文件描述符

struct event *event_new(struct event_base *base, evutil_socket_t fd,  short what, event_callback_fn cb,void *arg

—struct event_base *base 创建的事件框架
—evutil_socket_t fd,文件描述符
— short what 事件

  • #define EV_TIMEOUT 0x01 // 废弃
  • #define EV_READ 0x02 epollin
  • #define EV_WRITE 0x04 epollout
  • #define EV_SIGNAL 0x08 libevent封装了信号操作
  • #define EV_PERSIST 0x10 // 持续触发
  • #define EV_ET 0x20 // 边沿模式 ,默认为水平模式

what的设置决定了事件循环的工作模式,如果是指定了 EV_READ 并指定EV_PERSIST,则消息循环会一直检测直至通信一方关闭连接,如果不设置消息循环只检测一次,消息循环停止

—event_callback_fn cb what事件对应的回调函数,事件的处理动作
—void *arg 给cb传参
—没有缓冲区

释放事件

void event_free(struct event *event); 

回调函数

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

设置未决事件

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

int event_add(struct event *ev, const struct timeval *tv)

—const struct timeval *tv

  • 在tv时间内(如{0,100}),没有事件发生回调函数也会调用
  • tv设置为NULL,则事件被触发对应回调才会调用

设置非未决事件

对已经初始化的事件调用 event_del()将使其成为非未决和非激活的,如果事件不是未决的或者激活的,调用将没有效果

int event_del(struct event *ev);

事件循环

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

事件循环事件很长,可能为死循环

 int event_base_loopexit(struct event_base *base,const struct timeval *tv )
 //tv时间后如果event_base在执行激活事件的回调,则在执行完当前处理事件后退出

 int event_base_loopbreak(struct event_base *base);
//强制终止循环

释放资源

要先释放事件

event_base_free(struct event_base* base)

Libevent内部事件状态转换

在这里插入图片描述
非未决事件:没有资格被处理的事件
未决事件:有资格但还没被处理的事件


数据缓冲区Bufferevent

套接字通信时,需要带缓冲区的事件Bufferevent,头文件include<event/bufferevent.h>

套接字的文件描述符对应内核中一块缓冲区(又分为读写两块缓冲区),Libevent提供的Bufferevent也提供了这样的机制(自带缓冲区),事件读操作时,内核传输数据会放到Bufferevent缓冲区,因此读数据直接从Libevent提供的缓冲区读就行

  • bufferevent 由一个底层的传输端口(如套接字 ), 一个读取缓冲区和一个写入缓冲区组成
  • bufferevent 在读取或者写入了足够量的数据之后调用用户提供的回调
  • 每个 bufferevent 有两个数据相关的回调
    – 读缓冲区对应一个读取回调
    – 写缓冲区对应一个写入回调 (写缓冲区只要有数据就会自动发送,写入回调通知数据已发送,用处不大)

bufferevent和event区别
bufferevent是event升级版,它为事件维护了一块内存缓冲区供数据收发时使用(不用内核),读写缓冲区被操作时都有对应的回调函数可以进行相应的操作

创建基于套接字的bufferevent

struct bufferevent *bufferevent_socket_new(struct event_base *base,struct sockaddr *address, int addrlen

—evutil_socket_t fd
—enum bufferevent_options options

  • options: BEV_OPT_CLOSE_ON_FREE (一般只用这个)//释放 bufferevent 时关闭底层传输端口。这将关闭底层套接字,释放底层 bufferevent 等

—成功时函数返回一个 bufferevent,失败则返回 NULL

释放bufferevent

void bufferevent_free(struct bufferevent *bev);

读写缓冲区设置回调

给读写缓冲区设置回调

void bufferevent_setcb(struct bufferevent *bufev, bufferevent_data_cb readcb,bufferevent_data_cb writecb,bufferevent_event_cb eventcb,void *cbarg)

— bufferevent_data_cb readcb读
缓冲区有对应事件发生,读调用,在读回调中读数据,调用 bufferevent_read()

—bufferevent_data_cb writecb
写缓冲区有对应事件发生,写回调,可以直接传NULL

—bufferevent_event_cb eventcb
断开连接,连接成功等事件发生可以通过eventcb处理,可以传入NULL,

  • EV_EVENT_READING:读取操作时发生某事件,具体是哪种事件请看其他标志
  • BEV_EVENT_WRITING:写入操作时发生某事件,具体是哪种事件请看其他标志
  • BEV_EVENT_ERROR:操作时发生错误。关于错误的更多信息
  • BEV_EVENT_EOF:遇到文件结束指示
  • BEV_EVENT_CONNECTED:请求的连接过程已经完成
    –实现客户端适合可以用来判断

bufferevent_data_cb是被typedef定义过的函数指针类型

  • typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx)
  • setcb()时的 void* cbarg指向的内存传给readcb,由void *ctx接收

bufferevent启动链接

套接字通信时客户端用Libevent封装时调用

int bufferevent_socket_connect(struct bufferevent *bev, struct sockaddr *address, int addrlen)

启用/禁用缓冲区

禁用之后对应的回调不会被调用,即事件不会再处理

void bufferevent_enable( struct bufferevent *bufev, short events)

—events : EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE
默认ev_write是打开的
默认ev_read是关闭的(读缓冲的读回调默认关闭)

禁用

void bufferevent_disable(struct bufferevent *bufev, short events)

判断当前buffevent读写是否可用

short bufferevent_get_enabled(struct bufferevent *bufev)

链接监听器

套接字通信时服务器端调用,完成了创建监听套接字,绑定,监听,接收连接请求四步

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

—(struct event_base *base 事件处理框架
— evconnlistener_cb cb 接收连接后的用户操作
—unsigned flags

  • LEV_OPT_CLOSE_ON_FREE 释放链接监听器会关闭底层套接字
  • LEV_OPT_REUSEABLE libevent标记套接字可复用

—void *ptr 给回调函数cb传参,ptr指向的内存区域由cb->ptr取得
—int backlog:同时监听上限(128),可以传入-1即默认使用最大值
— const struct sockaddr *sa 服务器IP和端口信息


typedef void (*evconnlistener_cb)(
struct evconnlistener *listener,

  • listener与evconnlistener_new_bind返回值指向同一片内存

evutil_socket_t sock,

  • 用于通信的文件描述符

struct sockaddr *addr,

-客户端IP和端口信息
int len,
void *ptr

回调函数的填充由调用者实现,一般由操作系统调用,只注意实现即可


重新设置evconnlistener 的回调函数

void evconnlistener_set_cb(struct evconnlistener *lev, evconnlistener_cb cb, void *arg)

启用/禁用监听器

禁用

int evconnlistener_disable(struct evconnlistener *lev);

启用

int evconnlistener_enable(struct evconnlistener 

很少用到这两个函数

操作bufferevent中的数据

向bufferevent的输出缓冲区添加数据,写数据后会有size_t size这个字符串会被自动发送出去,写回调就会被调用

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);

释放链接监听器

void evconnlistener_free(struct evconnlistener 

Libevent实现客户端代码

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


// 读缓冲区回调
void read_cb(struct bufferevent *bev, void *arg)
{
	//读缓冲区数据

    char buf[1024] = {0};   
    bufferevent_read(bev, buf, sizeof(buf));
    char* p = "我已经收到了你发送的数据!";
    printf("client say: %s\n", p);


    // 发数据给客户端,往缓冲区写数据
    bufferevent_write(bev, p, strlen(p)+1);
    printf("====== send buf: %s\n", p);
}


// 写缓冲区回调
void write_cb(struct bufferevent *bev, void *arg)
{
    printf("我是写缓冲区的回调函数...\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 cb_listener(
        struct evconnlistener *listener, 
        evutil_socket_t fd, 
        struct sockaddr *addr, 
        int len, void *ptr)
{
   printf("connect new client\n");


   struct event_base* base = (struct event_base*)ptr;
   // 通信操作
   // 添加新事件
   struct bufferevent *bev;
   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[])
{


    // init server 
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_port = htons(9876);
    serv.sin_addr.s_addr = htonl(INADDR_ANY);


    struct event_base* base;
    base = event_base_new();
    // 创建套接字
    // 绑定
    // 接收连接请求
    struct evconnlistener* listener;
    listener = evconnlistener_new_bind(base, cb_listener, base, 
                                  LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 
                                  36, (struct sockaddr*)&serv, sizeof(serv));


    event_base_dispatch(base);


    evconnlistener_free(listener);
    event_base_free(base);


    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值