从 bufferevent 实现学习 Libevent 的使用

Libevent 2 提供了 bufferevent 接口,简化了编程的难度,bufferevent 实际上是对底层事件核心的封装,因此学习 bufferevent 的实现是研究 Libevent 底层 event、event_base 用法的一个好办法。本文假定你已经对 Libevent 有一定的认识,否则可以先阅读我关于 Libevent 的介绍:
Libevent(1)— 简介、编译、配置
Libevent(2)— event、event_base
Libevent(3)— 基础库
Libevent(4)— Bufferevent
Libevent(5)— 连接监听器

bufferevent 的简单范例

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

 
 
  1. #include <string.h>
  2. #include <errno.h>
  3. #include <stdio.h>
  4. #include <signal.h>
  5. #ifndef WIN32
  6. #include <netinet/in.h>
  7. # ifdef _XOPEN_SOURCE_EXTENDED
  8. # include <arpa/inet.h>
  9. # endif
  10. #include <sys/socket.h>
  11. #endif
  12.  
  13. // bufferevent
  14. #include <event2/bufferevent.h>
  15. // bufferevent 使用的 buffer
  16. #include <event2/buffer.h>
  17. // 连接监听器
  18. #include <event2/listener.h>
  19. #include <event2/util.h>
  20. #include <event2/event.h>
  21.  
  22. static const char MESSAGE[] = "Hello, World!\n";
  23.  
  24. static const int PORT = 9995;
  25.  
  26. // 新连接到来时的回调
  27. static void listener_cb(struct evconnlistener *, evutil_socket_t,
  28. struct sockaddr *, int socklen, void *);
  29. // 读取回调
  30. static void conn_writecb(struct bufferevent *, void *);
  31. // 事件回调
  32. static void conn_eventcb(struct bufferevent *, short, void *);
  33. // 信号回调
  34. static void signal_cb(evutil_socket_t, short, void *);
  35.  
  36. int
  37. main(int argc, char **argv)
  38. {
  39. struct event_base *base;
  40. struct evconnlistener *listener;
  41. struct event *signal_event;
  42.  
  43. struct sockaddr_in sin;
  44. #ifdef WIN32
  45. WSADATA wsa_data;
  46. WSAStartup(0x0201, &wsa_data);
  47. #endif
  48.  
  49. // 首先构建 base
  50. base = event_base_new();
  51. if (!base) {
  52. fprintf(stderr, "Could not initialize libevent!\n");
  53. return 1;
  54. }
  55.  
  56. memset(&sin, 0, sizeof(sin));
  57. sin.sin_family = AF_INET;
  58. sin.sin_port = htons(PORT);
  59.  
  60. // 创建监听器
  61. listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
  62. LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
  63. (struct sockaddr*)&sin,
  64. sizeof(sin));
  65.  
  66. if (!listener) {
  67. fprintf(stderr, "Could not create a listener!\n");
  68. return 1;
  69. }
  70.  
  71. // 中断信号
  72. signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);
  73.  
  74. if (!signal_event || event_add(signal_event, NULL)<0) {
  75. fprintf(stderr, "Could not create/add a signal event!\n");
  76. return 1;
  77. }
  78.  
  79. event_base_dispatch(base);
  80.  
  81. evconnlistener_free(listener);
  82. event_free(signal_event);
  83. event_base_free(base);
  84.  
  85. printf("done\n");
  86. return 0;
  87. }
  88.  
  89. static void
  90. listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
  91. struct sockaddr *sa, int socklen, void *user_data)
  92. {
  93. struct event_base *base = user_data;
  94. struct bufferevent *bev;
  95.  
  96. // 得到一个新的连接,通过连接 fd 构建一个 bufferevent
  97. bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
  98. if (!bev) {
  99. fprintf(stderr, "Error constructing bufferevent!");
  100. event_base_loopbreak(base);
  101. return;
  102. }
  103. // 设置创建的 bufferevent 的回调函数
  104. bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
  105. bufferevent_enable(bev, EV_WRITE);
  106. bufferevent_disable(bev, EV_READ);
  107.  
  108. // 写入数据到 bufferevent 中
  109. bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
  110. }
  111.  
  112. static void
  113. conn_writecb(struct bufferevent *bev, void *user_data)
  114. {
  115. struct evbuffer *output = bufferevent_get_output(bev);
  116. if (evbuffer_get_length(output) == 0) {
  117. printf("flushed answer\n");
  118. bufferevent_free(bev);
  119. }
  120. }
  121.  
  122. static void
  123. conn_eventcb(struct bufferevent *bev, short events, void *user_data)
  124. {
  125. if (events & BEV_EVENT_EOF) {
  126. printf("Connection closed.\n");
  127. } else if (events & BEV_EVENT_ERROR) {
  128. printf("Got an error on the connection: %s\n",
  129. strerror(errno));/*XXX win32*/
  130. }
  131. /* None of the other events can happen here, since we haven't enabled
  132. * timeouts */
  133. bufferevent_free(bev);
  134. }
  135.  
  136. static void
  137. signal_cb(evutil_socket_t sig, short events, void *user_data)
  138. {
  139. struct event_base *base = user_data;
  140. struct timeval delay = { 2, 0 };
  141.  
  142. printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");
  143.  
  144. // 停止事件循环
  145. event_base_loopexit(base, &delay);
  146. }
