Libevent安装和使用

1、Libevent简介

livevent是一个轻量级的开源的高性能的事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。Libevent是用于编写高速可移植非阻塞IO应用的库。

2、Libevent安装

ubuntu@VM-0-7-ubuntu:~/Project$ wget 
https://github.com/libevent/libevent/releases/download/release-2.1.10-stable/libevent-2.1.10-stable.tar.gz

ubuntu@VM-0-7-ubuntu:~/Project$ tar -xzvf libevent-2.1.10-stable.tar.gz 

ubuntu@VM-0-7-ubuntu:~/Project$ cd libevent-2.1.10-stable/

配置编译选项,如需交叉编译可在这一步指定交叉编译器以及安装路径–prefix指定编译路径 --host接编译后运行的机器  CC和CXX选项接交叉编译器

ubuntu@VM-0-7-ubuntu:~/Project/libevent-2.1.10-stable$ ./configure

ubuntu@VM-0-7-ubuntu:~/Project/libevent-2.1.10-stable$ make    

ubuntu@VM-0-7-ubuntu:~/Project/libevent-2.1.10-stable$ sudo make 
install  

3、libevent使用

3.1、创建event_base事件集合以及event事件函数

使用libevent函数之前需要分配一个或者多个event_base结构体。每个event_base结构体持有一个事件集合,可以检测以确定哪个事件是激活的。如果需要用多个线程检测IO,则需要为每个线程使用一个event_base。event_base_new()函数分配并且返回一个新的具有默认设置的event_base。

struct event_base *event_base_new(void);

接着就需要在base事件集合下创建event事件,event_new()试图分配和构造一个用于base的新的事件,设置事件标志读事件、写事件、边沿触发、信号还有持久化以及事件发生的回调函数。

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

下面是可监听的事件标志:

#define EV_TIMEOUT      0x01
#define EV_READ         0x02
#define EV_WRITE        0x04
#define EV_SIGNAL       0x08
#define EV_PERSIST      0x10
#define EV_ET           0x20

接着调用event_add()将添加事件,第一个参数为添加的事件,第二个参数可设置超时时间,若传NULL空指针则不超时。

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

启用事件循环,libevent循环处理事件,event_base_dispatch等同于没有设置标志的event_base_loop。所以,event_base_dispatch()将一直运行,直到被注册的事件全部被移除,或者调用了event_base_loopbreak()或者event_base_loopexit()为止。

int event_base_loop(struct event_base *base, int flags);

int event_base_dispatch(struct event_base *base);                                

3.2、bufferevent函数

多时候,除了响应事件之外,应用还希望做一定的数据缓冲。就用到bufferevent,基于套接字的bufferevent是最简单的,它使用libevent的底层事件机制来检测底层网络套接字是否已经就绪,可以进行读写操作,并且使用底层网络调用(如read、write)来发送和接收数据。

使用bufferevent_socket_new()创建基于套接字的bufferevent,将需要监听的文件描述符fd添加的到事件集合base中去,options为掩码通常为BEV_OPT_CLOSE_ON_FREE,表示释放bufferevent时关闭底层传输端口。这将关闭底层套接字,释放底层bufferevent等。

struct bufferevent *bufferevent_socket_new(struct event_base *base,
evutil_socket_t fd, 
enum bufferevent_options options);

接着调用bufferevent_setcb()函数设置回调函数。readcb、writecb和eventcb函数将分别在已经读取足够的数据、已经写入足够的数据,或者发生错误时被调用,最后void *cbarg是传入回调函数的参数。

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

最后启用bufferevent上的EV_READ、EV_WRITE或者EV_READ | EV_WRITE事件,与之前的ev_add添加事件类似。

void bufferevent_enable(struct bufferevent *bufev, short events);

3.3、Libevent服务器程序

/*********************************************************************************
 *      Copyright:  (C) 2020 WuYuJun<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  libevent_demo.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(05/31/2020)
 *         Author:  WuYuJun <[email protected]>
 *      ChangeLog:  1, Release initial version on "05/31/2020 03:00:52 PM"
 *                 
 ********************************************************************************/

#include  
#include  
#include  
#include 
#include 
#include 
#include           /*  See NOTES */
#include 
#include  
#include  
#include  
#include  





int tcp_server_init(int port, int back_log) ; 
void accept_cb(int fd, short events, void* arg) ; 

void socket_read_cb(struct bufferevent* bev, void* arg) ; 
void event_cb(struct bufferevent *bev, short event, void *arg) ; 

