首页libevent › libevent-2.0.21笔记

libevent-2.0.21笔记

参看http://www.wangafu.net/~nickm/libevent-book/ 和libevent-2.0.21源码

其中有部分我认为比较简单的,就没有记录在这里了。

关于源码,可以主要关注,evutil.c evbuffer.c event.c bufferevent.c bufferevent_sock.c 这几个文件。

——————————-笔记—————————
基础函数
用event_base_new 创建struct event_base *base ,
在bind,listen完后,
用event_new 创建struct event *listener_event用于监听是否有链接到达
通过event_add 将event添加到base中
在处理连接到达函数中添加读写event
event_base_dispatch(base),进行启动,其实就是个循环
event_free释放event

使用内置bufferevent,会自动收发数据,有点像preactor
从监听处理函数开始有变化,在监听处理函数中这么做:
struct bufferevent *bev;
evutil_make_socket_nonblocking(fd);
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, readcb, NULL, errorcb, NULL);//设置读写回调
bufferevent_setwatermark(bev, EV_READ, 0, MAX_LINE);
bufferevent_enable(bev, EV_READ|EV_WRITE);//设置bev关心的事件
之后只要在读回调函数中取到bufferevent内部的input,output指针
然后evbuffer_* 函数读取数据,而要发送数据只要将数据通过evbuffer_add添加到output,释放bufferevent 通过调用bufferevent_free

设置libevent
在complie libevent的时候指定CFLAGS=-DUSE_DEBUG可以取到底层更详细的log
log函数设置event_set_log_callback,log开关event_enable_debug_logging,log level标志要使用最新的,_开头的已被弃用
fatal error默认调用exit或abort,不过也可以用event_set_fatal_callback设置自己的fatal函数,不过要注意如果调用过自己的处理函数后,不能在调用libevent 任何函数

默认情况多线程是不支持的(即没有设置lock,condition等),如果要使libevent支持多线程,在event_base_new前面要调下面函数
int evthread_use_windows_threads(void);//win上设置
int evthread_use_pthreads(void); //unix上设置

如果lock有错误可以通过evthread_enable_lock_debugging进行捕捉,通过assert会exit
调试event使用event_enable_debug_mode
在使用bufferevent时,如果在创建bufferevent的时候,指定了BEV_OPT_THREADSAFE,
那么new bufferevent过程的内部函数会调用bufferevent_enable_locking和evbuffer_enable_locking
如果设置过了evthread_use_pthreads,那么bufferevent和从bufferevent取到的evbuffer都会变成线程安全
否则bufferevent和evbuffer是不会保证线程安全的,切记
进程退出前libevent_global_shutdown做清理,其实可以不用

创建event_base,觉得默认还是直接event_base_new要好点
struct event_config *event_config_new(void);
struct event_base *event_base_new_with_config(const struct event_config *cfg);
void event_config_free(struct event_config *cfg);
int event_config_avoid_method(struct event_config *cfg, const char *method);
int event_config_require_features(struct event_config *cfg,enum event_method_feature feature);

event_config_set_num_cpus_hint

event_base_free 不会清理关联在base上的event,socket!
event_base_priority_init可设置base优先级,如果优先级设置有限则关联进来的event优先级在0至EVENT_MAX_PRIORITIES -1
运行event loop
event_base_loop 如果设置了flag为EVLOOP_NONBLOCK 或者EVLOOP_ONCE 那么运行一次后就return了
event_base_dispatch 相当于flag是0,即event_base(base,0); 就可以持续运行除了发生错误或者是调用了event_base_loop
break() or event_base_loopexit()
区别:一个是直接exit loop,一个是在timeout事件exit loop;更改break和exit的说明
event_base_loopbreak() 加锁,将传入的base设置base->event_gotterm = 1;
event_base_loopexit() 注册了一个timeout事件,在timeout事件触发后调用回调函数设置base->event_gotterm = 1;
int event_base_got_exit(struct event_base *base); 获取退出状态
int event_base_got_break(struct event_base *base);

