【libevent】libevent快速入门

目录

简介

libevent和epoll区别?

原理简介

简易版QuickStart

安装

在线安装

编译安装

实战

查看libevent库是否存在

编写代码

使用BufferEvent

笔记

文档翻译


简介

基本的socket编程是阻塞/同步的,每个操作除非已经完成,出错,或者超时才会返回,这样对于每一个请求,要使用一个线程或者单独的进程去处理,系统资源没有办法支撑大量的请求。posix定义了可以使用异步的select系统调用,但是因为它采用了轮询的方式来判断某个fd是否变成active,效率不高。于是各系统就分别提出了基于异步的系统调用,例如Linux的epoll,由于在内核层面做了支持,所以可以用O(1)的效率查找到active的fd。基本上,libevent就是对这些高效IO的封装,提供统一的API,简化开发。

libevent和epoll区别?

epoll是Linux提供的一个事件检测api或者说一种检测机制,libevent是一个事件处理框架。

libevent支持多种事件检测机制,比如select,poll,epoll等,epoll只是其中一种机制,是Linux平台上效率最高的,也是Linux平台特有的。其他平台有和epoll类似的对应物件,比如bsd的kqueue,Windows的iocp...

一句话,libevent是一个开源库,epoll是一个Linux系统调用。
链接:https://www.zhihu.com/question/56908845/answer/494680014

struct event_base *base; 相当于声明epoll_fd

base = event_base_new(); 创建epoll_create

struct event; 相当于socket

event = event_new();创建epoll_event,绑定回调

event_add(); 相当于epoll_ctl,fd加入对应epoll_fd ,epoll_ctl_add,同时指定超时

evtimer_new(base,cb,arg);  创建超时事件  然后event_add()加入循环

最小堆保存时间事件,相同时长的事件保存在同一个队列中,不加入堆。

event_base_dispatch();  相当于while(1){epoll_wait()}

被触发的各种event会被放入active队列,统一执行

 https://blog.csdn.net/welongfor/article/details/86689224

原理简介

libevent默认情况下是单线程的,可以配置成多线程,每个线程有且只有一个event_base,对应一个struct event_base结构体以及附于其上的事件管理器,用来调度托管给它的一系列event,可以和操作系统的进程管理类比。当一个事件发生后,event_base会在合适的时间,不一定是立即去调用绑定在这个事件上的函数,直到这个函数执行完,再去调度其他的事件。

 

//创建一个event_base
struct event_base *base = event_base_new();
assert(base != NULL);

event_base内部有一个循环,循环阻塞在epoll等系统调用上,直到有一个/一些时间发生,然后去处理这些事件。当然,这些事件要被绑定在这个event_base上,每个事件对应一个struct event,可以是监听一个fd或者信号量之类的,struct event使用event_new来创建和绑定,使用event_add来将event绑定到event_base中。

 

// 创建并绑定一个event
struct event* listen_event;

//参数:event_base,监听的对象,需要监听的事件,事件发生后的回调函数,传给回调函数的参数
listen_event = event_new(base, listener, EV_READ | EV_PERSIST, callback_func, (void*)base);
//参数:event,超时时间,NULL表示无超时设置
event_add(listen_event, NULL);

注:libevent支持的事件及属性包括(使用bitfield实现)

  1. EV_TIMEOUT:超时;
  2. EV_READ:只要网络缓冲中还有数据,回调函数就会被触发;
  3. EV_WRITE:只要塞给网络缓冲的数据被写完,回调函数就会被触发;
  4. EV_SIGNAL:POSIX信号量;
  5. EV_PERSIST:不指定这个属性,回调函数被触发后事件会被删除;
  6. EV_ET:Edge-Trigger边缘触发(这个还不懂是什么意思)

然后启动event_base的循环,开始处理事件。循环地启动使用event_base_dispatch,循环将一直持续,找到不再有需要关注的事件,或者是遇到event_loopbreak()/event_loopexit()函数。

 