void to_upper(const char* buf, int buf_size, char *upper_buf, int upper_size) ;

int main(int argc, char **argv)
{
    int                     serv_fd = -1 ;
    struct event_base       *base = NULL;
    struct event            *event_serv = NULL ; 
    struct sigaction        sa;

    serv_fd = tcp_server_init(9806, 13);  
    if(serv_fd < 0)
    {
        printf("tcp_server_init error\n") ;
        return -1 ;
    }

    sa.sa_handler = SIG_IGN;
    sigaction( SIGPIPE, &sa, 0 );
    /*     server close,client still write to server will  have sigpipe and kill process  */ 


    base = event_base_new() ;
    assert(base != NULL) ;


    event_serv = event_new(base, serv_fd, EV_READ|EV_PERSIST, accept_cb, (void*)base);

    event_add(event_serv, NULL);  

    /* 循环处理event事件 */
    event_base_dispatch(base);  

    event_free(event_serv) ;
    event_base_free(base) ;
    return 0 ; 

}/* End Of Main */


void accept_cb(int fd, short events, void* arg)  
{  
    evutil_socket_t         cli_fd;  
    struct sockaddr_in      cli_addr ;  
    struct bufferevent      *bev ; 
    socklen_t               len = 0 ;
    struct event_base       *base ; 
    len = sizeof(cli_addr);  
    cli_fd = accept(fd, (struct sockaddr*)&cli_addr, &len );  
    evutil_make_socket_nonblocking(cli_fd);  
    printf("accept client%d successful\n", cli_fd);  
    base = (struct event_base*)arg;  

    bev = bufferevent_socket_new(base, cli_fd, BEV_OPT_CLOSE_ON_FREE);  
    
    bufferevent_setcb(bev, socket_read_cb, NULL, event_cb, arg);  

    bufferevent_enable(bev, EV_READ | EV_PERSIST);  
}  



void socket_read_cb(struct bufferevent* bev, void* arg)  
{  
    char    msg[4096];  
    char    reply[4096] ;

    size_t len = 0 ;
    
    memset(msg,0,sizeof(msg)) ;
    len = bufferevent_read(bev, msg, sizeof(msg));  


    printf("recv %d bytes the client: %s\n",len, msg);  

    memset(reply, 0, sizeof(reply)) ;
    to_upper(msg, len, reply, sizeof(reply)) ;
    bufferevent_write(bev, reply, len);  
} 


void event_cb(struct bufferevent *bev, short event, void *arg)  
{  
    struct event *ev ; 

    if (event & BEV_EVENT_EOF)  
        printf("connection closed\n");  
    else if (event & BEV_EVENT_ERROR)  
        printf("some other error\n");  
    
    //这将自动close套接字和free读写缓冲区  
    if(bev != NULL)
        bufferevent_free(bev);  
}  


int tcp_server_init(int port, int back_log)  
{  

    evutil_socket_t     serv_fd;  
    struct sockaddr_in  serv_addr;  

    serv_fd = socket(AF_INET, SOCK_STREAM, 0);  
    if( serv_fd < 0 )  
        return -1;  


    //允许多次绑定同一个地址。要用在socket和bind之间  
    evutil_make_listen_socket_reuseable(serv_fd);  
    serv_addr.sin_family = AF_INET;  
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY) ;
    serv_addr.sin_port = htons(port);  
    
    if( bind(serv_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0 )  
        return -2;  


    if( listen(serv_fd, back_log) < 0)  

        return -3;  

    //跨平台统一接口,将套接字设置为非阻塞状态  
   evutil_make_socket_nonblocking(serv_fd);  

    return serv_fd;  


}  


void to_upper(const char* buf, int buf_size, char *upper_buf, int upper_size)
{
    int             i = 0 ;
    if(buf == NULL || upper_buf == NULL || buf_size <= 0 || upper_size <= 0)
    {
        printf("Invaild input parameter in %s:%d\n" , __FUNCTION__, __LINE__) ;
        return ;
    }

    for(i=0 ; i<buf_size; i++)
    {
        if(buf[i] >= 'a' && buf[i] <= 'z')
        {
            upper_buf[i] = buf[i] + 'A' - 'a'  ;
        }
        else
        {
            upper_buf[i] = buf[i] ;
        }
    }
}

3.4、Libevent客户端