在2.1.2后会有event_base_loopcontinue,来继续运行退出后的base
event_base_gettimeofday_cached 获取系统时间,如果在执行回调,则取cache中预存的值,否则真正去调用并付给外部参数tv
event_base_dump_events 刷出base中的event列表到外部,这用于debug
event_base_foreach_event 2.1.2会提供一个遍历event的函数
event 工作
event_new 初始化一个event,但不插入到base中
event_free 即使event活跃或者在插入中也是安全的
#define EV_TIMEOUT 0x01
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10
#define EV_ET 0x20
默认pending的event在回调结束之后会变成non-pending,可以通过制定EV_PERSIST让事件持久化
event_self_cbarg 2.1.1 可用来在event_new中将正在创建的event传递给回调函数
如果要纯定时器,可以用evtimer_*这些宏快速创建
同样要捕捉singal,可以用evsignal_*这些宏快速创建
event_assign 做初始化,参数列表和event_new基本一样,除了是传递一个未初始化的event进去,而不像event_new从heap上返回一个event指针。
event_add
event_del 调用event_del的时候如果有event active但还没调用回调函数,则回调函数不会执行

event_base_priority_init 设置base优先级范围
event_priority_set 因为多个事件在同一时间触发的情况下,libevent不知道要调用哪个回调函数,因此可以针对event设置优先级,当同时触发,先执行高优先级的event的回调,再执行低优先级event的回调

判断event在哪种状态下是否pending或者active
int event_pending(const struct event *ev, short what, struct timeval *tv_out);
获取之前event_new的时候设置进去的参数
#define event_get_signal(ev) /* … */
evutil_socket_t event_get_fd(const struct event *ev);
struct event_base *event_get_base(const struct event *ev);
short event_get_events(const struct event *ev);
event_callback_fn event_get_callback(const struct event *ev);
void *event_get_callback_arg(const struct event *ev);
event_get_assignment copy event中已经赋值过的字段到外部传递进去的变量中
event_base_once 做一次性event添加,而用户不用控制创建释放之类的,且不支持EV_SIGNAL or EV_PERSIST
在2.0,如果event没有被触发,则不会释放内存,在2.1.2如果base释放的时候会即使event不活跃也会被释放
event_active 在某些情况下,即使事件没被触发,但需要的让其变成被触发,可以调用这

这三个初始化的相当于bzero样的功能,不建议使用
int event_initialized(const struct event *ev);
#define evsignal_initialized(ev) event_initialized(ev)
#define evtimer_initialized(ev) event_initialized(ev)

有关evutil_*函数的,可以看evutil.c,一般看看函数名就知道函数作用了

bufferevents 基本概念
bufferevents 相当于数据已被读取到buffer,但要注意目前只支持(面向流的协议)tcp,未来可能支持udp
有四种类型的bufferevents,但需要注意,不是所有bufferevent的接口都可以工作在这些类型上的。
基于socket的bufferevents 底层基于event_* 接口来实现
异步io的bufferevents 只支持windows iocp
filtering buffervents 处理输入输出数据前将数据传递给相关的bufferevent对象,没用过不好说
paired bufferevents 将bufferevents从一个传递到另一个
在设置bufferevent callback的时候,有一个event callback可以设置,如连接关闭,发送错误之类的,都会调用event callback
,event flag有这几种:
BEV_EVENT_READING
BEV_EVENT_WRITING
BEV_EVENT_ERROR
BEV_EVENT_TIMEOUT
BEV_EVENT_EOF
BEV_EVENT_CONNECTED
创建bufferevent,其中fd可以是-1,那之后调用bufferevent_socket_connect_hostname就可以
struct bufferevent *bufferevent_socket_new(
struct event_base *base,
evutil_socket_t fd,
enum bufferevent_options options);

libevent提供的connect,可以再setcb阶段设置event callbak取event flag判断是否connected
int bufferevent_socket_connect(struct bufferevent *bev,
struct sockaddr *address, int addrlen);

需要用到dns_base
int bufferevent_socket_connect_hostname(struct bufferevent *bev,
struct evdns_base *dns_base, int family, const char *hostname,
int port);
在event callback的阶段如果event flag是error,那么之前调用connect_hostname的时候dns解析不到hostname而失败
int bufferevent_socket_get_dns_error(struct bufferevent *bev);

data_cb 其实read 和 write callback的类型
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
这个是event callback,会多一个event flag参数
typedef void (*bufferevent_event_cb)(struct bufferevent *bev,
short events, void *ctx);

