http://blog.sina.com.cn/s/blog_56dee71a0100qx4s.html

很多时候,除了响应事件之外,应用还希望做一定的数据缓冲。比如说,写入数据的时候,通常的运行模式是:

l 决定要向连接写入一些数据,把数据放入到缓冲区中

l 等待连接可以写入

l 写入尽量多的数据

l 记住写入了多少数据,如果还有更多数据要写入,等待连接再次可以写入

这种缓冲IO模式很通用,libevent为此提供了一种通用机制,即bufferevent。bufferevent由一个底层的传输端口(如套接字),一个读取缓冲区和一个写入缓冲区组成。与通常的事件在底层传输端口已经就绪,可以读取或者写入的时候执行回调不同的是,bufferevent在读取或者写入了足够量的数据之后调用用户提供的回调。

bufferevent 的简单范例

这里选取了 Libevent 的一个范例程序 hello-world.c 来看看 Libevent 的用法:

#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifndef WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
# include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif // bufferevent
#include <event2/bufferevent.h>
// bufferevent 使用的 buffer
#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_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;
#ifdef WIN32
WSADATA wsa_data;
WSAStartup(0x0201, &wsa_data);
#endif // 首先构建 base
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); // 创建监听器
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;
} // 中断信号
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; // 得到一个新的连接,通过连接 fd 构建一个 bufferevent
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
fprintf(stderr, "Error constructing bufferevent!");
event_base_loopbreak(base);
return;
}
// 设置创建的 bufferevent 的回调函数
bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
bufferevent_enable(bev, EV_WRITE);
bufferevent_disable(bev, EV_READ); // 写入数据到 bufferevent 中
bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
} static void
conn_writecb(struct bufferevent *bev, void *user_data)
{
struct evbuffer *output = bufferevent_get_output(bev);
if (evbuffer_get_length(output) == 0) {
printf("flushed answer\n");
bufferevent_free(bev);
}
} 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 }; printf("Caught an interrupt signal; exiting cleanly in two seconds.\n"); // 停止事件循环
event_base_loopexit(base, &delay);
}

研究 bufferevent 的关键代码

这里只研究基于 socket 的 bufferevent。从上面 bufferevent 的使用可以看出,有几个关键函数:

  1. 开始需要调用 bufferevent_socket_new 创建一个 bufferevent
  2. 调用 bufferevent_setcb 设置回调函数
  3. 调用 bufferevent_write 写入数据
  4. 调用 bufferevent_free 释放 bufferevent

bufferevent_socket_new 的源码以及分析如下:

// base --- 新创建的 bufferevent 关联的 base
// fd --- bufferevent 关联的文件描述符
struct bufferevent *
bufferevent_socket_new(struct event_base *base, evutil_socket_t fd,
int options)
{
// bufferevent_private 结构体持有 bufferevent 的数据
struct bufferevent_private *bufev_p;
// bufev == &(bufev_p->bev);
// struct bufferevent 中存放的是不同类型的 bufferevent 所共有的部分
// struct bufferevent 是 struct bufferevent_private 的子集
struct bufferevent *bufev; // windows 下如果启用 IOCP 则构建异步 IO bufferevent
#ifdef WIN32
if (base && event_base_get_iocp(base))
// 细节略
return bufferevent_async_new(base, fd, options);
#endif if ((bufev_p = mm_calloc(1, sizeof(struct bufferevent_private)))== NULL)
return NULL; // 初始化 bufferevent_private
// 由于 bufferevent 有不同类型,所以这里设计了 bufferevent_ops_socket
// 对于不同类型的 bufferevent 有不同的 bufferevent_ops_socket 对象
// bufferevent_ops_socket 包括函数指针和一些信息
if (bufferevent_init_common(bufev_p, base, &bufferevent_ops_socket,
options) < 0) {
mm_free(bufev_p);
return NULL;
}
bufev = &bufev_p->bev;
// 设置 EVBUFFER_FLAG_DRAINS_TO_FD,此选项和 evbuffer_add_file() 函数有关(详见文档)
evbuffer_set_flags(bufev->output, EVBUFFER_FLAG_DRAINS_TO_FD); // 初始化 read 和 write event
// 一个 bufferevent(一个 fd)关联两个 event 对象 ev_read 和 ev_write
// ev_read --- socket 可读或者超时
// ev_write --- socket 可写或者超时
// 它们都未使用 Edge triggered 方式
event_assign(&bufev->ev_read, bufev->ev_base, fd,
EV_READ|EV_PERSIST, bufferevent_readcb, bufev);
event_assign(&bufev->ev_write, bufev->ev_base, fd,
EV_WRITE|EV_PERSIST, bufferevent_writecb, bufev); // 为输出缓冲区设置回调
// 当输出缓冲区被修改时调用 bufferevent_socket_outbuf_cb 回调函数
evbuffer_add_cb(bufev->output, bufferevent_socket_outbuf_cb, bufev); // 防止输入缓冲区和输出缓冲区被意外修改
evbuffer_freeze(bufev->input, 0);
evbuffer_freeze(bufev->output, 1); return bufev;
}

