当前位置:   article > 正文

libevent库学习(2)_bufferevent_enable

bufferevent_enable

话接上回:libevent库学习_StudyWinter的博客-CSDN博客_libevent库

重点还是:libevent库是基于【事件】的异步通信模型========回调

本次重点介绍bufferevent。

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

(1)决定要向连接写入一些数据,把数据放入到缓冲区中
(2)等待连接可以写入
(3)写入尽量多的数据
(4)记住写入了多少数据,如果还有更多数据要写入,等待连接再次可以写入

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

如图;

带有buf的事件对象有两个缓冲区,一个读缓冲区,一个写缓冲区;

读缓冲区设置对应的回调函数,使用bufferevent_read函数代替read函数读取数据;

写缓冲区设置对应的回调函数,使用bufferevent_write函数代替write函数写数据;

接下来介绍一波函数,对照【libevent库学习_StudyWinter的博客-CSDN博客_libevent库】这一节的常规事件来记忆。

1 bufferevent的创建和销毁【对比常规事件的event】

创建函数

  1. struct bufferevent *bufferevent_socket_new(struct event_base *base,
  2. evutil_socket_t fd,
  3. enum bufferevent_options options);
  4. // base: event_base
  5. // fd: 封装到bufferevent内的 fd
  6. // options:BEV_OPT_CLOSE_ON_FREE
  7. // 返回: 成功创建的 bufferevent事件对象。

销毁函数

  1. void bufferevent_socket_free(struct bufferevent *ev);
  2. // ev:bufferevent_socket_new函数的返回值

普通事件不同的是,普通事件在创建时就设置回调函数

  1. // 普通事件创建函数
  2. struct event *event_new(struct event_base *base,
  3. evutil_socket_t fd,
  4. short what,
  5. event_callback_fn cb;
  6. void *arg);
  7. // base: event_base_new()返回值。
  8. // fd: 绑定到 event 上的 文件描述符
  9. // what:对应的事件(r、w、e)
  10. // EV_READ 一次 读事件
  11. // EV_WRTIE 一次 写事件
  12. // EV_PERSIST 持续触发。 结合 event_base_dispatch 函数使用,生效。
  13. // cb:一旦事件满足监听条件,回调的函数。
  14. // typedef void (*event_callback_fn)(evutil_socket_t fd, short, void *)
  15. // arg: 回调的函数的参数。
  16. /// 返回值:成功创建的 event

而eventbufferevent事件需要在使用一个函数设置回调函数

  1. void bufferevent_setcb(struct bufferevent * bufev,
  2. bufferevent_data_cb readcb,
  3. bufferevent_data_cb writecb,
  4. bufferevent_event_cb eventcb,
  5. void *cbarg );
  6. // bufev: bufferevent_socket_new() 返回值
  7. // readcb: 设置 bufferevent 读缓冲,对应回调 read_cb{ bufferevent_read() 读数据 }
  8. // writecb: 设置 bufferevent 写缓冲,对应回调 write_cb { } -- 给调用者,发送写成功通知。 可以 NULL
  9. // eventcb: 设置 事件回调。 也可传NULL
  10. // cbarg: 上述回调函数使用的 参数。
  11. // 读回调函数
  12. typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void*ctx);
  13. void read_cb(struct bufferevent *bev, void *cbarg )
  14. {
  15. .....
  16. bufferevent_read(); --- read();
  17. }
  18. size_t bufferevent_read(struct bufferevent *bev, void *buf, size_t bufsize);
  19. // 写回调
  20. // write 回调函数类型:
  21. int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);

2 缓冲区有关知识

默认新建的bufferevent的写缓冲是enable,而读缓冲是disable的

开启读缓冲区

  1. // 函数
  2. void bufferevent_enable(struct bufferevent *bufev, short events); 启动
  3. events: EV_READ、EV_WRITE、EV_READ|EV_WRITE
  4. // 开启读缓冲
  5. bufferevent_enable(evev, EV_READ);

3 创建连接的客户端

之前在简单版的服务器-客户端通信中,客户端中使用socket、connect函数用于连接服务端,现在一个函数搞定

  1. int bufferevent_socket_connect(struct bufferevent *bev,
  2. struct sockaddr *address,
  3. int addrlen);
  4. // bev: bufferevent 事件对象(封装了fd)
  5. // address、len:等同于 connect() 参2/3

4 创建监听的服务器