这里客户端程序将标准输入也加入监听,变量 base设置为全局变量,在bufferevent异常事件处理的回调函数将libevent的循环终止,从主函数中 event_base_dispatch(base); 跳出,接下来后面函数释放资源,进入大循环重新申请资源,重新连接服务器

/*********************************************************************************
 *      Copyright:  (C) 2020 WuYuJun<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  libevent_cli.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(06/01/2020)
 *         Author:  WuYuJun <[email protected]>
 *      ChangeLog:  1, Release initial version on "06/01/2020 11:08:15 PM"
 *                 

 ********************************************************************************/

#include  
#include  
#include  
#include  
#include  
#include  

#include  
#include  
#include  
#include 


#include  
#include  
#include  
#include  


#define SOCK_PORT   9806
#define SOCK_IP     "127.0.0.1"

int g_stop = 0 ;

void cmd_msg_cb(int fd, short events, void* arg) ; 

static void event_cb(struct bufferevent *bev, short events, void *ctx) ;

void server_msg_cb(struct bufferevent* bev, void* arg) ; 

void sig_handler(int SIG_NUM)
{
       if(SIG_NUM == SIGUSR1)
                    g_stop = 1 ;
}

struct event_base       *base =  NULL ; 

int main(int argc,char **argv)
{

    struct bufferevent      *bev = NULL ;
    struct event            *ev_cmd = NULL ;
    struct sockaddr_in      server_addr; 


    while(!g_stop)
    {
        base = event_base_new() ;
        if(base == NULL)
        {
            printf("event_base_new() failed\n") ;
            return -1 ;
        }


        bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); 

        //监听标准输入
        ev_cmd = event_new(base, STDIN_FILENO,  EV_READ | EV_PERSIST,  cmd_msg_cb, (void*)bev);  
        event_add(ev_cmd, NULL);  



        memset(&server_addr, 0, sizeof(server_addr) );  
        server_addr.sin_family = AF_INET;  
        server_addr.sin_port = htons(SOCK_PORT);  
        inet_aton(SOCK_IP, &server_addr.sin_addr);  


        if( bufferevent_socket_connect(bev, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0 )
        {
            printf("connect server %s:%d error!\n", SOCK_IP, SOCK_PORT) ;
        }





        bufferevent_setcb(bev, server_msg_cb, NULL, event_cb, (void*)ev_cmd);  
        bufferevent_enable(bev, EV_READ | EV_PERSIST);  






        event_base_dispatch(base) ;   // 阻塞  执行循环
        bufferevent_free(bev) ;
        event_base_free(base) ;
    }/* End Of while(!g_stop) */

    return 0 ;
}/* End Of Main */



void cmd_msg_cb(int fd, short events, void* arg)  
{  
    char                    msg[1024];  
    int                     rv = -1 ;
    struct bufferevent      *bev = NULL ; 


    memset(msg, 0, sizeof(msg)) ;
    rv = read(fd, msg, sizeof(msg));  
    if( rv < 0 )  
    {  
        perror("read fail ");  
        exit(1);  
    }  
    bev = (struct bufferevent*)arg ;  

    //把终端的消息发送给服务器端  
    bufferevent_write(bev, msg, rv);  
} 


static void event_cb(struct bufferevent *bev, short events, void *arg)
{
    struct event *ev ;




    ev = (struct event*)arg;
    if (events & BEV_EVENT_ERROR)
    {
        fprintf(stdout,"Error from bufferevent!xxx \n");
    }
    if (events & BEV_EVENT_TIMEOUT)
    {
        fprintf(stdout,"Error from bufferevent  BEV_EVENT_TIMEOUT ! \n");
    }


    if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR))  // 断网重连机制
    {
        fprintf(stdout,"system error from bufferevent  BEV_EVENT_ERROR ! \n");
        if(ev != NULL)
        {
            event_free(ev) ;
        }
        else
        {
            fprintf(stdout,"system error ev is null");
        }

        if(base != NULL)
        {
            event_base_loopbreak(base);
        }
        else
        {
            fprintf(stdout,"system error base is null");
        }   
    }
}



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

    char        msg[1024] ;  
    size_t      len = 0 ; 
    
    len = bufferevent_read(bev, msg, sizeof(msg));  
    msg[len] = '\0';  
    printf("recv %ld bytes from server:%s\n",len, msg);  
}  

参考:https://blog.csdn.net/weixin_44836580/article/details/89644951

https://blog.csdn.net/nyiragongo/article/details/91974333

https://www.cnblogs.com/wainiwann/p/7096245.html

你可能感兴趣的:(linux学习)