其中 bufferevent_init_common 函数实现为:

int
bufferevent_init_common(struct bufferevent_private *bufev_private,
struct event_base *base,
const struct bufferevent_ops *ops,
enum bufferevent_options options)
{
struct bufferevent *bufev = &bufev_private->bev; // 创建输入缓冲区
if (!bufev->input) {
if ((bufev->input = evbuffer_new()) == NULL)
return -1;
} // 创建输出缓冲区
if (!bufev->output) {
if ((bufev->output = evbuffer_new()) == NULL) {
evbuffer_free(bufev->input);
return -1;
}
} // 初始化 bufferevent 的引用计数
bufev_private->refcnt = 1;
bufev->ev_base = base; /* Disable timeouts. */
// 清理超时时间
evutil_timerclear(&bufev->timeout_read);
evutil_timerclear(&bufev->timeout_write); bufev->be_ops = ops; /*
* Set to EV_WRITE so that using bufferevent_write is going to
* trigger a callback. Reading needs to be explicitly enabled
* because otherwise no data will be available.
*/
// enabled 被 bufferevent_get_enabled 函数返回
// enabled 的值可以为 EV_WRITE EV_READ
bufev->enabled = EV_WRITE; // bufferevent 相关线程初始化
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
if (options & BEV_OPT_THREADSAFE) {
if (bufferevent_enable_locking(bufev, NULL) < 0) {
/* cleanup */
evbuffer_free(bufev->input);
evbuffer_free(bufev->output);
bufev->input = NULL;
bufev->output = NULL;
return -1;
}
}
#endif
// 选项正确性检查
if ((options & (BEV_OPT_DEFER_CALLBACKS|BEV_OPT_UNLOCK_CALLBACKS))
== BEV_OPT_UNLOCK_CALLBACKS) {
event_warnx("UNLOCK_CALLBACKS requires DEFER_CALLBACKS");
return -1;
}
// defer callbacks 初始化
if (options & BEV_OPT_DEFER_CALLBACKS) {
if (options & BEV_OPT_UNLOCK_CALLBACKS)
event_deferred_cb_init(&bufev_private->deferred,
bufferevent_run_deferred_callbacks_unlocked,
bufev_private);
else
event_deferred_cb_init(&bufev_private->deferred,
bufferevent_run_deferred_callbacks_locked,
bufev_private);
} bufev_private->options = options; // 关联 bufferevent 和 buffer
evbuffer_set_parent(bufev->input, bufev);
evbuffer_set_parent(bufev->output, bufev); return 0;
}

bufferevent 创建成功之后,fd 上存在数据可读则调用 bufferevent_readcb