之前在简单版的服务器-客户端通信中,服务器使用socket、bind、listen、accep建立连接并监听客户端,现在也一个函数搞定,当这个函数的回调函数被调用,说明已经有新的客户端连接上来了。

  1. struct evconnlistener *evconnlistener_new_bind (
  2. struct event_base *base,
  3. evconnlistener_cb cb,
  4. void *ptr,
  5. unsigned flags,
  6. int backlog,
  7. const struct sockaddr *sa,
  8. int socklen);
  9. // base: event_base
  10. // cb: 回调函数。 一旦被回调,说明在其内部应该与客户端完成, 数据读写操作,进行通信。
  11. // ptr: 回调函数的参数
  12. // flags: LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE
  13. // backlog: listen() 2参。 -1 表最大值
  14. // sa:服务器自己的地址结构体
  15. // socklen:服务器自己的地址结构体大小。
  16. // 返回值:成功创建的监听器。

 释放监听服务器

void evconnlistener_free(struct evconnlistener *lev);

5 libevent实现TCP服务器流程

  • (1)创建event_base

  • (2)创建服务器连接监听器evconnlistener_new_bind()

  • (3)在evconnlistener_new_bind的回调函数中,处理接受连接后的操作

  • (4)回调函数被调用,说明有一个新的客户端连接上来。会得到一个新的fd,用于跟客户端通信(读、写)

  • (5)使用bufferevent_socket_new创建一个新的bufferevent事件,将fd封装到这个事件对象中

  • (6)使用bufferevent_setcb给这个事件对象的read、write、event设置回调

  • (7)设置bufferevent的读写缓冲区enable/disable

  • (8)接收、发送数据bufferevent_read()、bufferevent_write()

  • (9)启动循环

  • (10)释放资源

具体代码

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <string.h>
  7. #include <event2/event.h>
  8. #include <event2/listener.h>
  9. #include <event2/bufferevent.h>
  10. /**
  11. * libevent实现TCP服务器流程
  12. * (1)创建event_base
  13. * (2)创建服务器连接监听器evconnlistener_new_bind()
  14. * (3)在evconnlistener_new_bind的回调函数中,处理接受连接后的操作
  15. * (4)回调函数被调用,说明有一个新的客户端连接上来。会得到一个新的fd,用于跟客户端通信(读、写)
  16. * (5)使用bufferevent_socket_new创建一个新的bufferevent事件,将fd封装到这个事件对象中
  17. * (6)使用bufferevent_setcb给这个事件对象的read、write、event设置回调
  18. * (7)设置bufferevent的读写缓冲区enable/disable
  19. * (8)接收、发送数据bufferevent_read()、bufferevent_write()
  20. * (9)启动循环
  21. * (10)释放资源
  22. *
  23. */
  24. // 8.1读回调
  25. // 即从读缓冲区读数据,再发给写缓冲区
  26. void read_cb(struct bufferevent* bev, void* arg) {
  27. char buf[1024] = {0};
  28. // 使用bufferevent_read读数据
  29. bufferevent_read(bev, buf, sizeof(buf));
  30. printf("client say:%s\n", buf);
  31. char* p = "我是服务器,已经成功收到你发的数据......\n";
  32. // 写回给缓冲区
  33. bufferevent_write(bev, p, strlen(p) + 1);
  34. sleep(1);
  35. }
  36. // 8.2写回调
  37. void write_cb(struct bufferevent* bev, void* arg) {
  38. printf("我是服务器,成功写数据给客户端,写缓冲回调函数被回调......\n");
  39. }
  40. // 8.3事件回调
  41. void event_cb(struct bufferevent* bev, short events, void* arg) {
  42. if (events & BEV_EVENT_EOF) {
  43. printf("connection closed......\n");
  44. } else if (events & BEV_EVENT_ERROR) {
  45. printf("some other error......\n");
  46. }
  47. bufferevent_free(bev);
  48. printf("bufferevent资源已经被释放......\n");
  49. }
  50. // 3在evconnlistener_new_bind的回调函数中处理接受连接后的操作
  51. // 这个函数回调,说明有客户端已经建立连接
  52. void listener_cb(struct evconnlistener* listener,
  53. evutil_socket_t fd,
  54. struct sockaddr* addr,
  55. int len, void* ptr)
  56. {
  57. printf("connect new client......\n");
  58. // 因为创建bufferevent事件需要base,所以将base封装到void* ptr里面
  59. struct event_base* base = (struct event_base*)ptr;
  60. // 5使用bufferevent_socket_new创建一个新的bufferevent事件,将fd封装到这个事件对象中
  61. struct bufferevent* bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
  62. // 6使用bufferevent_setcb给这个事件对象设置回调函数
  63. bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
  64. // 7设置bufferevent的读写缓冲区,默认是写打开,读关闭
  65. bufferevent_enable(bev, EV_READ);
  66. }
  67. int main(int argc, const char* argv[])
  68. {
  69. // 1创建event_base
  70. struct event_base* base = event_base_new();
  71. // 初始化服务器地址结构
  72. struct sockaddr_in server_addr;
  73. memset(&server_addr, 0, sizeof(server_addr));
  74. server_addr.sin_family = AF_INET;
  75. server_addr.sin_port = htons(9527);
  76. server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  77. // 2创建服务器连接监听器evconnlistener_new_bind();
  78. // 这一个函数顶socket、bind、listen、accept
  79. struct evconnlistener* listener = NULL;
  80. listener = evconnlistener_new_bind(base, listener_cb, base,
  81. LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
  82. -1, (struct sockaddr*)(&server_addr), sizeof(server_addr));
  83. // 10启动循环
  84. event_base_dispatch(base);
  85. // 11释放资源
  86. evconnlistener_free(listener);
  87. event_base_free(base);
  88. return 0;
  89. }