//启动循环,开始处理事件
event_base_dispatch(base);

接下来再来关注事件发生时的回调函数callback_func,callback_func的原型如下所示

 

typedef void(* event_callback_fn)(evutil_socket_t sockfd, short event_type, void *arg)

传给callback_func的是一个监听的fd,监听的事件类型,以及event_new中最后一个参数。在上述程序中,是将event_base传给了callback_func,实际中更常用的是构造一个结构体,把需要传给回调函数的参数都放进来,然后传给event_new,event_new再传给回调函数。

所以总结一下,对于一个服务器而言,流程大致如下:

  1. 获取待监听的内容的fd;
  2. 创建一个event_base;
  3. 创建一个event,指定待监听的fd,待监听事件的类型,以及事件放生时的回调函数及传给回调函数的参数;
  4. 将event添加到event_base的事件管理器中;
  5. 开启event_base的事件处理循环;
  6. (异步)当事件发生的时候,调用前面设置的回调函数。

简易版QuickStart

 

安装

在线安装

yum  install  libevent

编译安装

1. 下载
官方网站:http://libevent.org/,目前最新版是2.1
下载压缩包名为 libevent-2.1.11-stable.tar.gz
2. 解压

tar -zxvf libevent-2.1.11-stable.tar.gz 

3. 配置安装路径

cd libevent-2.1.11-stable
./autogen.sh  #生成configure
./configure

也可以指定后缀,比如路径为./configure -prefix=/usr

4. 编译安装

make
make install

5. 测试是否已安装

ls -al /usr/local/lib | grep libevent

6. 使用样例程序进行测试

cd sample/

此目录下,有很多样例,可以进行测试,也可以进行学习

 

实战

https://www.cnblogs.com/WindSun/p/12142656.html

查看libevent库是否存在

使用Libevent编写简单的程序和编译

使用libevent库开发的程序,默认编译需要加入-levent
问题:如果在编译或运行时候发现出现:error while loading shared libraries: libevent-2.1.so.7: cannot open shared object file: No such file or directory
则需要查看libevent-2.1.so.7是否存在,需要先安装mlocate

locate libevent-2.1.so.7

结果: 系统已经安装了该模块,在路径 /usr/local/lib/
映射libevent-2.1.so.7到lib64下即可

ln -s /usr/local/lib/libevent-2.1.so.7 /usr/lib64/libevent-2.1.so.7

然后就可以正常的运行了

编写代码

下面的代码实现了一个简单的echo server,server启动后,client端启动并连接,在cmd中输入文字,server端收到后,将文字再返回给client。
server端代码:

/**
You need libevent2 to compile this piece of code
Please see: http://libevent.org/
Or you can simply run this command to install on Mac: brew install libevent
Cmd to compile this piece of code: g++ LibeventQuickStartServer.c  -o  LibeventQuickStartServer /usr/local/lib/libevent.a
**/
#include<stdio.h>  
#include<string.h>  
#include<errno.h>  
  
#include<unistd.h>  
#include<event.h>

void accept_cb(int fd, short events, void* arg);
void socket_read_cb(int fd, short events, void* arg);

int tcp_server_init(int port, int listen_num);

int main(int argc, char const *argv[])
{
    /* code */
    int listener = tcp_server_init(9999, 10);
    if (listener == -1)
    {
        perror("tcp_server_init error");
        return -1;
    }

    struct event_base* base = event_base_new();

    // 监听客户端请求链接事件
    struct event* ev_listen = event_new(base, listener, EV_READ | EV_PERSIST, accept_cb, base);

    event_add(ev_listen, NULL);

    event_base_dispatch(base);

    return 0;
}

