姜鹏辉的个人博客 GreyNius

【C】C语言编写http服务器记录-libevent笔记篇

2019-11-30

libevent

头文件:

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

编译时需要加 -levent

event2接口

参考:socket编程与libevent2的一些归纳总结

旧版libevent中,一般只能操作一个全局的event_base,而在新版libevent中,event_base交由用户来管理,用户可以创建删除event_base,也可以把event注册到不同的event_base上。

新建一个 event_base

#include <event2/event.h>
struct event_base *event_base_new(void);

释放一个event_base

#include <event2/event.h>
void event_base_free(struct event_base *eb);

event的生命周期

event的生命周期与相关的函数关系密切 event生命周期

用户自己创建的event是uninitialized的,需要使用event_assign()进行初始化,或者直接使用event_new()从无到有创建一个新的初始化了的event。在初始化时,完成了回调函数的绑定。 event的初始状态是non-pending,表示这个event不会被触发。

新建(并初始化)一个 event

struct event *event_new(struct event_base *base, evutil_socket_t fd, short events,
                        event_callback_fn callback, void *callback_arg);

新建event需要给定event_base, evutil_socket_t与系统相兼容,在linux下实际就是int,与socket()返回的类型一致

#ifdef WIN32
#define evutil_socket_t intptr_t
#else
#define evutil_socket_t int
#endif

events是一组flag,用于表示要监视的事件类型,还会影响event的一些行为,包括:

  • EV_TIMEOUT - 监视超时的事件 需要说明的是,在调用event_new()时,这个flag是不用设置的,如果event发生超时,则必然会触发,无论设置与否
  • EV_READ - 监视可读的事件
  • EV_WRITE - 监视可写的事件
  • EV_SIGNAL - 监视信号量
  • EV_PERSIST - 永久生效,否则触发一次后就失效了
  • EV_ET - 设置边缘触发(edge-triggered) callback和callback_arg是回调操作所需的,不再详述 新建的event是non-pending状态的

初始化一个event

int event_assign(struct event *ev,
                 struct event_base *base, evutil_socket_t fd, short events, 
                 event_callback_fn callback, void *callback_arg);

这个不会申请内存,其他同event_new()

释放一个event

void event_free(struct event *ev);

判断event是否初始化/被释放

int event_initialized(const struct event *ev);

将event置为pending状态

int event_add(struct event *ev, const struct timeval *timeout);

其中timeout可以指定超时时间,超时和EV_TIMEOUT配合使用。如果timeout如果为NULL,则表示永不超时,struct timeval的结构为:

struct timeval {
    time_t      tv_sec;     /* seconds */
    suseconds_t tv_usec;    /* microseconds */
};

额外说句,操作当前时间对应的timeval可以用

#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
int settimeofday(const struct timeval *tv, const struct timezone *tz);

将event置为non-pending状态

int event_del(struct event *ev);
检查event是否为pending状态
int event_pending(const struct event *ev, short events, struct timeval *tv);

需要注意的是,不需要查询event是否为active状态,因为在active时,线程正在执行回调函数,其他函数需要等到回调执行完毕,而此时已经退出了active状态

将event置为active状态

void event_active(struct event *ev, int res, short/* deprecated */);

res是要手动指派的flag

bufferevent

简介

数据缓冲区是libevent为IO缓冲区操作提供的一种通用机制,bufferevent 由一个底层的传输端口(如套接字)。
数据缓冲区是由读缓冲区写缓冲区两部分组成。

创建和释放

使用函数bufferevent_socket_new(),创建bufferevent的套接字.

struct bufferevent * bufferevent_socket_new(
    struct event_base *base,
    evutil_socket_t fd,
    enum bufferevent_options options
);
// 参数options: 
// BEV_OPT_CLOSE_ON_FREE 释放 bufferevent 时关闭底层传输端口
// 成功时返回bufferevent,失败则返回NULL

例:

struct event_base *base;
struct bufferevent *bev;

base = event_base_new();
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);

释放函数:

void bufferevent_free(struct bufferevent *bev);

设置回调函数

回调在创建的时候也可以设置。

void bufferevent_setcb(
    struct bufferevent *bufev,
    bufferevent_data_cb readcb,//使用 bufferevent_read()读取buff中数据信息
    bufferevent_data_cb writecb,//写回调只是提示你发生出去数据,没有实质作用				      
    bufferevent_event_cb eventcb, 				
    void *cbarg
;

操作数据

  • 向bufferevent的输出缓冲区添加数据
    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
    );
    

evconnlistener_new_bind

简介