需要注意的时候readcb,writecb,eventcb都是共用一个cbarg的,即使多次调用更改了一个,也会影响其他的
void bufferevent_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb, bufferevent_data_cb writecb,
bufferevent_event_cb eventcb, void *cbarg);
这个估计都不怎么用的到
void bufferevent_getcb(struct bufferevent *bufev,
bufferevent_data_cb *readcb_ptr,
bufferevent_data_cb *writecb_ptr,
bufferevent_event_cb *eventcb_ptr,
void **cbarg_ptr);
设置哪些事件允许,哪些不允许,只有两个EV_READ EV_WRITE,如果两个都禁止则bufferevent 不会去尝试读写
void bufferevent_enable(struct bufferevent *bufev, short events);
void bufferevent_disable(struct bufferevent *bufev, short events);
short bufferevent_get_enabled(struct bufferevent *bufev);

设置low_watermark
void bufferevent_setwatermark(struct bufferevent *bufev, short events,
size_t lowmark, size_t highmark);

返回bufferevent内部的input或output的evbuffer类型的指针
struct evbuffer *bufferevent_get_input(struct bufferevent *bufev);
struct evbuffer *bufferevent_get_output(struct bufferevent *bufev);

写数据到bufferevent的output buffer中
int bufferevent_write(struct bufferevent *bufev,
const void *data, size_t size);
int bufferevent_write_buffer(struct bufferevent *bufev,
struct evbuffer *buf);

读数据从bufferevent的input buffer到外部,第一个函数返回实际存到data中的字节数
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
int bufferevent_read_buffer(struct bufferevent *bufev,
struct evbuffer *buf);

这个再2.1.2之前并不是所有bufferevent 的type都能用
void bufferevent_set_timeouts(struct bufferevent *bufev,
const struct timeval *timeout_read, const struct timeval *timeout_write);

之前有说过如果在bufferevent_socket_new的时候如果fd设置了-1,那么现在还可以通过setfd在后面指定
int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd);
evutil_socket_t bufferevent_getfd(struct bufferevent *bufev);

要在bufferevent创建的时候指定了BEV_OPT_THREADSAFE才有效,供bufferevent多线程访问时候使用,bufferevent_*内部用
void bufferevent_lock(struct bufferevent *bufev);
void bufferevent_unlock(struct bufferevent *bufev)

bufferevent 高级主题
这个没有看,其中包括了filter , paired 两种类型bufferevent,读写速率控制,使用ssl

evbuffers buffered io 的实用性函数
evbuffer 只关注buffer部分,不涉及io就绪触发,io调度之类的.
struct evbuffer *evbuffer_new(void);
void evbuffer_free(struct evbuffer *buf);

默认 evbuffer不是线程安全的,如果需要多线程访问需要先调用enable_locking,lock是NULL的时候,则创建(递归锁),evbuffer_* 内部用
int evbuffer_enable_locking(struct evbuffer *buf, void *lock);
void evbuffer_lock(struct evbuffer *buf);
void evbuffer_unlock(struct evbuffer *buf);

返回内存存了多少bytes
size_t evbuffer_get_length(const struct evbuffer *buf);
返回evbuffer中的第一个chunk存了多少个bytes
size_t evbuffer_get_contiguous_space(const struct evbuffer *buf);

添加数据到evbuffer
int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen);
int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, …)
int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap);

扩展evbuffer,如果最后一个chunk空闲内存够则不操作,否则会新分配一个chunk,大致是这意思
int evbuffer_expand(struct evbuffer *buf, size_t datlen);

移动数据从一个evbuffer到另外一个evbuffer。第二个返回实际移动的字节数
int evbuffer_add_buffer(struct evbuffer *dst, struct evbuffer *src);
int evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst,
size_t datlen);

插入数据到evbuffer 开头。使用这两个函数要小心,并且不要将evbuffer共享到bufferevent
int evbuffer_prepend(struct evbuffer *buf, const void *data, size_t size);
int evbuffer_prepend_buffer(struct evbuffer *dst, struct evbuffer* src);

让evbuffer的开头有多少个字节的连续内存,并返回执行内存中连续数组的指针
unsigned char *evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size);

都是从evbuffer头部移除指定个字节
int evbuffer_drain(struct evbuffer *buf, size_t len);
int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen);