void accept_cb(int fd, short events, void* arg)
{
    evutil_socket_t sockfd;

    struct sockaddr_in client;
    socklen_t len = sizeof(client);

    sockfd = ::accept(fd, (struct sockaddr*)&client, &len);
    evutil_make_socket_nonblocking(sockfd);

    printf("accept a client %d\n", sockfd);

    struct event_base* base = (event_base*)arg;

    //动态创建一个event结构体,并将其作为回调参数传递给
    struct event* ev = event_new(NULL, -1, 0, NULL, NULL);
    event_assign(ev, base, sockfd, EV_READ | EV_PERSIST, socket_read_cb, (void*)ev);

    event_add(ev, NULL);
}


void socket_read_cb(int fd, short events, void* arg)
{
    char msg[4096];
    struct event* ev = (struct event*)arg;
    int len = read(fd, msg, sizeof(msg) - 1);

    if(len <= 0)
    {
        printf("some error happen when read\n");
        event_free(ev);
        close(fd);
        return;
    }

    msg[len] = '\0';
    printf("recv the client msg : %s\n", msg);

    char reply_msg[4096] = "I have received the msg: ";
    strcat(reply_msg + strlen(reply_msg), msg);

    write(fd, reply_msg, strlen(reply_msg));
}

typedef struct sockaddr SA;  
int tcp_server_init(int port, int listen_num)  
{  
    int errno_save;  
    evutil_socket_t listener;  
  
    listener = ::socket(AF_INET, SOCK_STREAM, 0);  
    if( listener == -1 )  
        return -1;  
  
    //允许多次绑定同一个地址。要用在socket和bind之间  
    evutil_make_listen_socket_reuseable(listener);  
  
    struct sockaddr_in sin;  
    sin.sin_family = AF_INET;  
    sin.sin_addr.s_addr = 0;  
    sin.sin_port = htons(port);  
  
    if( ::bind(listener, (SA*)&sin, sizeof(sin)) < 0 )  
        goto error;  
  
    if( ::listen(listener, listen_num) < 0)  
        goto error;  
  
  
    //跨平台统一接口,将套接字设置为非阻塞状态  
    evutil_make_socket_nonblocking(listener);  
  
    return listener;  
  
    error:  
        errno_save = errno;  
        evutil_closesocket(listener);  
        errno = errno_save;  
  
        return -1;  
}  

client端代码:


/**
You need libevent2 to compile this piece of code
Please see: http://libevent.org/
Or you can simply run this command to install on Mac: brew install libevent
Cmd to compile this piece of code: g++ LibeventQuickStartClient.c -o LibeventQuickStartClient /usr/local/lib/libevent.a
**/
#include<sys/types.h>  
#include<sys/socket.h>  
#include<netinet/in.h>  
#include<arpa/inet.h>  
#include<errno.h>  
#include<unistd.h>  
  
#include<stdio.h>  
#include<string.h>  
#include<stdlib.h>  
  
#include<event.h>  
#include<event2/util.h>  
  
  
  
  
int tcp_connect_server(const char* server_ip, int port);  
  
  
void cmd_msg_cb(int fd, short events, void* arg);  
void socket_read_cb(int fd, short events, void *arg);  
  
int main(int argc, char** argv)  
{  
    if( argc < 3 )  
    {  
        printf("please input 2 parameter\n");  
        return -1;  
    }  
  
  
    //两个参数依次是服务器端的IP地址、端口号  
    int sockfd = tcp_connect_server(argv[1], atoi(argv[2]));  
    if( sockfd == -1)  
    {  
        perror("tcp_connect error ");  
        return -1;  
    }  
  
    printf("connect to server successful\n");  
  
    struct event_base* base = event_base_new();  
  
    struct event *ev_sockfd = event_new(base, sockfd,  
                                        EV_READ | EV_PERSIST,  
                                        socket_read_cb, NULL);  
    event_add(ev_sockfd, NULL);  
  
    //监听终端输入事件  
    struct event* ev_cmd = event_new(base, STDIN_FILENO,  
                                      EV_READ | EV_PERSIST, cmd_msg_cb,  
                                      (void*)&sockfd);  
  
  
    event_add(ev_cmd, NULL);  
  
    event_base_dispatch(base);  
  
    printf("finished \n");  
    return 0;  
}  
  
  
  
  
  
  
void cmd_msg_cb(int fd, short events, void* arg)  
{  
    char msg[1024];  
  
    int ret = read(fd, msg, sizeof(msg));  
    if( ret <= 0 )  
    {  
        perror("read fail ");  
        exit(1);  
    }  
  
    int sockfd = *((int*)arg);  
  
    //把终端的消息发送给服务器端  
    //为了简单起见,不考虑写一半数据的情况  
    write(sockfd, msg, ret);  
}  
  
  
void socket_read_cb(int fd, short events, void *arg)  
{  
    char msg[1024];  
  
    //为了简单起见,不考虑读一半数据的情况  
    int len = read(fd, msg, sizeof(msg)-1);  
    if( len <= 0 )  
    {  
        perror("read fail ");  
        exit(1);  
    }  
  
    msg[len] = '\0';  
  
    printf("recv %s from server\n", msg);  
}  
  
  
  