研究 bufferevent 的关键代码

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

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

bufferevent_socket_new 的源码以及分析如下:

 
 
  1. // base --- 新创建的 bufferevent 关联的 base
  2. // fd --- bufferevent 关联的文件描述符
  3. struct bufferevent *
  4. bufferevent_socket_new(struct event_base *base, evutil_socket_t fd,
  5. int options)
  6. {
  7. // bufferevent_private 结构体持有 bufferevent 的数据
  8. struct bufferevent_private *bufev_p;
  9. // bufev == &(bufev_p->bev);
  10. // struct bufferevent 中存放的是不同类型的 bufferevent 所共有的部分
  11. // struct bufferevent 是 struct bufferevent_private 的子集
  12. struct bufferevent *bufev;
  13.  
  14. // windows 下如果启用 IOCP 则构建异步 IO bufferevent
  15. #ifdef WIN32
  16. if (base && event_base_get_iocp(base))
  17. // 细节略
  18. return bufferevent_async_new(base, fd, options);
  19. #endif
  20.  
  21. if ((bufev_p = mm_calloc(1, sizeof(struct bufferevent_private)))== NULL)
  22. return NULL;
  23.  
  24. // 初始化 bufferevent_private
  25. // 由于 bufferevent 有不同类型,所以这里设计了 bufferevent_ops_socket
  26. // 对于不同类型的 bufferevent 有不同的 bufferevent_ops_socket 对象
  27. // bufferevent_ops_socket 包括函数指针和一些信息
  28. if (bufferevent_init_common(bufev_p, base, &bufferevent_ops_socket,
  29. options) < 0) {
  30. mm_free(bufev_p);
  31. return NULL;
  32. }
  33. bufev = &bufev_p->bev;
  34. // 设置 EVBUFFER_FLAG_DRAINS_TO_FD,此选项和 evbuffer_add_file() 函数有关(详见文档)
  35. evbuffer_set_flags(bufev->output, EVBUFFER_FLAG_DRAINS_TO_FD);
  36.  
  37. // 初始化 read 和 write event
  38. // 一个 bufferevent(一个 fd)关联两个 event 对象 ev_read 和 ev_write
  39. // ev_read --- socket 可读或者超时
  40. // ev_write --- socket 可写或者超时
  41. // 它们都未使用 Edge triggered 方式
  42. event_assign(&bufev->ev_read, bufev->ev_base, fd,
  43. EV_READ|EV_PERSIST, bufferevent_readcb, bufev);
  44. event_assign(&bufev->ev_write, bufev->ev_base, fd,
  45. EV_WRITE|EV_PERSIST, bufferevent_writecb, bufev);
  46.  
  47. // 为输出缓冲区设置回调
  48. // 当输出缓冲区被修改时调用 bufferevent_socket_outbuf_cb 回调函数
  49. evbuffer_add_cb(bufev->output, bufferevent_socket_outbuf_cb, bufev);
  50.  
  51. // 防止输入缓冲区和输出缓冲区被意外修改
  52. evbuffer_freeze(bufev->input, 0);
  53. evbuffer_freeze(bufev->output, 1);
  54.  
  55. return bufev;
  56. }

