当前位置:   article > 正文

详解libevent网络库(二)---即时聊天通讯_libevent基本通讯

libevent基本通讯

使用bufferevent简单实现服务器与客户端之间的即时聊天通讯

目录

使用bufferevent简单实现服务器与客户端之间的即时聊天通讯

前言

为libevent造一个房子---架构分析图

创建套接字bufferevent

设置bufferevent回调函数

操控bufferevent中的数据

为libevent客户端实现打基础

为libevent服务端实现打基础


前言

上回我们说到初学libevent框架,使用利用event_new、fifo实现进程间通讯此为无缓冲方式,承上篇所述,本文将讲述使用bufferevent利用数据缓存区实现网络通讯,文章的最后将以此为例实现客户端与服务器之间的聊天通讯。

libevent入门篇(一)--初识libevent

为libevent造一个房子---架构分析图

  1. 盖房的套路(从外到内看)
  2. 装修属于自己的房间(由内而外)

数据缓冲区---bufferevent

是libevent为IO缓冲区操作提供的一种通用机制,bufferevent 由一个底层的传输端口(如套接字 )。

数据缓冲区是由读缓冲区写缓冲区两部分组成。

 

创建套接字bufferevent
 

使用函数bufferevent_socket_new(),创建bufferevent的套接字

  1. // struct bufferevent也是一个 event
  2. struct bufferevent * bufferevent_socket_new(
  3. struct event_base *base,
  4. evutil_socket_t fd,
  5. enum bufferevent_options options
  6. );
  7. // 参数options: 例如:BEV_OPT_CLOSE_ON_FREE 释放 bufferevent 时关闭底层传输端口
  8. // 成功时返回bufferevent,失败则返回NULL

当然我们也忘不了释放函数 bufferevent_free()

void bufferevent_free(struct bufferevent *bev);

