libevent学习

一,event事件的学习

//1,创建根
struct event_base *event_base_new(void);

//2,创建节点
struct event event_new(struct event_base ,
evutil_socket_t,
short,
event_callback_fn,
void *);

//3,添加节点
int event_add(struct event *ev,
const struct timeval *timeout);

//4,添加循环等待client处理
int event_base_dispatch(struct event_base *);

//5,删除节点
int event_del(struct event *);

//6,释放节点
void event_free(struct event *);

#include <event.h>
#include <string.h>
#include <unistd.h>

struct event *readEv = NULL;

void readcb(evutil_socket_t fd, short events, void *arg)
{
    char buf[256] = {0};
    int ret = recv(fd,buf,sizeof(buf),0);
    if(ret > 0){
        send(fd,buf,ret,0);
    }
    else if(ret == 0){
        //客户端关闭
        printf("client closed\n");
        close(fd);
        event_del(readEv);
        event_free(readEv);
        readEv = NULL;
    }
    else {
        perror("recv err");
        close(fd);
        event_del(readEv);
        event_free(readEv);
        readEv = NULL;
    }
}

void conncb(evutil_socket_t fd, short events, void *arg)
{
    struct event_base *base = arg;
   struct sockaddr_in client;
   socklen_t len = sizeof(client);
   int cfd = accept(fd,(struct sockaddr*)&client,&len);
   if(cfd > 0){
       //得到新连接,上树
        readEv = event_new(base,cfd,EV_READ|EV_PERSIST,readcb,NULL);
        event_add(readEv,NULL);
   }
}

int main()
{
    //1. 创建socket
    int lfd = socket(AF_INET,SOCK_STREAM,0);
    //2. 绑定bind
    struct sockaddr_in serv;
    bzero(&serv,sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_port = htons(8888);
    serv.sin_addr.s_addr = htonl(INADDR_ANY);

    int opt = 1;
    setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置端口复用

    if(bind(lfd,(struct sockaddr*)&serv,sizeof(serv)) < 0){
        perror("bind err");
        return -1;
    }
    //3. 监听listen
    listen(lfd,128);

    //libevent 创建根节点
    struct event_base *base = event_base_new();
    //创建event事件,设置回调函数,事件类型,文件描述符
    //struct event *event_new(struct event_base *, evutil_socket_t, short, event_callback_fn, void *); 
    struct event *connEv = event_new(base,lfd,EV_READ|EV_PERSIST,conncb,base);
    //设置监听event_add
    // event_add(struct event *ev, const struct timeval *timeout);
    event_add(connEv,NULL);//开始监听新连接事件
    //事件循环 event_base_dispatch 
    event_base_dispatch(base);
    //各种释放
    event_base_free(base);//释放根
    event_free(connEv);
    if(readEv ){
        event_free(readEv);
    }
    close(lfd);
    return 0;
}

二,bufferevent

1, server初始化
struct bufferevent *bufferevent_socket_new(struct event_base *base,
evutil_socket_t fd,
int options);

bufferevent_socket_new 对已经存在socket创建bufferevent事件,
可用于后面讲到的链接监听器的回调函数中,参数说明
//base – 对应根节点
//fd – 文件描述符
//第三个参数: BEV_OPT_CLOSE_ON_FREE = (1<<0), –释放bufferevent自动关闭底层接口
BEV_OPT_THREADSAFE = (1<<1), – 使bufferevent能够在多线程下是安全的
BEV_OPT_DEFER_CALLBACKS = (1<<2),
BEV_OPT_UNLOCK_CALLBACKS = (1<<3)

2, client 链接server函数
int bufferevent_socket_connect(struct bufferevent *bev,
struct sockaddr *serv ,
int socklen );
参数:
bev – 需要提前初始化的bufferevent事件
serv – 对端的ip地址,端口,协议的结构指针
socklen – 描述serv的长度

3, 释放bufferevent
void bufferevent_free(struct bufferevent *bufev);

4,bufferevent_setcb用于设置bufferevent的回调函数,
readcb,writecb,eventcb分别对应了读回调,
写回调,事件回调,cbarg代表回调函数的参数。

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

5,read和write的回调函数
typedef void (*bufferevent_data_cb)(struct bufferevent *bev,
void *ctx);

6, 事件的回调函数的处理

typedef void (*bufferevent_event_cb)(struct bufferevent *bev,
short what,
void *ctx);
 What 代表 对应的事件BEV_EVENT_EOF,
BEV_EVENT_ERROR,
BEV_EVENT_TIMEOUT,
BEV_EVENT_CONNECTED

7,写信息函数
①int bufferevent_write(struct bufferevent *bufev,
const void *data, size_t size);

 bufferevent_write是将data的数据写到bufferevent的写缓冲区

②int bufferevent_write_buffer(struct bufferevent *bufev,
struct evbuffer *buf);

bufferevent_write_buffer 是将数据写到写缓冲区另外一个写法,
实际上bufferevent的内部的两个缓冲区结构就是struct evbuffer。

9, 读取数据的函数

①size_t bufferevent_read(struct bufferevent *bufev,
void *data,
size_t size);

bufferevent_read 是将bufferevent的读缓冲区数据读到data中,
同时将读到的数据从bufferevent的读缓冲清除。

②int bufferevent_read_buffer(struct bufferevent *bufev,
struct evbuffer *buf);

bufferevent_read_buffer 将bufferevent读缓冲数据读到buf中,接口的另外一种。

10, 设置事件是否生效

①int bufferevent_enable(struct bufferevent *bufev,
short event);

②int bufferevent_disable(struct bufferevent *bufev,
short event);
bufferevent_enable与bufferevent_disable是设置事件是否生效,
如果设置为disable,事件回调将不会被触发。

三, 监听listenevent 事件

1, 链接监听器封装了底层的socket通信相关函数,
比如socket,bind,listen,accept这几个函数。
链接监听器创建后实际上相当于调用了socket,bind,listen,
此时等待新的客户端连接到来, 如果有新的客户端连接,
那么内部先进行accept处理,然后调用用户指定的回

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:值的参考
#define LEV_OPT_LEAVE_SOCKETS_BLOCKING  (1u<<0)  文件描述符为阻塞的

#define LEV_OPT_CLOSE_ON_FREE       (1u<<1)     关闭时自动释放

#define LEV_OPT_CLOSE_ON_EXEC       (1u<<2)

#define LEV_OPT_REUSEABLE       (1u<<3)     端口复用

#define LEV_OPT_THREADSAFE      (1u<<4)     分配锁,线程安

2, 函数与前一个函数不同的地方在与后2个参数,使用本函数时,认为socket已经初始化好,
并且bind完成,甚至也可以做完listen,所以大多数时候,我们都可以使用第一个函数。

struct evconnlistener *evconnlistener_new(struct event_base *base,
evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
evutil_socket_t fd);

3, 回调函数
typedef void (evconnlistener_cb)(struct evconnlistener ,
evutil_socket_t fd,
struct sockaddr * addr,
int socklen,
void * arg);
主要回调函数fd参数会与客户端通信的描述符,并非是等待连接的监听的那个描述符,
所以cliaddr对应的也是新连接的对端地址信息,已经是accept处理好的。

4, 使链接监听器生效
int evconnlistener_enable(struct evconnlistener *lev);

5, 使链接监听器失效
int evconnlistener_disable(struct evconnlistener *lev);

6, 释放链接监听器

void evconnlistener_free(struct evconnlistener *lev);

实现代码

//基于bufferevent的回射服务器
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>

static const char MESSAGE[] = "Hello, World!\n";

static const int PORT = 9995;

static void listener_cb(struct evconnlistener *, evutil_socket_t,
    struct sockaddr *, int socklen, void *);
static void conn_writecb(struct bufferevent *, void *);
static void conn_readcb(struct bufferevent *, void *);
static void conn_eventcb(struct bufferevent *, short, void *);
static void signal_cb(evutil_socket_t, short, void *);

int
main(int argc, char **argv)
{
    struct event_base *base;//定义根节点
    struct evconnlistener *listener;//定义连接监听器
    struct event *signal_event;//定义一个事件 信号

    struct sockaddr_in sin;

    base = event_base_new();//创建根节点
    if (!base) {
        fprintf(stderr, "Could not initialize libevent!\n");
        return 1;
    }

    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(PORT);
    //申请连接监听器,封装 socket,bind,,,listen,accept,同时得到新的描述符传给listener_cb 
    listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
        LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
        (struct sockaddr*)&sin,
        sizeof(sin));

    if (!listener) {
        fprintf(stderr, "Could not create a listener!\n");
        return 1;
    }
    //定义了 SIGINT信号事件,设置回调位signal_cb 
    signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);

    if (!signal_event || event_add(signal_event, NULL)<0) {
        fprintf(stderr, "Could not create/add a signal event!\n");
        return 1;
    }

    event_base_dispatch(base);//事件循环

    evconnlistener_free(listener);// 连接监听器的释放
    event_free(signal_event);//信号事件的释放
    event_base_free(base);//根节点的释放

    printf("done\n");
    return 0;
}