typedef struct sockaddr SA;  
int tcp_connect_server(const char* server_ip, int port)  
{  
    int sockfd, status, save_errno;  
    struct sockaddr_in server_addr;  
  
    memset(&server_addr, 0, sizeof(server_addr) );  
  
    server_addr.sin_family = AF_INET;  
    server_addr.sin_port = htons(port);  
    status = inet_aton(server_ip, &server_addr.sin_addr);  
  
    if( status == 0 ) //the server_ip is not valid value  
    {  
        errno = EINVAL;  
        return -1;  
    }  
  
    sockfd = ::socket(PF_INET, SOCK_STREAM, 0);  
    if( sockfd == -1 )  
        return sockfd;  
  
  
    status = ::connect(sockfd, (SA*)&server_addr, sizeof(server_addr) );  
  
    if( status == -1 )  
    {  
        save_errno = errno;  
        ::close(sockfd);  
        errno = save_errno; //the close may be error  
        return -1;  
    }  
  
    evutil_make_socket_nonblocking(sockfd);  
  
    return sockfd;  
}  

程序运行截图

客户端

 

Paste_Image.png

服务端

Paste_Image.png

 

使用BufferEvent

在上面的代码中,client的cmd中有信息输入时,client直接将数据写入到fd中,server中收到信息后,也是直接将信息写入到fd中,因为fd是非阻塞的,所以不能保证正确。那么需要一个自己管理的缓存来管理自己的数据。那么步骤将稍微有些变化,如下所示:

  1. 设置scokfd为nonblocking;
  2. 使用bufferevent_socket_new创建一个struct bufferevent* bev,关联上面的sockfd,并托管给event_base;
  3. 使用bufferevent_setcb(bev, read_cb, write_cb, error_cb, (void*)arg);
  4. 使用buffevent_enable(bev, EV_READ|EV_WRITE|EV_PERSIST)来启动read/write事件

代码如下所示:

使用bufferevent的server端代码

 

#include<stdio.h>
#include<string.h>
#include<errno.h>

#include<event.h>
#include<event2/bufferevent.h>



void accept_cb(int fd, short events, void* arg);
void socket_read_cb(bufferevent* bev, void* arg);
void event_cb(struct bufferevent *bev, short event, void *arg);
int tcp_server_init(int port, int listen_num);

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

    int listener = tcp_server_init(9999, 10);
    if( listener == -1 )
    {
        perror(" tcp_server_init error ");
        return -1;
    }

    struct event_base* base = event_base_new();

    //添加监听客户端请求连接事件
    struct event* ev_listen = event_new(base, listener, EV_READ | EV_PERSIST,
                                        accept_cb, base);
    event_add(ev_listen, NULL);


    event_base_dispatch(base);
    event_base_free(base);


    return 0;
}