接下来就是我们为所欲为的时刻了,把我们的房间装修成想要的样子(为缓冲区添加读写操作,设置读写回调函数

设置bufferevent回调函数

 

使用函数bufferevent_setcb(),设置回调函数

  1. //对bufferevent设置回调函数
  2. void bufferevent_setcb(
  3. struct bufferevent *bufev,
  4. bufferevent_data_cb readcb,//使用 bufferevent_read()读取buff中数据信息
  5. bufferevent_data_cb writecb,//写回调只是提示你发生出去数据,没有实质作用
  6. bufferevent_event_cb eventcb,
  7. void *cbarg
  8. );
  1. //读写回调函数typedef
  2. typedef void (*bufferevent_data_cb)(
  3. struct bufferevent *bev,
  4. void *ctx
  5. );
  6. typedef void (*bufferevent_event_cb)(
  7. struct bufferevent *bev,
  8. short events,
  9. void *ctx
  10. );
  11. //events参数:
  12. // EV_EVENT_READING: 读取操作时发生某事件,具体是哪种事件请看其他标志
  13. // BEV_EVENT_WRITING:写入操作时发生某事件,具体是哪种事件请看其他标志
  14. // BEV_EVENT_ERROR: 操作时发生错误
  15. // BEV_EVENT_TIMEOUT:发生超时
  16. // BEV_EVENT_EOF: 遇到文件结束指示。
  17. // BEV_EVENT_CONNECTED:请求的连接过程已经完成实现客户端的时候可以判断
  18. //EVUTIL_SOCKET_ERROR():关于错误的更多信息
EV_EVENT_READING读取操作时发生某事件,具体是哪种事件请看其他标志
BEV_EVENT_WRITING写入操作时发生某事件,具体是哪种事件请看其他标志
BEV_EVENT_ERROR操作时发生错误
BEV_EVENT_TIMEOUT发生超时
BEV_EVENT_EOF遇到文件结束指示
BEV_EVENT_CONNECTED请求的连接过程已经完成实现客户端的时候可以判断
 

对回调函数的管理--设置回调函数为启动状态或禁用态:

 

可以启用或者禁用 bufferevent 上的 EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE 事件。没有启用读取或者写入事件时,  bufferevent 将不会试图进行数据读取或者写入。

  1. //开启
  2. void bufferevent_enable(
  3. struct bufferevent *bufev,
  4. short events
  5. );
  6. //禁用, 对应的回调就不会被调用
  7. void bufferevent_disable(
  8. struct bufferevent *bufev,
  9. short events
  10. );
  11. short bufferevent_get_enabled(
  12. struct bufferevent *bufev
  13. );

操控bufferevent中的数据

向bufferevent的输出缓冲区添加数据

  1. int bufferevent_write(
  2. struct bufferevent *bufev,
  3. const void *data,
  4. size_t size
  5. );

从bufferevent的输入缓冲区移除数据

  1. size_t bufferevent_read(
  2. struct bufferevent *bufev,
  3. void *data,
  4. size_t size
  5. );

为libevent客户端实现打基础

在实现通讯之前,我们需要拥有connect()作为客户端的启动链接,这时我们需要调用bufferevent_socket_connect()函数---思维导向理解为socket编程中需要connect()

  1. int bufferevent_socket_connect( struct bufferevent *bev,
  2. struct sockaddr *address,
  3. int addrlen
  4. );
  • address 和 addrlen 参数跟标准调用 connect() 的参数相同

 

  1. bufferevent 未设置套接字,该函数将为其分配一个新的流套接字, 并且设置状态为非阻塞

  2. bufferevent 已设置套接字,调用 bufferevent_socket_connect() 将告知libevent 套接字还未连接,直到连接成功之前不应该对其进行读取或者写入操作。

  3. 连接完成之前可以向输出缓冲区添加数据

为libevent服务端实现打基础

在实现通讯之前,我们需要拥有bind()绑定与listen()监听,当然libevent为我们提供了更好的选择就是evconnlistener_new_bind()

链接监听器 - evconnlistener

  1. struct evconnlistener *evconnlistener_new_bind(
  2. struct event_base *base, evconnlistener_cb cb, void *ptr,
  3. unsigned flags,
  4. int backlog,
  5. const struct sockaddr *sa, int socklen
  6. );

当然我们也可以使用evconnlistener_new()函数,这两个可以二者取其一使用,推荐使用bind处理起来更简单方便,原因在于其内部为我们实现了绑定操作,感兴趣的小伙伴可查看源码对其深入研究

  1. struct evconnlistener * evconnlistener_new(
  2. struct event_base *base,
  3. evconnlistener_cb cb,
  4. void *ptr,
  5. unsigned flags, int backlog, evutil_socket_t fd
  6. );

回调函数的编写

  1. typedef void (*evconnlistener_cb)(
  2. struct evconnlistener *listener, evutil_socket_t sock,
  3. struct sockaddr *addr,
  4. int len,
  5. void *ptr
  6. );

注:两个 evconnlistener_new*()函数都分配和返回一个新的连接监听器对象。连接监听器使用 event_base 来得知什么时候在给定的监听套接字上有新的 TCP 连接到达时,监听器调用回调函数。

当然我们也忘不了释放函数 vconnlistener_free()

void evconnlistener_free(struct evconnlistener *lev);

对evconnlistener的管理--设置为启动状态或禁用态以及修改回调设置:

以下两个函数暂时禁止或者重新允许监听新连接

  1. int evconnlistener_disable(struct evconnlistener *lev);
  2. int evconnlistener_enable(struct evconnlistener *lev);

函数调整 evconnlistener 的回调函数和其参数

  1. void evconnlistener_set_cb(
  2. struct evconnlistener *lev,
  3. evconnlistener_cb cb,
  4. void *arg
  5. );

demo

举个栗子?

  1. //server.c
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <string.h>
  8. #include <event2/event.h>
  9. #include <event2/listener.h>
  10. #include <event2/bufferevent.h>
  11. // 读缓冲区回调
  12. void read_cb(struct bufferevent *bev, void *arg)
  13. {
  14. char buf[1024] = {0};
  15. bufferevent_read(bev, buf, sizeof(buf));
  16. char* p = "我已经收到了你发送的数据!";
  17. printf("client say: %s\n", buf);
  18. }
  19. // 写缓冲区回调
  20. void write_cb(struct bufferevent *bev, void *arg)
  21. {
  22. printf("我是写缓冲区的回调函数...您已发送\n");
  23. }
  24. // 事件
  25. void event_cb(struct bufferevent *bev, short events, void *arg)
  26. {
  27. if (events & BEV_EVENT_EOF)
  28. {
  29. printf("connection closed\n");
  30. }
  31. else if(events & BEV_EVENT_ERROR)
  32. {
  33. printf("some other error\n");
  34. }
  35. bufferevent_free(bev);
  36. printf("buffevent 资源已经被释放...\n");
  37. }
  38. void send_cb(evutil_socket_t fd, short what, void *arg);
  39. void cb_listener(
  40. struct evconnlistener *listener,
  41. evutil_socket_t fd,
  42. struct sockaddr *addr,
  43. int len, void *ptr)
  44. {
  45. printf("connect new client\n");
  46. struct event_base* base = (struct event_base*)ptr;
  47. // 通信操作
  48. // 添加新事件
  49. struct bufferevent *bev;
  50. bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
  51. // 给bufferevent缓冲区设置回调
  52. bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
  53. bufferevent_enable(bev, EV_READ);
  54. // 创建一个事件
  55. struct event* ev = event_new(base, STDIN_FILENO,
  56. EV_READ | EV_PERSIST,
  57. send_cb, bev);
  58. event_add(ev, NULL);
  59. }
  60. void send_cb(evutil_socket_t fd, short what, void *arg)
  61. {
  62. char buf[1024] = {0};
  63. struct bufferevent* bev = (struct bufferevent*)arg;
  64. // printf("请输入要发送的数据: \n");
  65. read(fd, buf, sizeof(buf));
  66. bufferevent_write(bev, buf, strlen(buf)+1);
  67. }
  68. int main(int argc, const char* argv[])
  69. {
  70. // init server
  71. struct sockaddr_in serv;
  72. memset(&serv, 0, sizeof(serv));
  73. serv.sin_family = AF_INET;
  74. serv.sin_port = htons(9876);
  75. serv.sin_addr.s_addr = htonl(INADDR_ANY);
  76. struct event_base* base;
  77. base = event_base_new();
  78. // 创建套接字
  79. // 绑定
  80. // 接收连接请求
  81. struct evconnlistener* listener;
  82. listener = evconnlistener_new_bind(base, cb_listener, base,
  83. LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
  84. 36, (struct sockaddr*)&serv, sizeof(serv));
  85. event_base_dispatch(base);
  86. evconnlistener_free(listener);
  87. event_base_free(base);
  88. return 0;
  89. }
  1. //client.c
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <string.h>
  8. #include <event2/event.h>
  9. #include <event2/bufferevent.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("Server say: %s\n", buf);
  15. }
  16. void write_cb(struct bufferevent *bev, void *arg)
  17. {
  18. printf("我是写缓冲区的回调函数...您已发送\n");
  19. }
  20. void event_cb(struct bufferevent *bev, short events, void *arg)
  21. {
  22. if (events & BEV_EVENT_EOF)
  23. {
  24. printf("connection closed\n");
  25. }
  26. else if(events & BEV_EVENT_ERROR)
  27. {
  28. printf("some other error\n");
  29. }
  30. else if(events & BEV_EVENT_CONNECTED)
  31. {
  32. printf("服务器已连接\n");
  33. return;
  34. }
  35. bufferevent_free(bev);
  36. printf("free bufferevent...\n");
  37. }
  38. void send_cb(evutil_socket_t fd, short what, void *arg)
  39. {
  40. char buf[1024] = {0};
  41. struct bufferevent* bev = (struct bufferevent*)arg;
  42. // printf("请输入要发送的数据: \n");
  43. read(fd, buf, sizeof(buf));
  44. bufferevent_write(bev, buf, strlen(buf)+1);
  45. }
  46. int main(int argc, const char* argv[])
  47. {
  48. struct event_base* base;
  49. base = event_base_new();
  50. struct bufferevent* bev;
  51. bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
  52. // 连接服务器
  53. struct sockaddr_in serv;
  54. memset(&serv, 0, sizeof(serv));
  55. serv.sin_family = AF_INET;
  56. serv.sin_port = htons(9876);
  57. evutil_inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
  58. bufferevent_socket_connect(bev, (struct sockaddr*)&serv, sizeof(serv));
  59. // 设置回调
  60. bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
  61. bufferevent_enable(bev, EV_READ | EV_PERSIST);
  62. // 创建一个事件
  63. struct event* ev = event_new(base, STDIN_FILENO,
  64. EV_READ | EV_PERSIST,
  65. send_cb, bev);
  66. event_add(ev, NULL);
  67. event_base_dispatch(base);
  68. event_base_free(base);
  69. return 0;
  70. }

更换你想要的服务器地址ip编译后就尽情享受聊天吧!!~

  1. gcc server.c -o server -levent
  2. gcc client.c -o client -levent

文中函数相关参数可翻阅libevent中文手册https://download.csdn.net/download/lemon_tea666/11247645

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

闽ICP备14008679号