其中 bufferevent_init_common 函数实现为:

 
 
  1. int
  2. bufferevent_init_common(struct bufferevent_private *bufev_private,
  3. struct event_base *base,
  4. const struct bufferevent_ops *ops,
  5. enum bufferevent_options options)
  6. {
  7. struct bufferevent *bufev = &bufev_private->bev;
  8.  
  9. // 创建输入缓冲区
  10. if (!bufev->input) {
  11. if ((bufev->input = evbuffer_new()) == NULL)
  12. return -1;
  13. }
  14.  
  15. // 创建输出缓冲区
  16. if (!bufev->output) {
  17. if ((bufev->output = evbuffer_new()) == NULL) {
  18. evbuffer_free(bufev->input);
  19. return -1;
  20. }
  21. }
  22.  
  23. // 初始化 bufferevent 的引用计数
  24. bufev_private->refcnt = 1;
  25. bufev->ev_base = base;
  26.  
  27. /* Disable timeouts. */
  28. // 清理超时时间
  29. evutil_timerclear(&bufev->timeout_read);
  30. evutil_timerclear(&bufev->timeout_write);
  31.  
  32. bufev->be_ops = ops;
  33.  
  34. /*
  35. * Set to EV_WRITE so that using bufferevent_write is going to
  36. * trigger a callback. Reading needs to be explicitly enabled
  37. * because otherwise no data will be available.
  38. */
  39. // enabled 被 bufferevent_get_enabled 函数返回
  40. // enabled 的值可以为 EV_WRITE EV_READ
  41. bufev->enabled = EV_WRITE;
  42.  
  43. // bufferevent 相关线程初始化
  44. #ifndef _EVENT_DISABLE_THREAD_SUPPORT
  45. if (options & BEV_OPT_THREADSAFE) {
  46. if (bufferevent_enable_locking(bufev, NULL) < 0) {
  47. /* cleanup */
  48. evbuffer_free(bufev->input);
  49. evbuffer_free(bufev->output);
  50. bufev->input = NULL;
  51. bufev->output = NULL;
  52. return -1;
  53. }
  54. }
  55. #endif
  56. // 选项正确性检查
  57. if ((options & (BEV_OPT_DEFER_CALLBACKS|BEV_OPT_UNLOCK_CALLBACKS))
  58. == BEV_OPT_UNLOCK_CALLBACKS) {
  59. event_warnx("UNLOCK_CALLBACKS requires DEFER_CALLBACKS");
  60. return -1;
  61. }
  62. // defer callbacks 初始化
  63. if (options & BEV_OPT_DEFER_CALLBACKS) {
  64. if (options & BEV_OPT_UNLOCK_CALLBACKS)
  65. event_deferred_cb_init(&bufev_private->deferred,
  66. bufferevent_run_deferred_callbacks_unlocked,
  67. bufev_private);
  68. else
  69. event_deferred_cb_init(&bufev_private->deferred,
  70. bufferevent_run_deferred_callbacks_locked,
  71. bufev_private);
  72. }
  73.  
  74. bufev_private->options = options;
  75.  
  76. // 关联 bufferevent 和 buffer
  77. evbuffer_set_parent(bufev->input, bufev);
  78. evbuffer_set_parent(bufev->output, bufev);
  79.  
  80. return 0;
  81. }

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

 
 
  1. // fd 可读
  2. static void
  3. bufferevent_readcb(evutil_socket_t fd, short event, void *arg)
  4. {
  5. struct bufferevent *bufev = arg;
  6. struct bufferevent_private *bufev_p =
  7. EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
  8. struct evbuffer *input;
  9. int res = 0;
  10. short what = BEV_EVENT_READING;
  11. ev_ssize_t howmuch = -1, readmax=-1;
  12.  
  13. _bufferevent_incref_and_lock(bufev);
  14.  
  15. // 如果超时了
  16. if (event == EV_TIMEOUT) {
  17. /* Note that we only check for event==EV_TIMEOUT. If
  18. * event==EV_TIMEOUT|EV_READ, we can safely ignore the
  19. * timeout, since a read has occurred */
  20. what |= BEV_EVENT_TIMEOUT;
  21. goto error;
  22. }
  23.  
  24. input = bufev->input;
  25.  
  26. /*
  27. * If we have a high watermark configured then we don't want to
  28. * read more data than would make us reach the watermark.
  29. */
  30. // 是否设置了输入缓冲区的最大大小
  31. if (bufev->wm_read.high != 0) {
  32. howmuch = bufev->wm_read.high - evbuffer_get_length(input);
  33. /* we somehow lowered the watermark, stop reading */
  34. // 缓冲区中数据过多
  35. if (howmuch <= 0) {
  36. // 暂停 bufferevent 的数据读取
  37. // 具体的做法是移除 read event(ev_read)
  38. bufferevent_wm_suspend_read(bufev);
  39. goto done;
  40. }
  41. }
  42. // 获取可读最大大小
  43. // 和限速有关,如果不限速,则为 MAX_TO_READ_EVER(16384) 也就是 16K
  44. readmax = _bufferevent_get_read_max(bufev_p);
  45. if (howmuch < 0 || howmuch > readmax) /* The use of -1 for "unlimited"
  46. * uglifies this code. XXXX */
  47. howmuch = readmax;
  48. // 如果读取暂停
  49. if (bufev_p->read_suspended)
  50. goto done;
  51.  
  52. // 输入缓冲区可读
  53. evbuffer_unfreeze(input, 0);
  54. // 读取 fd 上的数据
  55. res = evbuffer_read(input, fd, (int)howmuch); /* XXXX evbuffer_read would do better to take and return ev_ssize_t */
  56. // 输入缓冲区禁止读取
  57. evbuffer_freeze(input, 0);
  58.  
  59. // 读取数据失败
  60. if (res == -1) {
  61. // 获取到错误
  62. int err = evutil_socket_geterror(fd);
  63. // EINTR、EAGAIN
  64. // Windows 下为 WSAEWOULDBLOCK、WSAEINTR
  65. if (EVUTIL_ERR_RW_RETRIABLE(err))
  66. goto reschedule;
  67. // 如果错误是不可重试的,报错
  68. /* error case */
  69. what |= BEV_EVENT_ERROR;
  70. // eof
  71. } else if (res == 0) {
  72. /* eof case */
  73. what |= BEV_EVENT_EOF;
  74. }
  75.  
  76. if (res <= 0)
  77. goto error;
  78.  
  79. _bufferevent_decrement_read_buckets(bufev_p, res);
  80.  
  81. /* Invoke the user callback - must always be called last */
  82. // 判断是否需要调用回调
  83. if (evbuffer_get_length(input) >= bufev->wm_read.low)
  84. _bufferevent_run_readcb(bufev);
  85.  
  86. goto done;
  87.  
  88. reschedule:
  89. goto done;
  90.  
  91. error:
  92. // 出错后暂停读取数据
  93. bufferevent_disable(bufev, EV_READ);
  94. // 通过事件回调通知出错
  95. _bufferevent_run_eventcb(bufev, what);
  96.  
  97. done:
  98. _bufferevent_decref_and_unlock(bufev);
  99. }

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

 
 
  1. int
  2. evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
  3. {
  4. struct evbuffer_chain **chainp;
  5. int n;
  6. int result;
  7.  
  8. #ifdef USE_IOVEC_IMPL
  9. int nvecs, i, remaining;
  10. #else
  11. struct evbuffer_chain *chain;
  12. unsigned char *p;
  13. #endif
  14.  
  15. EVBUFFER_LOCK(buf);
  16.  
  17. // buffer 是否可读
  18. if (buf->freeze_end) {
  19. result = -1;
  20. goto done;
  21. }
  22.  
  23. // 获取当前 socket 可读的字节数
  24. n = get_n_bytes_readable_on_socket(fd);
  25. if (n <= 0 || n > EVBUFFER_MAX_READ)
  26. n = EVBUFFER_MAX_READ;
  27. // 尽可能多的读取
  28. if (howmuch < 0 || howmuch > n)
  29. howmuch = n;
  30.  
  31. // 一种实现
  32. #ifdef USE_IOVEC_IMPL
  33. /* Since we can use iovecs, we're willing to use the last
  34. * NUM_READ_IOVEC chains. */
  35. // 调整缓冲区(细节略)
  36. if (_evbuffer_expand_fast(buf, howmuch, NUM_READ_IOVEC) == -1) {
  37. result = -1;
  38. goto done;
  39. } else {
  40. IOV_TYPE vecs[NUM_READ_IOVEC];
  41. #ifdef _EVBUFFER_IOVEC_IS_NATIVE
  42. nvecs = _evbuffer_read_setup_vecs(buf, howmuch, vecs,
  43. NUM_READ_IOVEC, &chainp, 1);
  44. #else
  45. /* We aren't using the native struct iovec. Therefore,
  46. we are on win32. */
  47. struct evbuffer_iovec ev_vecs[NUM_READ_IOVEC];
  48. nvecs = _evbuffer_read_setup_vecs(buf, howmuch, ev_vecs, 2,
  49. &chainp, 1);
  50.  
  51. for (i=0; i < nvecs; ++i)
  52. WSABUF_FROM_EVBUFFER_IOV(&vecs[i], &ev_vecs[i]);
  53. #endif
  54.  
  55. #ifdef WIN32
  56. {
  57. // 读取到的数据字节数
  58. DWORD bytesRead;
  59. DWORD flags=0;
  60. // windows 下进行 recv
  61. if (WSARecv(fd, vecs, nvecs, &bytesRead, &flags, NULL, NULL)) {
  62. /* The read failed. It might be a close,
  63. * or it might be an error. */
  64. // 这里使用 WSARecv 时需要注意 WSAECONNABORTED 表示了连接关闭了
  65. if (WSAGetLastError() == WSAECONNABORTED)
  66. n = 0;
  67. else
  68. n = -1;
  69. } else
  70. n = bytesRead;
  71. }
  72. #else
  73. // 非 windows 平台 read
  74. n = readv(fd, vecs, nvecs);
  75. #endif
  76. }
  77.  
  78. // 使用另外一种实现
  79. #else /*!USE_IOVEC_IMPL*/
  80. /* If we don't have FIONREAD, we might waste some space here */
  81. /* XXX we _will_ waste some space here if there is any space left
  82. * over on buf->last. */
  83. if ((chain = evbuffer_expand_singlechain(buf, howmuch)) == NULL) {
  84. result = -1;
  85. goto done;
  86. }
  87.  
  88. /* We can append new data at this point */
  89. p = chain->buffer + chain->misalign + chain->off;
  90.  
  91. // read
  92. #ifndef WIN32
  93. n = read(fd, p, howmuch);
  94. #else
  95. n = recv(fd, p, howmuch, 0);
  96. #endif
  97. #endif /* USE_IOVEC_IMPL */
  98.  
  99. if (n == -1) {
  100. result = -1;
  101. goto done;
  102. }
  103. if (n == 0) {
  104. result = 0;
  105. goto done;
  106. }
  107.  
  108. #ifdef USE_IOVEC_IMPL
  109. remaining = n;
  110. for (i=0; i < nvecs; ++i) {
  111. ev_ssize_t space = (ev_ssize_t) CHAIN_SPACE_LEN(*chainp);
  112. if (space < remaining) {
  113. (*chainp)->off += space;
  114. remaining -= (int)space;
  115. } else {
  116. (*chainp)->off += remaining;
  117. buf->last_with_datap = chainp;
  118. break;
  119. }
  120. chainp = &(*chainp)->next;
  121. }
  122. #else
  123. chain->off += n;
  124. advance_last_with_data(buf);
  125. #endif
  126. buf->total_len += n;
  127. buf->n_add_for_cb += n;
  128.  
  129. /* Tell someone about changes in this buffer */
  130. // 调用回调
  131. evbuffer_invoke_callbacks(buf);
  132. result = n;
  133. done:
  134. EVBUFFER_UNLOCK(buf);
  135. return result;
  136. }

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

 
 
  1. // fd 可写
  2. static void
  3. bufferevent_writecb(evutil_socket_t fd, short event, void *arg)
  4. {
  5. struct bufferevent *bufev = arg;
  6. struct bufferevent_private *bufev_p =
  7. EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
  8. int res = 0;
  9. short what = BEV_EVENT_WRITING;
  10. int connected = 0;
  11. ev_ssize_t atmost = -1;
  12.  
  13. _bufferevent_incref_and_lock(bufev);
  14.  
  15. // 如果超时了
  16. if (event == EV_TIMEOUT) {
  17. /* Note that we only check for event==EV_TIMEOUT. If
  18. * event==EV_TIMEOUT|EV_WRITE, we can safely ignore the
  19. * timeout, since a read has occurred */
  20. what |= BEV_EVENT_TIMEOUT;
  21. goto error;
  22. }
  23. // 正在连接
  24. if (bufev_p->connecting) {
  25. // 连接是否完成
  26. int c = evutil_socket_finished_connecting(fd);
  27. /* we need to fake the error if the connection was refused
  28. * immediately - usually connection to localhost on BSD */
  29. if (bufev_p->connection_refused) {
  30. bufev_p->connection_refused = 0;
  31. c = -1;
  32. }
  33.  
  34. // 如果还没有连接完成,则不处理
  35. if (c == 0)
  36. goto done;
  37.  
  38. // 如果连接完成
  39. bufev_p->connecting = 0;
  40. // 如果连接出错
  41. if (c < 0) {
  42. // 移除 bufferevent 的事件
  43. event_del(&bufev->ev_write);
  44. event_del(&bufev->ev_read);
  45. // 事件回调,告知连接出错
  46. _bufferevent_run_eventcb(bufev, BEV_EVENT_ERROR);
  47. goto done;
  48. // 如果连接成功
  49. } else {
  50. connected = 1;
  51. #ifdef WIN32
  52. // 是否为异步 IO bufferevent(IOCP)
  53. if (BEV_IS_ASYNC(bufev)) {
  54. event_del(&bufev->ev_write);
  55. bufferevent_async_set_connected(bufev);
  56. _bufferevent_run_eventcb(bufev,
  57. BEV_EVENT_CONNECTED);
  58. goto done;
  59. }
  60. #endif
  61. // 通知连接成功
  62. _bufferevent_run_eventcb(bufev,
  63. BEV_EVENT_CONNECTED);
  64. // 如果不可写
  65. if (!(bufev->enabled & EV_WRITE) ||
  66. bufev_p->write_suspended) {
  67. // 移除 ev_write
  68. event_del(&bufev->ev_write);
  69. goto done;
  70. }
  71. }
  72. }
  73.  
  74. // 获取可写最大大小
  75. // 和限速有关,如果不限速,则为 MAX_TO_WRITE_EVER(16384) 也就是 16K
  76. atmost = _bufferevent_get_write_max(bufev_p);
  77.  
  78. // 如果不可写
  79. if (bufev_p->write_suspended)
  80. goto done;
  81.  
  82. // 如果输出缓冲区存在数据
  83. if (evbuffer_get_length(bufev->output)) {
  84. evbuffer_unfreeze(bufev->output, 1);
  85. // 写入尽可能多的数据到 fd
  86. res = evbuffer_write_atmost(bufev->output, fd, atmost);
  87. evbuffer_freeze(bufev->output, 1);
  88. if (res == -1) {
  89. int err = evutil_socket_geterror(fd);
  90. // 如果写入数据出错时可重试
  91. if (EVUTIL_ERR_RW_RETRIABLE(err))
  92. goto reschedule;
  93. // 真正出错则设置
  94. what |= BEV_EVENT_ERROR;
  95. // 连接断开
  96. } else if (res == 0) {
  97. /* eof case
  98. XXXX Actually, a 0 on write doesn't indicate
  99. an EOF. An ECONNRESET might be more typical.
  100. */
  101. what |= BEV_EVENT_EOF;
  102. }
  103. if (res <= 0)
  104. goto error;
  105.  
  106. _bufferevent_decrement_write_buckets(bufev_p, res);
  107. }
  108.  
  109. // 如果缓冲区所有数据都被写入完了
  110. if (evbuffer_get_length(bufev->output) == 0) {
  111. // 清除 ev_write(无需继续写入数据了)
  112. event_del(&bufev->ev_write);
  113. }
  114.  
  115. /*
  116. * Invoke the user callback if our buffer is drained or below the
  117. * low watermark.
  118. */
  119. // 尝试调用写入回调函数
  120. if ((res || !connected) &&
  121. evbuffer_get_length(bufev->output) <= bufev->wm_write.low) {
  122. _bufferevent_run_writecb(bufev);
  123. }
  124.  
  125. goto done;
  126.  
  127. reschedule:
  128. // 如果无法写入,但是输出缓冲区已经为空时
  129. if (evbuffer_get_length(bufev->output) == 0) {
  130. // 无需继续写入数据了
  131. event_del(&bufev->ev_write);
  132. }
  133. goto done;
  134.  
  135. error:
  136. // 出错时告知上层
  137. bufferevent_disable(bufev, EV_WRITE);
  138. _bufferevent_run_eventcb(bufev, what);
  139.  
  140. done:
  141. _bufferevent_decref_and_unlock(bufev);
  142. }

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

 
 
  1. int
  2. evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd,
  3. ev_ssize_t howmuch)
  4. {
  5. int n = -1;
  6.  
  7. EVBUFFER_LOCK(buffer);
  8.  
  9. // 是否不可写
  10. if (buffer->freeze_start) {
  11. goto done;
  12. }
  13.  
  14. // 写尽量多的数据
  15. if (howmuch < 0 || (size_t)howmuch > buffer->total_len)
  16. howmuch = buffer->total_len;
  17.  
  18. // 如果有数据需要写
  19. if (howmuch > 0) {
  20. // 使用 evbuffer_write_sendfile 写数据
  21. #ifdef USE_SENDFILE
  22. struct evbuffer_chain *chain = buffer->first;
  23. if (chain != NULL && (chain->flags & EVBUFFER_SENDFILE))
  24. n = evbuffer_write_sendfile(buffer, fd, howmuch);
  25. else {
  26. #endif
  27. #ifdef USE_IOVEC_IMPL
  28. // 使用 evbuffer_write_iovec 写数据
  29. n = evbuffer_write_iovec(buffer, fd, howmuch);
  30. #elif defined(WIN32)
  31. /* XXX(nickm) Don't disable this code until we know if
  32. * the WSARecv code above works. */
  33. void *p = evbuffer_pullup(buffer, howmuch);
  34. // windows 下 send
  35. n = send(fd, p, howmuch, 0);
  36. #else
  37. void *p = evbuffer_pullup(buffer, howmuch);
  38. // 其他平台 write
  39. n = write(fd, p, howmuch);
  40. #endif
  41. #ifdef USE_SENDFILE
  42. }
  43. #endif
  44. }
  45.  
  46. if (n > 0)
  47. // 如果写入的数据大于 0 则缓冲区对应移除相关数据
  48. evbuffer_drain(buffer, n);
  49.  
  50. done:
  51. EVBUFFER_UNLOCK(buffer);
  52. return (n);
  53. }

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

 
 
  1. static void
  2. bufferevent_socket_outbuf_cb(struct evbuffer *buf,
  3. const struct evbuffer_cb_info *cbinfo,
  4. void *arg)
  5. {
  6. struct bufferevent *bufev = arg;
  7. struct bufferevent_private *bufev_p =
  8. EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
  9.  
  10. // evbuffer 中有数据
  11. if (cbinfo->n_added &&
  12. // bufferevent 可以写入数据
  13. (bufev->enabled & EV_WRITE) &&
  14. // 检测 ev_write 是否已经是 pending 状态或者已经被调度
  15. // 这里无需重复注册 ev_write
  16. !event_pending(&bufev->ev_write, EV_WRITE, NULL) &&
  17. // bufferevent 是否已经禁止写入
  18. !bufev_p->write_suspended) {
  19. /* Somebody added data to the buffer, and we would like to
  20. * write, and we were not writing. So, start writing. */
  21. // 注册 ev_write 写入数据
  22. if (be_socket_add(&bufev->ev_write, &bufev->timeout_write) == -1) {
  23. /* Should we log this? */
  24. }
  25. }
  26. }