void accept_cb(int fd, short events, void* arg)
{
    evutil_socket_t sockfd;

    struct sockaddr_in client;
    socklen_t len = sizeof(client);

    sockfd = ::accept(fd, (struct sockaddr*)&client, &len );
    evutil_make_socket_nonblocking(sockfd);

    printf("accept a client %d\n", sockfd);

    struct event_base* base = (event_base*)arg;

    bufferevent* bev = bufferevent_socket_new(base, sockfd, 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(bufferevent* bev, void* arg)
{
    char msg[4096];

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

    msg[len] = '\0';
    printf("recv the client msg: %s", msg);


    char reply_msg[4096] = "I have recvieced the msg: ";

    strcat(reply_msg + strlen(reply_msg), msg);
    bufferevent_write(bev, reply_msg, strlen(reply_msg));
}



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

    if (event & BEV_EVENT_EOF)
        printf("connection closed\n");
    else if (event & BEV_EVENT_ERROR)
        printf("some other error\n");

    //这将自动close套接字和free读写缓冲区
    bufferevent_free(bev);
}


typedef struct sockaddr SA;
int tcp_server_init(int port, int listen_num)
{
    int errno_save;
    evutil_socket_t listener;

    listener = ::socket(AF_INET, SOCK_STREAM, 0);
    if( listener == -1 )
        return -1;

    //允许多次绑定同一个地址。要用在socket和bind之间
    evutil_make_listen_socket_reuseable(listener);

    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = 0;
    sin.sin_port = htons(port);

    if( ::bind(listener, (SA*)&sin, sizeof(sin)) < 0 )
        goto error;

    if( ::listen(listener, listen_num) < 0)
        goto error;


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

    return listener;

    error:
        errno_save = errno;
        evutil_closesocket(listener);
        errno = errno_save;

        return -1;
}

使用bufferevent的client端代码

#include<sys/types.h>  
#include<sys/socket.h>  
#include<netinet/in.h>  
#include<arpa/inet.h>  
#include<errno.h>  
#include<unistd.h>  
  
#include<stdio.h>  
#include<string.h>  
#include<stdlib.h>  
  
#include<event.h>  
#include<event2/bufferevent.h>  
#include<event2/buffer.h>  
#include<event2/util.h>  
  
  
  
  
int tcp_connect_server(const char* server_ip, int port);  
  
  
void cmd_msg_cb(int fd, short events, void* arg);  
void server_msg_cb(struct bufferevent* bev, void* arg);  
void event_cb(struct bufferevent *bev, short event, void *arg);  
  
int main(int argc, char** argv)  
{  
    if( argc < 3 )  
    {  
        printf("please input 2 parameter\n");  
        return -1;  
    }  
  
  
    //两个参数依次是服务器端的IP地址、端口号  
    int sockfd = tcp_connect_server(argv[1], atoi(argv[2]));  
    if( sockfd == -1)  
    {  
        perror("tcp_connect error ");  
        return -1;  
    }  
  
    printf("connect to server successful\n");  
  
    struct event_base* base = event_base_new();  
  
    struct bufferevent* bev = bufferevent_socket_new(base, sockfd,  
                                                     BEV_OPT_CLOSE_ON_FREE);  
  
    //监听终端输入事件  
    struct event* ev_cmd = event_new(base, STDIN_FILENO,  
                                      EV_READ | EV_PERSIST, cmd_msg_cb,  
                                      (void*)bev);  
    event_add(ev_cmd, NULL);  
  
    //当socket关闭时会用到回调参数  
    bufferevent_setcb(bev, server_msg_cb, NULL, event_cb, (void*)ev_cmd);  
    bufferevent_enable(bev, EV_READ | EV_PERSIST);  
  
  
    event_base_dispatch(base);  
  
    printf("finished \n");  
    return 0;  
}  
  
  
  
  
  
  
void cmd_msg_cb(int fd, short events, void* arg)  
{  
    char msg[1024];  
  
    int ret = read(fd, msg, sizeof(msg));  
    if( ret < 0 )  
    {  
        perror("read fail ");  
        exit(1);  
    }  
  
    struct bufferevent* bev = (struct bufferevent*)arg;  
  
    //把终端的消息发送给服务器端  
    bufferevent_write(bev, msg, ret);  
}  
  
  
void server_msg_cb(struct bufferevent* bev, void* arg)  
{  
    char msg[1024];  
  
    size_t len = bufferevent_read(bev, msg, sizeof(msg));  
    msg[len] = '\0';  
  
    printf("recv %s from server\n", msg);  
}  
  
  
void event_cb(struct bufferevent *bev, short event, void *arg)  
{  
  
    if (event & BEV_EVENT_EOF)  
        printf("connection closed\n");  
    else if (event & BEV_EVENT_ERROR)  
        printf("some other error\n");  
  
    //这将自动close套接字和free读写缓冲区  
    bufferevent_free(bev);  
  
    struct event *ev = (struct event*)arg;  
    //因为socket已经没有,所以这个event也没有存在的必要了  
    event_free(ev);  
}  
  
  
typedef struct sockaddr SA;  
int tcp_connect_server(const char* server_ip, int port)  
{  
    int sockfd, status, save_errno;  
    struct sockaddr_in server_addr;  
  
    memset(&server_addr, 0, sizeof(server_addr) );  
  
    server_addr.sin_family = AF_INET;  
    server_addr.sin_port = htons(port);  
    status = inet_aton(server_ip, &server_addr.sin_addr);  
  
    if( status == 0 ) //the server_ip is not valid value  
    {  
        errno = EINVAL;  
        return -1;  
    }  
  
    sockfd = ::socket(PF_INET, SOCK_STREAM, 0);  
    if( sockfd == -1 )  
        return sockfd;  
  
  
    status = ::connect(sockfd, (SA*)&server_addr, sizeof(server_addr) );  
  
    if( status == -1 )  
    {  
        save_errno = errno;  
        ::close(sockfd);  
        errno = save_errno; //the close may be error  
        return -1;  
    }  
  
    evutil_make_socket_nonblocking(sockfd);  
  
    return sockfd;  
}  

欢迎留言交流学习
参考链接:
https://www.felix021.com/blog/read.php?2068
http://blog.csdn.net/luotuo44/article/details/39670221



作者:shareLin
链接:https://www.jianshu.com/p/8ea60a8d3abb
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

/**
   Allocate a new evconnlistener object to listen for incoming TCP connections
   on a given address.

   @param base The event base to associate the listener with.
   @param cb A callback to be invoked when a new connection arrives. If the
      callback is NULL, the listener will be treated as disabled until the
      callback is set.
   @param ptr A user-supplied pointer to give to the callback.
   @param flags Any number of LEV_OPT_* flags
   @param backlog Passed to the listen() call to determine the length of the
      acceptable connection backlog.  Set to -1 for a reasonable default.
   @param addr The address to listen for connections on.
   @param socklen The length of the address.
 */
EVENT2_EXPORT_SYMBOL
struct evconnlistener *evconnlistener_new_bind(
    struct event_base *base,  //事件基座
    evconnlistener_cb cb,     //回调函数
    void *ptr,                //回调函数的参数
    unsigned flags,           //flags参数控制回调函数的行为
    int backlog,              //backlog是任何时刻网络栈允许处于还未接受状态的最大未决连接数
    const struct sockaddr *sa, 
    int socklen);

flags取值:http://www.cppblog.com/mysileng/archive/2013/02/07/197763.aspx
backlog是任何时刻网络栈允许处于还未接受状态的最大未决连接数。更多细节请查看系统的listen()函数文档。如果backlog是负的,libevent会试图挑选一个较好的值;如果为0,libevent认为已经对提供的套接字调用了listen()。

使用bufferevent的server端代码(libevent-2.1.10-stable)


/********************************************************
Copyright (C),  2016-2018,
FileName:		main
Author:        	woniu201
Email:         	wangpengfei.201@163.com
Created:       	2018/11/21
Description:    使用libevent实现TCP Server
********************************************************/
#include <iostream>
#include <string.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/listener.h>

#ifdef _WIN32
#include <windows.h>
#include <time.h>
#include <ws2tcpip.h>
#else
#include <unistd.h>
#include <arpa/inet.h>
#endif

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "wsock32.lib")