6 libevent实现TCP客户端流程

  • (1)创建event_base

  • (2)使用bufferevent_socket_new创建一个和服务器通信的bufferevent事件对象

  • (3)使用bufferevent_socket_connect连接服务器

  • (4)使用bufferevent_setcb给bufferevent对象的read、write、event设置回调

  • (5)设置bufferevent对象的读写缓冲区enable/disenable

  • (6)接收、发送数据bufferevent_read()、bufferevent_write()

  • (7)释放资源

具体代码

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <string.h>
  7. #include <event2/bufferevent.h>
  8. #include <event2/event.h>
  9. #include <arpa/inet.h>
  10. void read_cb(struct bufferevent *bev, void *arg)
  11. {
  12. char buf[1024] = {0};
  13. bufferevent_read(bev, buf, sizeof(buf));
  14. printf("fwq say:%s\n", buf);
  15. bufferevent_write(bev, buf, strlen(buf)+1);
  16. sleep(1);
  17. }
  18. void write_cb(struct bufferevent *bev, void *arg)
  19. {
  20. printf("----------我是客户端的写回调函数,没卵用\n");
  21. }
  22. void event_cb(struct bufferevent *bev, short events, void *arg)
  23. {
  24. if (events & BEV_EVENT_EOF)
  25. {
  26. printf("connection closed\n");
  27. }
  28. else if(events & BEV_EVENT_ERROR)
  29. {
  30. printf("some other error\n");
  31. }
  32. else if(events & BEV_EVENT_CONNECTED)
  33. {
  34. printf("已经连接服务器...\\(^o^)/...\n");
  35. return;
  36. }
  37. // 释放资源
  38. bufferevent_free(bev);
  39. }
  40. // 客户端与用户交互,从终端读取数据写给服务器
  41. void read_terminal(evutil_socket_t fd, short what, void *arg)
  42. {
  43. // 读数据
  44. char buf[1024] = {0};
  45. int len = read(fd, buf, sizeof(buf));
  46. struct bufferevent* bev = (struct bufferevent*)arg;
  47. // 发送数据
  48. bufferevent_write(bev, buf, len+1);
  49. }
  50. int main(int argc, const char* argv[])
  51. {
  52. struct event_base* base = NULL;
  53. base = event_base_new();
  54. int fd = socket(AF_INET, SOCK_STREAM, 0);
  55. // 通信的fd放到bufferevent中
  56. struct bufferevent* bev = NULL;
  57. bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
  58. // init server info
  59. struct sockaddr_in serv;
  60. memset(&serv, 0, sizeof(serv));
  61. serv.sin_family = AF_INET;
  62. serv.sin_port = htons(9876);
  63. inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
  64. // 连接服务器
  65. bufferevent_socket_connect(bev, (struct sockaddr*)&serv, sizeof(serv));
  66. // 设置回调
  67. bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
  68. // 设置读回调生效
  69. // bufferevent_enable(bev, EV_READ);
  70. // 创建事件
  71. struct event* ev = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST,
  72. read_terminal, bev);
  73. // 添加事件
  74. event_add(ev, NULL);
  75. event_base_dispatch(base);
  76. event_free(ev);
  77. event_base_free(base);
  78. return 0;
  79. }

测试

 在编译的时候,需要在后面加上 -levent

7 感受

在使用libevent库的时候明显能感觉到很方便,就是搭架子,然后再组装在一起,难点就是函数太多,初学不好记

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/534185
推荐阅读
相关标签
  

闽ICP备14008679号