evconnlistener其实是对event_base和event的封装而已。

链接监听器

使用函数evconnlistener_new_bind()进行绑定和监听
定义:

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

参数含义:

  • *base:base参数
  • cb:listener回调函数
  • ptr:自定义参数
  • flags:标志值(参见附录,LEV_OPT_*)
  • backlog:
  • *sa:服务端的sockaddr
  • socklen:上一个参数的长度 释放时使用vconnlistener_free()
    定义:
    void evconnlistener_free(struct evconnlistener *lev);
    

附录

bufferevent 定义

struct bufferevent {
    struct event_base *ev_base;

    //读事件
    struct event ev_read;
    //写事件
    struct event ev_write;
    //读缓冲区,输入缓冲
    struct evbuffer *input;
    //写缓冲区,输出缓冲
    struct evbuffer *output;

    //读水位
    struct event_watermark wm_read;
    //写水位
    struct event_watermark wm_write;

    //发生读触发用户设置的回调
    evbuffercb readcb;
    //发生写触发用户设置的回调
    evbuffercb writecb;
    //发生错误触发用户设置的回调
    everrorcb errorcb;
    //当前设置的回调函数传递的参数,和上面3个回调配合使用
    void *cbarg;

    //设置读超时时间,默认为0
    int timeout_read;    /* in seconds */
    //设置写超时时间,默认为0
    int timeout_write;    /* in seconds */

    //当前事件是否可用
    short enabled;    /* events that are currently enabled */
};
//水位
struct event_watermark {
    //低水位
    size_t low;
    //高水位
    size_t high;
};

bufferevent_cb 回调参数

参数|含义 –|– EV_EVENT_READING|读取操作时发生某事件,具体是哪种事件请看其他标志 BEV_EVENT_WRITING|写入操作时发生某事件,具体是哪种事件请看其他标志 BEV_EVENT_ERROR|操作时发生错误 BEV_EVENT_TIMEOUT|发生超时 BEV_EVENT_EOF|遇到文件结束指示 BEV_EVENT_CONNECTED|请求的连接过程已经完成实现客户端的时候可以判断

evconnlistener定义

//一系列的工作函数,因为listener可以用于不同的协议。
struct evconnlistener_ops {
    int (*enable)(struct evconnlistener *);
    int (*disable)(struct evconnlistener *);
    void (*destroy)(struct evconnlistener *);
    void (*shutdown)(struct evconnlistener *);
    evutil_socket_t (*getfd)(struct evconnlistener *);
    struct event_base *(*getbase)(struct evconnlistener *);
};

//一层一层封装,加上隔离
struct evconnlistener {
    const struct evconnlistener_ops *ops;   //操作函数
    void *lock;                             //锁变量,用于线程安全
    evconnlistener_cb cb;                   //用户的回调函数
    evconnlistener_errorcb errorcb;         //发生错误时的回调函数
    void *user_data;                        //回调函数的参数,当回调函数执行时候,通过形参传入回调函数内部
    unsigned flags;                         //属性标志 ,例如socket套接字属性,可以是阻塞,非阻塞,reuse等。
    short refcnt;                           //引用计数
    unsigned enabled : 1;                   //位域为1.即只需一个比特位来存储这个成员 
};
struct evconnlistener_event {
    struct evconnlistener base;
    struct event listener;     //内部event,插入到event_base,完成监听
};

listener 标志值

参数 含义
LEV_OPT_LEAVE_SOCKETS_BLOCKING 默认情况下,当连接监听器接收到新的客户端socket连接后,会把该socket设置为非阻塞的。如果设置该选项,那么就把客户端socket保留为阻塞的
LEV_OPT_CLOSE_ON_FREE 当连接监听器释放时,会自动关闭底层的socket
LEV_OPT_CLOSE_ON_EXEC 为底层的socket设置close-on-exec标志
LEV_OPT_REUSEABLE: 在某些平台,默认情况下当一个监听socket被关闭时,其他socket不能马上绑定到同一个端口,要等一会儿才行。设置该标志后,Libevent会把该socket设置成reuseable。这样,关闭该socket后,其他socket就能马上使用同一个端口
LEV_OPT_THREADSAFE 为连接监听器分配锁。这样可以确保线程安全

参考

libevent中的bufferevent原理
详解libevent网络库(一)—框架的搭建
详解libevent网络库(二)—即时聊天通讯
libevent绑定、监听和读写数据
使用bufferevent进行libevent服务端和客户端的开发 Libevent源码分析—–连接监听器evconnlistener
socket编程与libevent2的一些归纳总结


Similar Posts

Comments