using namespace std;

char IPdotdec[20]; //存放点分十进制IP地址
struct in_addr s; // IPv4地址结构体

/************************************
@ Brief:		读缓冲区回调
@ Author:		woniu201
@ Created:		2018/11/21
@ Return:
************************************/
void read_cb(struct bufferevent* bev, void* arg)
{
	char buf[1024] = { 0 };
	char* ip = (char*)arg;

	bufferevent_read(bev, buf, sizeof(buf));

	cout << "client " << ip << " say:" << buf << endl;


	//写数据给客户端
	const char* p = "i am server, i received your msg!";
	bufferevent_write(bev, p, strlen(p) + 1);
}


/************************************
@ Brief:		写缓冲区回调
@ Author:		woniu201
@ Created:		2018/11/21
@ Return:
************************************/
void write_cb(struct bufferevent* bev, void* arg)
{
	cout << "I'm 服务器,成功写数据给客户端,写缓冲回调函数被调用..." << endl;
}

/************************************
@ Brief:		事件回调
@ Author:		woniu201
@ Created:		2018/11/21
@ Return:
************************************/
void event_cb(struct bufferevent* bev, short events, void* arg)
{
	char* ip = (char*)arg;
	if (events & BEV_EVENT_EOF)
	{
		cout << "connection closed:" << ip << endl;
	}
	else if (events & BEV_EVENT_ERROR)
	{
		cout << "some other error !" << endl;
	}

	bufferevent_free(bev);
	cout << "bufferevent 资源已经被释放..." << endl;
}