// fd 可读
static void
bufferevent_readcb(evutil_socket_t fd, short event, void *arg)
{
struct bufferevent *bufev = arg;
struct bufferevent_private *bufev_p =
EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
struct evbuffer *input;
int res = 0;
short what = BEV_EVENT_READING;
ev_ssize_t howmuch = -1, readmax=-1; _bufferevent_incref_and_lock(bufev); // 如果超时了
if (event == EV_TIMEOUT) {
/* Note that we only check for event==EV_TIMEOUT. If
* event==EV_TIMEOUT|EV_READ, we can safely ignore the
* timeout, since a read has occurred */
what |= BEV_EVENT_TIMEOUT;
goto error;
} input = bufev->input; /*
* If we have a high watermark configured then we don't want to
* read more data than would make us reach the watermark.
*/
// 是否设置了输入缓冲区的最大大小
if (bufev->wm_read.high != 0) {
howmuch = bufev->wm_read.high - evbuffer_get_length(input);
/* we somehow lowered the watermark, stop reading */
// 缓冲区中数据过多
if (howmuch <= 0) {
// 暂停 bufferevent 的数据读取
// 具体的做法是移除 read event(ev_read)
bufferevent_wm_suspend_read(bufev);
goto done;
}
}
// 获取可读最大大小
// 和限速有关,如果不限速,则为 MAX_TO_READ_EVER(16384) 也就是 16K
readmax = _bufferevent_get_read_max(bufev_p);
if (howmuch < 0 || howmuch > readmax) /* The use of -1 for "unlimited"
* uglifies this code. XXXX */
howmuch = readmax;
// 如果读取暂停
if (bufev_p->read_suspended)
goto done; // 输入缓冲区可读
evbuffer_unfreeze(input, 0);
// 读取 fd 上的数据
res = evbuffer_read(input, fd, (int)howmuch); /* XXXX evbuffer_read would do better to take and return ev_ssize_t */
// 输入缓冲区禁止读取
evbuffer_freeze(input, 0); // 读取数据失败
if (res == -1) {
// 获取到错误
int err = evutil_socket_geterror(fd);
// EINTR、EAGAIN
// Windows 下为 WSAEWOULDBLOCK、WSAEINTR
if (EVUTIL_ERR_RW_RETRIABLE(err))
goto reschedule;
// 如果错误是不可重试的,报错
/* error case */
what |= BEV_EVENT_ERROR;
// eof
} else if (res == 0) {
/* eof case */
what |= BEV_EVENT_EOF;
} if (res <= 0)
goto error; _bufferevent_decrement_read_buckets(bufev_p, res); /* Invoke the user callback - must always be called last */
// 判断是否需要调用回调
if (evbuffer_get_length(input) >= bufev->wm_read.low)
_bufferevent_run_readcb(bufev); goto done; reschedule:
goto done; error:
// 出错后暂停读取数据
bufferevent_disable(bufev, EV_READ);
// 通过事件回调通知出错
_bufferevent_run_eventcb(bufev, what); done:
_bufferevent_decref_and_unlock(bufev);
}

这里比较关键的函数为 evbuffer_read:

int
evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
{
struct evbuffer_chain **chainp;
int n;
int result; #ifdef USE_IOVEC_IMPL
int nvecs, i, remaining;
#else
struct evbuffer_chain *chain;
unsigned char *p;
#endif EVBUFFER_LOCK(buf); // buffer 是否可读
if (buf->freeze_end) {
result = -1;
goto done;
} // 获取当前 socket 可读的字节数
n = get_n_bytes_readable_on_socket(fd);
if (n <= 0 || n > EVBUFFER_MAX_READ)
n = EVBUFFER_MAX_READ;
// 尽可能多的读取
if (howmuch < 0 || howmuch > n)
howmuch = n; // 一种实现
#ifdef USE_IOVEC_IMPL
/* Since we can use iovecs, we're willing to use the last
* NUM_READ_IOVEC chains. */
// 调整缓冲区(细节略)
if (_evbuffer_expand_fast(buf, howmuch, NUM_READ_IOVEC) == -1) {
result = -1;
goto done;
} else {
IOV_TYPE vecs[NUM_READ_IOVEC];
#ifdef _EVBUFFER_IOVEC_IS_NATIVE
nvecs = _evbuffer_read_setup_vecs(buf, howmuch, vecs,
NUM_READ_IOVEC, &chainp, 1);
#else
/* We aren't using the native struct iovec. Therefore,
we are on win32. */
struct evbuffer_iovec ev_vecs[NUM_READ_IOVEC];
nvecs = _evbuffer_read_setup_vecs(buf, howmuch, ev_vecs, 2,
&chainp, 1); for (i=0; i < nvecs; ++i)
WSABUF_FROM_EVBUFFER_IOV(&vecs[i], &ev_vecs[i]);
#endif #ifdef WIN32
{
// 读取到的数据字节数
DWORD bytesRead;
DWORD flags=0;
// windows 下进行 recv
if (WSARecv(fd, vecs, nvecs, &bytesRead, &flags, NULL, NULL)) {
/* The read failed. It might be a close,
* or it might be an error. */
// 这里使用 WSARecv 时需要注意 WSAECONNABORTED 表示了连接关闭了
if (WSAGetLastError() == WSAECONNABORTED)
n = 0;
else
n = -1;
} else
n = bytesRead;
}
#else
// 非 windows 平台 read
n = readv(fd, vecs, nvecs);
#endif
} // 使用另外一种实现
#else /*!USE_IOVEC_IMPL*/
/* If we don't have FIONREAD, we might waste some space here */
/* XXX we _will_ waste some space here if there is any space left
* over on buf->last. */
if ((chain = evbuffer_expand_singlechain(buf, howmuch)) == NULL) {
result = -1;
goto done;
} /* We can append new data at this point */
p = chain->buffer + chain->misalign + chain->off; // read
#ifndef WIN32
n = read(fd, p, howmuch);
#else
n = recv(fd, p, howmuch, 0);
#endif
#endif /* USE_IOVEC_IMPL */ if (n == -1) {
result = -1;
goto done;
}
if (n == 0) {
result = 0;
goto done;
} #ifdef USE_IOVEC_IMPL
remaining = n;
for (i=0; i < nvecs; ++i) {
ev_ssize_t space = (ev_ssize_t) CHAIN_SPACE_LEN(*chainp);
if (space < remaining) {
(*chainp)->off += space;
remaining -= (int)space;
} else {
(*chainp)->off += remaining;
buf->last_with_datap = chainp;
break;
}
chainp = &(*chainp)->next;
}
#else
chain->off += n;
advance_last_with_data(buf);
#endif
buf->total_len += n;
buf->n_add_for_cb += n; /* Tell someone about changes in this buffer */
// 调用回调
evbuffer_invoke_callbacks(buf);
result = n;
done:
EVBUFFER_UNLOCK(buf);
return result;
}

读完了 bufferevent_readcb 接下来再看看 bufferevent_writecb:

