5

本文地址:https://segmentfault.com/a/1190000005601925


Libevent的辅助函数和数据类型

头文件是<event2/util.h>。以下只列出我自己会用到的部分。

基本类型

evutil_socket_t
Socket的抽象。除了Windows之外,其他系统都是一个int类型。如果考虑Windows的兼容性的话,建议用这个类型。

标准整型

以下是几种数据长度的定义

    ----------------------------------------------------------
       Type         位宽   符号数     最大值          最小值
    ----------------------------------------------------------
    ev_uint64_t      64      x    EV_UINT64_MAX        0
    ev_int64_t       64      √    EV_INT64_MAX    EV_INT64_MIN
    ev_uint32_t      32      x    EV_UINT32_MAX        0
    ev_int32_t       32      √    EV_INT32_MAX    EV_INT32_MIN
    ev_uint16_t      16      x    EV_UINT16_MAX        0
    ev_int16_t       16      √    EV_INT16_MAX    EV_INT16_MIN
    ev_uint8_t       8       x    EV_UINT8_MAX         0
    ev_int8_t        8       √    EV_INT8_MAX     EV_INT8_MIN

其他一些类型
ev_ssize_t
ev_off_t

适配函数的宏

#define evutili_timer_add(tvp, uvp, vvp)
#define evutili_timer_sub(tvp, uvp, vvp)

计算timeval数据加减的宏,vvp = tvp +/- uvp。注意三者都要使用指针

#define evutil_timerclear(tvp)
#define evutil_timerisset(tvp)

将timeval清零,或者判断是否被清零

#define evutil_timercmp(tvp, uvp, cmp)
判断timeval的先后,其中cmp是比较富豪,比如==, <=, >=, <, >, !=

int evutil_gettimeofday (struct timeval *tv, struct timezone *tz);

Socket相关的函数

#define evutil_socket_geterror (sock)
#define evutil_socket_error_to_string (errcode)

获得指定socket的error code,以及转为可读的string

int evutil_make_socket_nonblocking (evutil_sopcket_t sock);

将一个socket非阻塞。

字符串操作

ev_int64_t evutil_strtoll (const char *s, char **endptr, int base);
int evutil_snprintf (char *but, size_t buflen, const char *format, ...);
int evutil_vsnprintf (char *bug, size_t buflen, const char *format, va_list ap);

数据结构体

#define evutil_offsetof (type, field)

Bufferevent:概念和基本知识

传统的libevent使用方法:

  1. 当需要放数据的时候,存入数据到buffer
  2. 等待socket可写
  3. 尽量向socket中写更多的data
  4. 如果还有data未写入,则再等待socket可写

使用头文件<event2/bufferevent.h>可以使用bufferevent,节省read/write调用,只需要将数据放入/取出一个buffer即可
  目前bufferevent只支持TCP,未来可能支持UDP
  每个bufferevent有一个read buffer和一个write buffer,都是struct evbuffer。这个后文再讲。

回调和bufferevent

Bufferevent使用叫做watermarks(水位线)的东西来定义回调函数的调用时机。有以下几个watermarks:
  Read low-water mark:当read buffer的量大于等于这么多时,调用callback。默认是0,即一有数据就回调。
  Read high-water mark:当read buffer的量大于等于这么多时,停止read,直到buffer里面的数据低于这个值为止,重新开始read。默认是无限。
  Write low-water mark:当write buffer的量小于等于这么多时,调用回调。默认是0
  Write high-water mark:bufferevent未直接使用这个值。参见后文

Bufferevent也有错误回调事件回调,用于告知一些非数据时间和错误。如下:
  BEV_EVENT_READING
  BEV_EVENT_WRITING
  BEV_EVENT_ERROR:操作发生错误。需要调用EVUTIL_SOCKET_ERROR()来判断出现了什么错误
  BEV_EVENT_TIMEOUT
  BEV_EVENT_EOF
  BEV_EVENT_CONNECTED:请求连接已经完成

延迟回调
一般情况下,bufferevent的callback时立刻调用的。但是如果调用关系很复杂的话可能会出bug,这个时候可以将bufferevent设置为延迟的(defered),这样会使得回调函数放在event loop中被执行,单线程。

Bufferevent的选项

BEV_OPT_CLOSE_ON_FREE:当bufferevent释放时,关闭底层传输
BEV_OPT_THREADSAFE:为bufferevent使用lock
BEV_OPT_DEFER_CALLBACKS:将callback设为延迟的
BEV_OPT_UNLOCK_CALLBACKS:默认情况下如果有THREADSAFE标志,调用callback时会加锁。使用这个标志是的即便有THREADSAFE标志,调用callback也不加锁

使用基于socket的bufferevent

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

这里的fd可以不指定,此时fd的参数是-1。如果指定了fd,这个fd必须是已经nonblock的。

int bufferevent_socket_connect (struct bufferevent *bev,
                                struct sockaddr    *address,
                                int                 addrlen);

这是对connect()的封装。如果bev的fd是-1,那么会自动调用socket(),并且设置nonblock。,随后再异步调用connect();如果fd已经指定了,那么只是告诉bev去做connect()操作。
  正常情况下,这会引起BEV_EVENT_CONNECTED回调

int bufferevent_socket_connect_hostname (
                struct bufferevent *bev,
                struct event_base  *dns_base,
                int                 family,
                const char         *hostname,
                int                 port);

这是connect()封装的另一个版本,但是目标改为hostname。这会导致bufferevent自动去解析DNS。其中family可选以下值:AF_INET, AF_INET6, AF_UNSPEC
  dns_base参数可选。如果是NULL,那么bufferevent会一直阻塞直到DNS解析完成——当然不推荐这么做。如果带了参数,则libevent会异步处理DNS请求。
  剩下的工作与上面的connect封装相同。