/************************************
@ Brief:		监听回调
@ Author:		woniu201
@ Created:		2018/11/21
@ Return:
************************************/
void cb_listener(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* addr, int len, void* ptr)
{
	struct sockaddr_in* client = (sockaddr_in*)addr;
	

	inet_ntop(AF_INET, (void*)&(client->sin_addr), IPdotdec, 16);
	cout << "connect new client: " << IPdotdec << "::" << ntohs(client->sin_port) << endl;

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

	//启动 bufferevent的 读缓冲区。默认是disable 的
	bufferevent_enable(bev, EV_READ);
}

int main()
{
#ifdef WIN32
	WORD wVersionRequested;
	WSADATA wsaData;
	wVersionRequested = MAKEWORD(2, 2);
	(void)WSAStartup(wVersionRequested, &wsaData);
#endif
	//init server
	struct sockaddr_in serv;

	memset(&serv, 0, sizeof(serv));
	serv.sin_family = AF_INET;
	serv.sin_port = htons(8888);
	serv.sin_addr.s_addr = htonl(INADDR_ANY);

	//创建 event_base
	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;
}

 

笔记

当有可读事件时会调用readcb函数,当有可写事件时调用writecb函数,发生错误时调用eventcb函数。

 

libevent有个机制大家要注意,bufferevent_socket读写事件普通的描述符读写事件。如果是带有bufferevent的,bufferevent本身又带有一层,为什么能设置高水位和低水位,就是通过bufferevent这一层来的;至于回调函数,

readcb是当套接字缓冲区有数据,对于bufferevent这种,如果超过低水位之类的才会触发;对于普通的描述符会立即触发(使用epool的LT模式的话);

writecb是能够写到缓冲区中才会触发,是一个后续的操作,可以理解成write-complete

也就是 writecb 是 bufferevent_write(bev, p, strlen(p) + 1);的数据真正的写到发送缓冲区 完成了才触发 

这个writecb是 bufferevent_write 写完之后, 触发的, 

文档翻译

http://www.cppblog.com/mysileng/category/20374.html

posted on 2022-10-04 01:26  bdy  阅读(98)  评论(0编辑  收藏  举报

导航