最后来看看释放过程:

 
 
  1. void
  2. bufferevent_free(struct bufferevent *bufev)
  3. {
  4. BEV_LOCK(bufev);
  5. // 清理回调
  6. bufferevent_setcb(bufev, NULL, NULL, NULL, NULL);
  7. // 此函数似乎啥也没做
  8. _bufferevent_cancel_all(bufev);
  9. // 真正的清理发生在 bufferevent 引用计数为 0 时
  10. _bufferevent_decref_and_unlock(bufev);
  11. }
  12.  
  13. int
  14. _bufferevent_decref_and_unlock(struct bufferevent *bufev)
  15. {
  16. struct bufferevent_private *bufev_private =
  17. EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
  18. struct bufferevent *underlying;
  19.  
  20. EVUTIL_ASSERT(bufev_private->refcnt > 0);
  21.  
  22. // 引用计数减 1
  23. if (--bufev_private->refcnt) {
  24. BEV_UNLOCK(bufev);
  25. return 0;
  26. }
  27. // 如果 bufferevent 引用技术为 0 了
  28.  
  29. // 获取底层 bufferevent
  30. underlying = bufferevent_get_underlying(bufev);
  31.  
  32. /* Clean up the shared info */
  33. if (bufev->be_ops->destruct)
  34. // 调用 be_socket_destruct
  35. // 清理 ev_read 和 ev_write
  36. // 关闭 socket
  37. bufev->be_ops->destruct(bufev);
  38.  
  39. /* XXX what happens if refcnt for these buffers is > 1?
  40. * The buffers can share a lock with this bufferevent object,
  41. * but the lock might be destroyed below. */
  42. /* evbuffer will free the callbacks */
  43. // 释放缓冲区
  44. evbuffer_free(bufev->input);
  45. evbuffer_free(bufev->output);
  46.  
  47. // 如果使用了限速,则进行相关清理
  48. if (bufev_private->rate_limiting) {
  49. if (bufev_private->rate_limiting->group)
  50. bufferevent_remove_from_rate_limit_group_internal(bufev,0);
  51. if (event_initialized(&bufev_private->rate_limiting->refill_bucket_event))
  52. event_del(&bufev_private->rate_limiting->refill_bucket_event);
  53. event_debug_unassign(&bufev_private->rate_limiting->refill_bucket_event);
  54. mm_free(bufev_private->rate_limiting);
  55. bufev_private->rate_limiting = NULL;
  56. }
  57.  
  58. event_debug_unassign(&bufev->ev_read);
  59. event_debug_unassign(&bufev->ev_write);
  60.  
  61. BEV_UNLOCK(bufev);
  62. if (bufev_private->own_lock)
  63. EVTHREAD_FREE_LOCK(bufev_private->lock,
  64. EVTHREAD_LOCKTYPE_RECURSIVE);
  65.  
  66. /* Free the actual allocated memory. */
  67. mm_free(((char*)bufev) - bufev->be_ops->mem_offset);
  68.  
  69. /* Release the reference to underlying now that we no longer need the
  70. * reference to it. We wait this long mainly in case our lock is
  71. * shared with underlying.
  72. *
  73. * The 'destruct' function will also drop a reference to underlying
  74. * if BEV_OPT_CLOSE_ON_FREE is set.
  75. *
  76. * XXX Should we/can we just refcount evbuffer/bufferevent locks?
  77. * It would probably save us some headaches.
  78. */
  79. if (underlying)
  80. bufferevent_decref(underlying);
  81.  
  82. return 1;
  83. }

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

原文地址:http://name5566.com/4509.html


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值