这一章节讲述一些bufferevent的高级特性,对初学者来说并不是必须的。如果你刚刚学习如何使用bufferevent,可以跳过本章节。

成对的bufferevent

有时,你需要编写一个与自己通信的网络程序。例如,您可以编写一个程序来通过某个协议建立隧道用户连接,而该程序有时也希望通过该协议建立自己的连接隧道。当然,您可以通过打开与您自己的侦听端口的连接并让您的程序使用自己来实现这一点,但是,让您的程序通过网络堆栈与自己对话会浪费资源。

相反,您可以创建一对成对的bufferevent,以便写入一个的所有字节都被另一个接收(反之亦然),但不使用socket。

int bufferevent_pair_new(struct event_base *base, int options,
    struct bufferevent *pair[2]);

调用bufferevent_pair_new()pair[0]pair[1]设置为一对bufferevent,每个都连接到另一个,除BEV_OPT_CLOSE_ON_FREE外,支持所有常用的选项,并且BEV_OPT_DEFER_CALLBACKS标志位始终打开。

为什么 成对的bufferevent 需要在回调延迟的情况下运行?对一对元素中的一个元素的操作调用改变 bufferevent 的回调是很常见的,从而调用另一个 bufferevent 的回调,等等通过许多步骤。当回调没有延迟时,这个调用链会经常溢出堆栈,使其他连接饿死,并要求所有回调都是可重入的。

成对的缓冲事件支持刷新;将 mode 参数设置为 BEV_NORMALBEV_FLUSH 强制所有相关数据从一对缓冲区事件传输到另一个缓冲区事件,忽略否则会限制它的水印。将 mode 设置为 BEV_FINISHED 还会在相反的 bufferevent 上生成一个 EOF 事件。

释放对中的任何一个成员不会自动释放另一个或生成 EOF 事件;它只会使配对中的另一个成员解除链接。一旦 bufferevent 被取消链接,它将不再成功读取或写入数据或生成任何事件。

struct bufferevent *bufferevent_pair_get_partner(struct bufferevent *bev)

有时你想通过成对的bufferevent中的一个来获取另一个,你可以调用bufferevent_pair_get_partner()函数来达到此目的。如果成对中的另一个不存在了,则返回NULL。

过滤的bufferevent

有时您想转换通过 bufferevent 对象的所有数据。您可以这样做以添加压缩层,或将协议包装在另一个协议中以进行传输。

enum bufferevent_filter_result {
        BEV_OK = 0,
        BEV_NEED_MORE = 1,
        BEV_ERROR = 2
};
typedef enum bufferevent_filter_result (*bufferevent_filter_cb)(
    struct evbuffer *source, struct evbuffer *destination, ev_ssize_t dst_limit,
    enum bufferevent_flush_mode mode, void *ctx);


struct bufferevent *bufferevent_filter_new(struct bufferevent *underlying,
        bufferevent_filter_cb input_filter,
        bufferevent_filter_cb output_filter,
        int options,
        void (*free_context)(void *),
        void *ctx);

bufferevent_filter_new() 函数创建一个新的过滤的bufferevent,包裹着现有的“底层”bufferevent。通过底层的bufferevent接收的所有数据在到达过滤的bufferevent之前用“输入”过滤器进行转换,通过过滤的bufferevent发送的所有数据在发送到底层的bufferevent之前用“输出”过滤器进行转换。

将过滤器添加到底层 bufferevent 会替换底层 bufferevent 上的回调。您仍然可以向底层 bufferevent 的 evbuffers 添加回调,但是如果您希望过滤器仍然工作,则不能在 bufferevent 本身上设置回调。

input_filter 和 output_filter 函数如下所述。选项中支持所有常用选项。如果设置了 BEV_OPT_CLOSE_ON_FREE,那么释放过滤bufferevent也会释放底层bufferevent。 ctx 字段是传递给过滤器函数的任意指针;如果提供了 free_context 函数,它会在过滤缓冲区事件关闭之前在 ctx 上调用。