static void
listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
    struct sockaddr *sa, int socklen, void *user_data)
{
    struct event_base *base = user_data;
    struct bufferevent *bev;//定义bufferevent 
    //创建了一个bufferevent 
    bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    if (!bev) {
        fprintf(stderr, "Error constructing bufferevent!");
        event_base_loopbreak(base);
        return;
    }
    //设置回调 -- 读回调为空,写回调 conn_writecb , 事件回调 -conn_eventcb 
    bufferevent_setcb(bev, conn_readcb, conn_writecb, conn_eventcb, NULL);
    //使 写生效 
    bufferevent_enable(bev, EV_WRITE | EV_READ);
    //使读 失效 
    //bufferevent_disable(bev, EV_READ);
    //将MESSAGE 写到bufferevent的写缓冲区中
    //bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}

static void
conn_readcb(struct bufferevent *bev, void *user_data)
{
    //读数据  要从 bufferevent缓冲区读 数据
    char buf[256] = {0};
    //读数据
    int ret = bufferevent_read(bev,buf,sizeof(buf));
    if(ret > 0){
        bufferevent_write(bev,buf,ret);//写到写缓冲区
    }
}
static void
conn_writecb(struct bufferevent *bev, void *user_data)
{
    // struct evbuffer 是bufferevent 的缓冲区类型
    struct evbuffer *output = bufferevent_get_output(bev);
    if (evbuffer_get_length(output) == 0) {
        // 如果 bufferevent的写缓冲区内容长度为0,代表已经将写缓冲区内容刷新到底层缓冲区
        printf("flushed answer\n");
//      bufferevent_free(bev);//释放bufferevent 关闭底层接口 
    }
}

static void
conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
    if (events & BEV_EVENT_EOF) {
        printf("Connection closed.\n");
    } else if (events & BEV_EVENT_ERROR) {
        printf("Got an error on the connection: %s\n",
            strerror(errno));/*XXX win32*/
    }
    /* None of the other events can happen here, since we haven't enabled
     * timeouts */
    bufferevent_free(bev);
}

static void
signal_cb(evutil_socket_t sig, short events, void *user_data)
{
    struct event_base *base = user_data;
    struct timeval delay = { 2, 0 };//设置延迟2s

    printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");
    //如果有事件,处理完当前事件退出 
    event_base_loopexit(base, &delay);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值