// fd 可写
static void
bufferevent_writecb(evutil_socket_t fd, short event, void *arg)
{
struct bufferevent *bufev = arg;
struct bufferevent_private *bufev_p =
EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
int res = 0;
short what = BEV_EVENT_WRITING;
int connected = 0;
ev_ssize_t atmost = -1; _bufferevent_incref_and_lock(bufev); // 如果超时了
if (event == EV_TIMEOUT) {
/* Note that we only check for event==EV_TIMEOUT. If
* event==EV_TIMEOUT|EV_WRITE, we can safely ignore the
* timeout, since a read has occurred */
what |= BEV_EVENT_TIMEOUT;
goto error;
}
// 正在连接
if (bufev_p->connecting) {
// 连接是否完成
int c = evutil_socket_finished_connecting(fd);
/* we need to fake the error if the connection was refused
* immediately - usually connection to localhost on BSD */
if (bufev_p->connection_refused) {
bufev_p->connection_refused = 0;
c = -1;
} // 如果还没有连接完成,则不处理
if (c == 0)
goto done; // 如果连接完成
bufev_p->connecting = 0;
// 如果连接出错
if (c < 0) {
// 移除 bufferevent 的事件
event_del(&bufev->ev_write);
event_del(&bufev->ev_read);
// 事件回调,告知连接出错
_bufferevent_run_eventcb(bufev, BEV_EVENT_ERROR);
goto done;
// 如果连接成功
} else {
connected = 1;
#ifdef WIN32
// 是否为异步 IO bufferevent(IOCP)
if (BEV_IS_ASYNC(bufev)) {
event_del(&bufev->ev_write);
bufferevent_async_set_connected(bufev);
_bufferevent_run_eventcb(bufev,
BEV_EVENT_CONNECTED);
goto done;
}
#endif
// 通知连接成功
_bufferevent_run_eventcb(bufev,
BEV_EVENT_CONNECTED);
// 如果不可写
if (!(bufev->enabled & EV_WRITE) ||
bufev_p->write_suspended) {
// 移除 ev_write
event_del(&bufev->ev_write);
goto done;
}
}
} // 获取可写最大大小
// 和限速有关,如果不限速,则为 MAX_TO_WRITE_EVER(16384) 也就是 16K
atmost = _bufferevent_get_write_max(bufev_p); // 如果不可写
if (bufev_p->write_suspended)
goto done; // 如果输出缓冲区存在数据
if (evbuffer_get_length(bufev->output)) {
evbuffer_unfreeze(bufev->output, 1);
// 写入尽可能多的数据到 fd
res = evbuffer_write_atmost(bufev->output, fd, atmost);
evbuffer_freeze(bufev->output, 1);
if (res == -1) {
int err = evutil_socket_geterror(fd);
// 如果写入数据出错时可重试
if (EVUTIL_ERR_RW_RETRIABLE(err))
goto reschedule;
// 真正出错则设置
what |= BEV_EVENT_ERROR;
// 连接断开
} else if (res == 0) {
/* eof case
XXXX Actually, a 0 on write doesn't indicate
an EOF. An ECONNRESET might be more typical.
*/
what |= BEV_EVENT_EOF;
}
if (res <= 0)
goto error; _bufferevent_decrement_write_buckets(bufev_p, res);
} // 如果缓冲区所有数据都被写入完了
if (evbuffer_get_length(bufev->output) == 0) {
// 清除 ev_write(无需继续写入数据了)
event_del(&bufev->ev_write);
} /*
* Invoke the user callback if our buffer is drained or below the
* low watermark.
*/
// 尝试调用写入回调函数
if ((res || !connected) &&
evbuffer_get_length(bufev->output) <= bufev->wm_write.low) {
_bufferevent_run_writecb(bufev);
} goto done; reschedule:
// 如果无法写入,但是输出缓冲区已经为空时
if (evbuffer_get_length(bufev->output) == 0) {
// 无需继续写入数据了
event_del(&bufev->ev_write);
}
goto done; error:
// 出错时告知上层
bufferevent_disable(bufev, EV_WRITE);
_bufferevent_run_eventcb(bufev, what); done:
_bufferevent_decref_and_unlock(bufev);
}

bufferevent_writecb 中比较关键的一个函数为 evbuffer_write_atmost:

int
evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd,
ev_ssize_t howmuch)
{
int n = -1; EVBUFFER_LOCK(buffer); // 是否不可写
if (buffer->freeze_start) {
goto done;
} // 写尽量多的数据
if (howmuch < 0 || (size_t)howmuch > buffer->total_len)
howmuch = buffer->total_len; // 如果有数据需要写
if (howmuch > 0) {
// 使用 evbuffer_write_sendfile 写数据
#ifdef USE_SENDFILE
struct evbuffer_chain *chain = buffer->first;
if (chain != NULL && (chain->flags & EVBUFFER_SENDFILE))
n = evbuffer_write_sendfile(buffer, fd, howmuch);
else {
#endif
#ifdef USE_IOVEC_IMPL
// 使用 evbuffer_write_iovec 写数据
n = evbuffer_write_iovec(buffer, fd, howmuch);
#elif defined(WIN32)
/* XXX(nickm) Don't disable this code until we know if
* the WSARecv code above works. */
void *p = evbuffer_pullup(buffer, howmuch);
// windows 下 send
n = send(fd, p, howmuch, 0);
#else
void *p = evbuffer_pullup(buffer, howmuch);
// 其他平台 write
n = write(fd, p, howmuch);
#endif
#ifdef USE_SENDFILE
}
#endif
} if (n > 0)
// 如果写入的数据大于 0 则缓冲区对应移除相关数据
evbuffer_drain(buffer, n); done:
EVBUFFER_UNLOCK(buffer);
return (n);
}

代码读到这里,对于 bufferevent 的创建、socket 读写已经有了一定的了解,下面再看看 bufferevent_write,此函数实际上只是直接向输出缓冲区写入数据,缓冲区写入数据后,会调用回调 bufferevent_socket_outbuf_cb(创建 bufferevent 时设置的),此回调工作内容比较简单,主要就是将 ev_write 注册到 base 中去:

