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(){ } --给调用者发送写成功通知
        可以直接传NULL

eventcb:设置事件回调,也可以传NULL

cbarg:上述回调函数的参数

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 9004

void 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);//默认读缓冲是不可的,所以要设置成enable
    return;
}

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 9004

void 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 ;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值