只要底层输入缓冲区上有新的可读数据,就会调用输入过滤器函数。只要过滤器的输出缓冲区中有新的可写数据,就会调用输出过滤器函数。每个都有一对 evbuffer:一个用于读取数据的源 evbuffer 和一个用于写入数据的目标 evbuffer。 dst_limit 参数描述要添加到目标的字节上限。过滤器函数可以忽略这个值,但这样做可能会违反高水位线或速率限制。如果 dst_limit 为 -1,则没有限制。 mode 参数告诉过滤器在写作中的积极程度。如果是BEV_NORMAL,那么它应该尽可能多地写,可以方便地转换。 BEV_FLUSH 值意味着尽可能多地写入,而 BEV_FINISHED 意味着过滤函数应该在流的末尾额外进行任何必要的清理。最后,过滤器函数的 ctx 参数是一个空指针,提供给 bufferevent_filter_new() 构造函数。

如果任何数据成功写入目标缓冲区,过滤器函数必须返回 BEV_OK,如果没有更多的数据可以写入目标缓冲区而没有更多的输入或使用不同的刷新模式,则返回 BEV_NEED_MORE,如果出现不可恢复的错误,则返回 BEV_ERROR。

创建过滤器可以对底层bufferevent进行读取和写入。您不需要自己管理读/写:过滤器将在不想读取时为您暂停对底层 bufferevent 的读取。对于 2.0.8-rc 及更高版本,允许启用/禁用独立于过滤器的底层 bufferevent 的读取和写入。但是,如果您这样做,您可能会阻止过滤器成功获取它想要的数据。

您不需要同时指定输入过滤器和输出过滤器:您省略的任何过滤器都将替换为不转换数据而直接传递数据的过滤器。

限制单次读、写最大值

默认情况下,bufferevents 不会在每次调用事件循环时读取或写入最大可能的字节数;这样做会导致奇怪的不公平行为和资源匮乏。另一方面,默认值可能不适用于所有情况。

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

ev_ssize_t bufferevent_get_max_single_read(struct bufferevent *bev);
ev_ssize_t bufferevent_get_max_single_write(struct bufferevent *bev);

这两个“set”函数分别替换了当前的读写最大值。如果大小值为 0 或高于 EV_SSIZE_MAX,则将最大值设置为默认值。这些函数在成功时返回 0,在失败时返回 -1。

两个“get”函数分别返回当前每个循环的读取和写入最大值。

限速

一些程序想要限制用于任何单个bufferevent或一组bufferevent的带宽量。 Libevent 2.0.4-alpha 和 Libevent 2.0.5-alpha 添加了一个基本工具来设置单个bufferevent的上限,或者将bufferevent分配给一个速率受限的组。

限速模式

Libevent 的速率限制使用令牌桶算法来决定一次读取或写入多少字节。每个受速率限制的对象在任何给定时间都有一个“读桶”和一个“写桶”,它们的大小决定了允许对象立即读取或写入的字节数。每个桶都有一个重新填充率、一个最大突发大小和一个计时单位或“滴答”。每当计时单位结束时,桶会根据重新填充率按比例重新填充——但如果它变得比其突发大小更满,则任何多余的字节都会丢失。

因此,重新填充速率决定了对象发送或接收字节的最大平均速率,而突发大小决定了在单个突发中将发送或接收的最大字节数。计时单位决定了发送的顺畅程度。

#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 结构表示一对令牌桶的配置值,用于限制对单个bufferevent或bufferevent组的读写。调用 ev_token_bucket_cfg_new 函数并提供最大平均读取速率、最大读取突发、最大写入速率、最大写入突发和滴答的长度。如果 tick_len 参数为 NULL,则刻度的长度默认为一秒。该函数可能会在出错时返回 NULL

请注意, read_ratewrite_rate 参数以每个滴答的字节为单位进行缩放。也就是说,如果滴答是十分之一秒,而 read_rate 是 300,那么最大平均读取速率是每秒 3000 字节。不支持超过 EV_RATE_LIMIT_MAX 的速率和突发值。

要限制bufferevent的传输速率,请使用 ev_token_bucket_cfg 对其调用 bufferevent_set_rate_limit()。该函数在成功时返回 0,在失败时返回 -1。您可以为任意数量的bufferevent提供相同的 ev_token_bucket_cfg。要删除 bufferevent 的速率限制,请调用 bufferevent_set_rate_limit(),为 cfg 参数传递 NULL。

要释放 ev_token_bucket_cfg,请调用 ev_token_bucket_cfg_free()。请注意,在没有缓冲区事件使用 ev_token_bucket_cfg 之前,目前这样做是不安全的。

以下内容暂时用不到,先不翻译。

标签: libevent

添加新评论