static void
bufferevent_socket_outbuf_cb(struct evbuffer *buf,
const struct evbuffer_cb_info *cbinfo,
void *arg)
{
struct bufferevent *bufev = arg;
struct bufferevent_private *bufev_p =
EVUTIL_UPCAST(bufev, struct bufferevent_private, bev); // evbuffer 中有数据
if (cbinfo->n_added &&
// bufferevent 可以写入数据
(bufev->enabled & EV_WRITE) &&
// 检测 ev_write 是否已经是 pending 状态或者已经被调度
// 这里无需重复注册 ev_write
!event_pending(&bufev->ev_write, EV_WRITE, NULL) &&
// bufferevent 是否已经禁止写入
!bufev_p->write_suspended) {
/* Somebody added data to the buffer, and we would like to
* write, and we were not writing. So, start writing. */
// 注册 ev_write 写入数据
if (be_socket_add(&bufev->ev_write, &bufev->timeout_write) == -1) {
/* Should we log this? */
}
}
}

最后来看看释放过程:

void
bufferevent_free(struct bufferevent *bufev)
{
BEV_LOCK(bufev);
// 清理回调
bufferevent_setcb(bufev, NULL, NULL, NULL, NULL);
// 此函数似乎啥也没做
_bufferevent_cancel_all(bufev);
// 真正的清理发生在 bufferevent 引用计数为 0 时
_bufferevent_decref_and_unlock(bufev);
} int
_bufferevent_decref_and_unlock(struct bufferevent *bufev)
{
struct bufferevent_private *bufev_private =
EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
struct bufferevent *underlying; EVUTIL_ASSERT(bufev_private->refcnt > 0); // 引用计数减 1
if (--bufev_private->refcnt) {
BEV_UNLOCK(bufev);
return 0;
}
// 如果 bufferevent 引用技术为 0 了 // 获取底层 bufferevent
underlying = bufferevent_get_underlying(bufev); /* Clean up the shared info */
if (bufev->be_ops->destruct)
// 调用 be_socket_destruct
// 清理 ev_read 和 ev_write
// 关闭 socket
bufev->be_ops->destruct(bufev); /* XXX what happens if refcnt for these buffers is > 1?
* The buffers can share a lock with this bufferevent object,
* but the lock might be destroyed below. */
/* evbuffer will free the callbacks */
// 释放缓冲区
evbuffer_free(bufev->input);
evbuffer_free(bufev->output); // 如果使用了限速,则进行相关清理
if (bufev_private->rate_limiting) {
if (bufev_private->rate_limiting->group)
bufferevent_remove_from_rate_limit_group_internal(bufev,0);
if (event_initialized(&bufev_private->rate_limiting->refill_bucket_event))
event_del(&bufev_private->rate_limiting->refill_bucket_event);
event_debug_unassign(&bufev_private->rate_limiting->refill_bucket_event);
mm_free(bufev_private->rate_limiting);
bufev_private->rate_limiting = NULL;
} event_debug_unassign(&bufev->ev_read);
event_debug_unassign(&bufev->ev_write); BEV_UNLOCK(bufev);
if (bufev_private->own_lock)
EVTHREAD_FREE_LOCK(bufev_private->lock,
EVTHREAD_LOCKTYPE_RECURSIVE); /* Free the actual allocated memory. */
mm_free(((char*)bufev) - bufev->be_ops->mem_offset); /* Release the reference to underlying now that we no longer need the
* reference to it. We wait this long mainly in case our lock is
* shared with underlying.
*
* The 'destruct' function will also drop a reference to underlying
* if BEV_OPT_CLOSE_ON_FREE is set.
*
* XXX Should we/can we just refcount evbuffer/bufferevent locks?
* It would probably save us some headaches.
*/
if (underlying)
bufferevent_decref(underlying); return 1;
}

更多详细的内容还需要更进一步阅读源码。