int bufferevent_socket_get_dns_error (struct bufferevent *bev);

通用的bufferevent操作

void bufferevent_free (struct bufferevent *bev);

释放bfferevent。如果callback是defered的,那么bufferevent会等到callback返回之后才释放。
如果指定了BEV_OPT_CLOSE_ON_FREE,那么socket也会被close掉。

typedef void (*bufferevent_data_cb) (struct bufferevent *bev, void *ctx);
typedef void (*bufferevent_event_cb) (struct bufferevent *bev, short events, void *ctx);
void buffevent_setcb (struct buffevent    *bufev,
                      bufferevent_data_cb  readcb,
                      bufferevent_data_cb  writecb,
                      bufferevent_event_cb eventcb,
                      void                *cbarg);
void bufferevent_get_cb (struct buffevent     *bufev,
                         bufferevent_data_cb  *readcb_ptr,
                         bufferevent_data_cb  *writecb_ptr,
                         bufferevent_event_cb *eventcb_ptr,
                         void                **cbarg_ptr);

设置。获取bufferevent的callback。如果不想使用某个callback,则传入NULL。

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

使能/禁用指定的的callback。默认情况下,刚初始化的bufferevent,write使能,而read禁止。

void bufferevent_setwatermark (struct buffevent *bev,
                               short             events,
                               size_t            lowmark,
                               size_t            highmark);

设置watermark。对于high-watermark,0表示无限。

struct evbuffer *bufferevent_get_input (struct bufferevent *bev);
struct evbuffer *bufferevent_get_output(struct bufferevent *bev);

获取到bufferevent中对应的read/write buffer。

int bufferevent_write (struct bufferevent *ev, const void *data, size_t size);
int bufferevent_write_buffer (struct bufferevent *bev, struct evbuffer *buf);

函数一:直接向bufferevent附加数据
函数二:将evbuffer的全部内容附加到bufferevent中并晴空evbuffer

size_t bufferevent_read (struct bufferevent *bev, void *data, size_t size);
int bufferevent_read_buffer (struct bufferevent *bev, struct evbuffer *buf);

函数一:直接从bufferevent中读出数据,返回数据长度
函数二:将bufferevent中的全部数据抽取到evbuffer中

void bufferevent_set_timeouts (struct bufferevent  *bev,
                               const struct timeval *timeout_read,
                               const struct timeval *timeout_write);

设置timeout,使得当一段时间没有数据时,触发回调函数。此时的实践中会包含 BEV_EVENT_TIMEOUT

int bufferevent_flush (struct bufferevent *bufev,
                       short               iotype,
                       enum bufferevent_flush_mode state);

强制读/写尽可能多的数据。这个函数目前对socket没有作用。

类型特定的bufferevent函数

以下几个函数的含义正如字面意思,就不特别说明了

int bufferevent_priority_set (struct bufferevent *bev, int pri);
int bufferevent_get_priority (struct bufferevent *bev);

int bufferevent_setfd (struct bufferevent *bev, evutil_socket_t fd);
evutil_socket_t bufferevent_getfd (struct bufferevent *bev);

struct event_base *bufferevent_get_base (struct bufferevent *bev);
struct bufferevent *bufferevent_get_underlying (struct bufferevent *bev);

void bufferevent_lock   (struct bufferevent *bev);
void buyfferevent_unlock(struct bufferevent *bev);

Bufferevents:高级主题

这里讲了很多bufferevent的高级功能。本文章只是列出其中会使用到的部分

限制每次read/write的长度

int bufferevent_set_max_single_read (struct bufferevent *bev, size_t size);
int bufferevent_set_max_single_write(struct bufferevent *bev, size_t size);

同时也有相对应的get函数

速率(带宽)限制

#define EV_RATE_LIMIT_MAX    EV_SSIZE_MAX
struct ev_token_bucket_cfg;
struct ev_token_bucket_cfg *ev_token_bucket_cfg_new (
                                size_t read_rate,     size_t read_burst,
                                size_t write_rate,    size_t write_burst,
                                const struct timeval *tick_len);
void ev_token_bucket_cfg_free (struct ev_token_bucket_cfg *cfg);
int bufferevent_set_rate_limit (struct bufferevent         *bev,
                                struct ev_token_bucket_cfg *cfg);

其中ev_token_bucket_cfg_new()的前四个函数的单位均为bytes/tick,而tick的单位由tick_len指定。如果tick_len为NULL,那么默认为1秒。

Bufferevents 和 SSL

这里其实是一个很重要的内容,讲的是如何在bufferevent中使用SSL。Libevent将SSL深度耦合了进来,使得你可以很方便地使用bufferevent来完成SSL通信。
  呃,缺点就是libevent和OpenSSL的缺点的集合。其实对于嵌入式开发来说,因为CPU是分立的,所以性能上的缺点并不明显,最大的问题是占用磁盘空间啊!Libevent的库本身就不小,加上OpenSSL更是超大。我弄懂libevent的时候,我们的系统已经准备改用其他的异步I/O和SSL库,所以我也就不看了
  另外吐槽一下:我们这么多年了还是没时间把libevent和OpenSSL完全替换的工作做完,在这期间我自己都把libev、libuv、PolarSSL(mbedTLS)、cyaSSL看了……

系列篇

Libevent官方文档学习笔记(1. libevent_core部分)
Libevent官方文档学习笔记(2. bufferevent部分)(本文)
Libevent官方文档学习笔记(3. evbuffer部分)


amc
927 声望228 粉丝

电子和互联网深耕多年,拥有丰富的嵌入式和服务器开发经验。现负责腾讯心悦俱乐部后台开发