从evbuffer的开头copy数据出来,第二个从指定位置copy(2.1.1出现),如果速度慢可以使用evbuffer_peek替代
ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data, size_t datlen);
ev_ssize_t evbuffer_copyout_from(struct evbuffer *buf,
const struct evbuffer_ptr *pos,
void *data_out, size_t datlen);

按行读取,返回指向行的指针,需要自己释放内存
char *evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out,
enum evbuffer_eol_style eol_style);

pos可访问
struct evbuffer_ptr {
ev_ssize_t pos;
struct {
/* internal fields */
} _internal;
};

需要注意,任何修改evbuffer都会导致之前返回的evbuffer_ptr失效
这三个函数是用来从evbuffer中找匹配项,并返回位置
struct evbuffer_ptr evbuffer_search(struct evbuffer *buffer,
const char *what, size_t len, const struct evbuffer_ptr *start);
struct evbuffer_ptr evbuffer_search_range(struct evbuffer *buffer,
const char *what, size_t len, const struct evbuffer_ptr *start,
const struct evbuffer_ptr *end);
struct evbuffer_ptr evbuffer_search_eol(struct evbuffer *buffer,
struct evbuffer_ptr *start, size_t *eol_len_out,
enum evbuffer_eol_style eol_style);

how分SET和ADD,SET情况下是将pos移动到position位置,ADD的时候要保证pos不为空,这时,会将pos向前移动position个字节
int evbuffer_ptr_set(struct evbuffer *buffer, struct evbuffer_ptr *pos,
size_t position, enum evbuffer_ptr_how how);

struct evbuffer_iovec {
void *iov_base;
size_t iov_len;
};

相当于将chunk的指针赋值给vec_out,这个要具体用过才好判断
int evbuffer_peek(struct evbuffer *buffer, ev_ssize_t len,
struct evbuffer_ptr *start_at,
struct evbuffer_iovec *vec_out, int n_vec);

同样,操作时不要在reserve_space之后更改evbuffer,如果要加数据,可以存储在vec[i]->iov_base中
同理不要在commit_space之前将vec[i]内部的指针指向其他地方。
更改空间大小,n_vecs不能大于2。相当于将evbuffer内部有指针赋在了vec数组中
int evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size,
struct evbuffer_iovec *vec, int n_vecs);
如果vec中数据填充后,直接commit就可以了
int evbuffer_commit_space(struct evbuffer *buf,
struct evbuffer_iovec *vec, int n_vecs);

int evbuffer_write(struct evbuffer *buffer, evutil_socket_t fd);
int evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd,
ev_ssize_t howmuch);
int evbuffer_read(struct evbuffer *buffer, evutil_socket_t fd, int howmuch);

通过引用加数据挂到evbuffer
typedef void (*evbuffer_ref_cleanup_cb)(const void *data,
size_t datalen, void *extra);
int evbuffer_add_reference(struct evbuffer *outbuf,
const void *data, size_t datlen,
evbuffer_ref_cleanup_cb cleanupfn, void *extra);

走网络发送文件,避免copy数据到用户控件,注意:在2.0.*版本,发送到网络只能用evbuffer_write*(), drain it with evbuffer_drain(), or move it to another evbuffer with evbuffer_*_buffer().
int evbuffer_add_file(struct evbuffer *output, int fd, ev_off_t offset,
size_t length);

还有其他一些文件粒度控制的函数,都是以evbuffer_file_*,有需要可以再去查看

evbuffer间的引用
int evbuffer_add_buffer_reference(struct evbuffer *outbuf,
struct evbuffer *inbuf);

连接监听
struct evconnlistener *evconnlistener_new(struct event_base *base,
evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
evutil_socket_t fd);
struct evconnlistener *evconnlistener_new_bind(struct event_base *base,
evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
const struct sockaddr *sa, int socklen);
void evconnlistener_free(struct evconnlistener *lev);

可以重置callback
void evconnlistener_set_cb(struct evconnlistener *lev,
evconnlistener_cb cb, void *arg);

在listener中也可以设置error callback
typedef void (*evconnlistener_errorcb)(struct evconnlistener *lis, void *ptr);
void evconnlistener_set_error_cb(struct evconnlistener *lev,
evconnlistener_errorcb errorcb);

发表评论