bufferevent 与 socket的更多相关文章

  1. (转)Libevent(4)— Bufferevent

    转自:http://name5566.com/4215.html 参考文献列表:http://www.wangafu.net/~nickm/libevent-book/ 此文编写的时候,使用到的 Li ...

  2. Libevent学习笔记(四) bufferevent 的 concepts and basics

    Bufferevents and evbuffers Every bufferevent has an input buffer and an output buffer. These are of ...

  3. libevent系列文章

    Libevent 2 提供了 bufferevent 接口,简化了编程的难度,bufferevent 实际上是对底层事件核心的封装,因此学习 bufferevent 的实现是研究 Libevent 底 ...

  4. libevent入门

    Libevent API =============================== evtimer_new evtimer_new(base, callback, NULL) 用来做定时器,即当 ...

  5. Libevent API

    evtimer_new evtimer_new(base, callback, NULL) 用来做定时器,即当达到一定时间后调用回调函数callback.用evtimer_add激活定时器.比如: m ...

  6. libevent reference Mannual V -- Bufferevents

    FYI: http://www.wangafu.net/~nickm/libevent-book/Ref6_bufferevent.html Bufferevents: concepts and ba ...

  7. Libevent:7Bufferevents基本概念

    很多时候,应用程序除了能响应事件之外,还希望能够处理一定量的数据缓存.比如,当写数据的时候,一般会经历下列步骤: l  决定向一个链接中写入一些数据:将数据放入缓冲区中: l  等待该链接变得可写: ...

  8. libevent之基于socket的bufferevent

    基于socket的bufferevent由一个socket的传输层和read/write buffer组成.区别于常规的event,当socket可读或者可写时会回调用户的callback,buffe ...

  9. 处理大并发之五 使用libevent利器bufferevent

    转自:http://blog.csdn.net/feitianxuxue/article/details/9386843 处理大并发之五 使用libevent利器bufferevent 首先来翻译一段 ...

随机推荐

  1. css 浮动规则

    1. 如果浮动元素的高度大于父级元素,那么浮动元素块会超过父级元素的底部.解决办法:将父级元素也设置为浮动定位. 2. 不改变box尺寸的情况下增加box内部的内边距:box-sizing: bord ...

  2. MySQL 存储 utf8mb4

    1.如果是阿里云数据库 a.控制台->修改参数character_set_server参数为UTF8mb4 b.设置库的字符集为UTF8mb4 2.如果是自己mysql服务器 [client] ...

  3. (转)]PYTHON Tkinter GUI

    import Tkinterroot=Tkinter.Tk()label=Tkinter.Label(root,text='hello ,python')label.pack()      #将LAB ...

  4. XHR2:js异步上传

    http://dev.opera.com/articles/xhr2/ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 2 ...

  5. 关于面试总结-python笔试题(递归)

    前言 本篇继续收集一些常见的python笔试题,以基础知识为主,递归是面试最喜欢考的一个问题,不管是做开发还是测试,都无法避免考递归.本篇结合实际案例,讲下几种关于递归的场景. 计算n的阶乘 计算n! ...

  6. NYOJ760-See LCS again,有技巧的暴力!

    See LCS again 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 There are A, B two sequences, the number of ele ...

  7. [luoguP1168]中位数(主席树+离散化)

    传送门 模板题一道,1A. ——代码 #include <cstdio> #include <algorithm> #define ls son[now][0], l, mid ...

  8. [转]Fedora 添加国内源(sohu, 163)

    第一种方案 在国内163和搜狐提供很好的源,现在我们把它们俩添加到我们的源库.1. 添加搜狐的源 进入网站http://mirrors.sohu.com/,在左边找到fedora目录,点击该行右边的h ...

  9. 【收藏】下载Chrome商店插件的方法,万恶的gwd

    以下是下载离线插件包的方法: 第一步: 每个Google Chrome扩展都有一个固定的ID,例如https://chrome.google.com/webstore/detail/bfbmjmiod ...

  10. GDKOI2018游记

    D0 开开心心去酒店,在Vanda,资磁,然而和其他人住的比较远,不资磁. 开开心心打开玩具熊,吓尿了..第四部贼难. 晚上看了看网络流,1点才睡.3点多好像梦到玩具熊被吓醒,4点继续睡,6点起. D ...