libevent的使用

文章目录

  • libevent封装的框架思想
  • 常用函数分析
  • 使用fifo的读写
  • 未决和非未决
  • bufferevent特性
  • bufferevent函数
  • 客户端和服务器连接和监听
  • libevent实现socket通信


libevent封装的框架思想

libevent框架:1. 创建 event_base		(乐高底座)2. 创建 事件evnet	3. 将事件 添加到 base上	4. 循环监听事件满足5. 释放 event_base1. 创建 event_base		(乐高底座)struct event_base *event_base_new(void);struct event_base *base = event_base_new();2. 创建 事件evnet	常规事件 event	--> event_new(); bufferevent --> bufferevent_socket_new();3. 将事件 添加到 base上	int event_add(struct event *ev, const struct timeval *tv)4. 循环监听事件满足int event_base_dispatch(struct event_base *base);event_base_dispatch(base);5. 释放 event_baseevent_base_free(base);

常用函数分析

创建事件event:struct event *ev;struct event *event_new(struct event_base *base,evutil_socket_t fd,short what,event_callback_fn cb;  void *arg);base: event_base_new()返回值。fd: 绑定到 event 上的 文件描述符what:对应的事件(r、w、e)在		EV_READ		一次 读事件EV_WRTIE	一次 写事件EV_PERSIST	持续触发。 结合 event_base_dispatch 函数使用,生效。cb:一旦事件满足监听条件,回调的函数。typedef void (*event_callback_fn)(evutil_socket_t fd,  short,  void *)	arg: 回调的函数的参数。返回值:成功创建的 event
事件event操作:添加事件到 event_baseint event_add(struct event *ev, const struct timeval *tv);ev: event_new() 的返回值。tv:为NULL,不会超时。意为:一直等到事件被触发,回调函数会被调用。为非0,等待期间,检查事件没有被触发,时间到,回调函数依旧会被调用。将事件从base上拿下int event_del(struct event *ev);ev: event_new() 的返回值。释放事件int event_free(struct event *ev);ev: event_new() 的返回值。

使用fifo的读写

read.c

#include <stdio.h>  
#include <unistd.h>  
#include <stdlib.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <string.h>  
#include <fcntl.h>  
#include <event2/event.h>  // 对操作处理函数  
void read_cb(evutil_socket_t fd, short what, void *arg)  
{  // 读管道  char buf[1024] = {0};  int len = read(fd, buf, sizeof(buf));  printf("read event: %s \n", what & EV_READ ? "Yes" : "No");  printf("data len = %d, buf = %s\n", len, buf);  sleep(1);  
}  // 读管道  
int main(int argc, const char* argv[])  
{  unlink("myfifo");  //创建有名管道  mkfifo("myfifo", 0664);  // open file  //int fd = open("myfifo", O_RDONLY | O_NONBLOCK);  int fd = open("myfifo", O_RDONLY);  if(fd == -1)  {  perror("open error");  exit(1);  }  // 创建个event_base  struct event_base* base = NULL;  base = event_base_new();  // 创建事件  struct event* ev = NULL;  ev = event_new(base, fd, EV_READ | EV_PERSIST, read_cb, NULL);  // 添加事件  event_add(ev, NULL);  // 事件循环  event_base_dispatch(base);  // while(1) { epoll();}  // 释放资源  event_free(ev);  event_base_free(base);  close(fd);  return 0;  
}  

write.c

#include <stdio.h>  
#include <unistd.h>  
#include <stdlib.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <string.h>  
#include <fcntl.h>  
#include <event2/event.h>  // 对操作处理函数  
void write_cb(evutil_socket_t fd, short what, void *arg)  
{  // write管道  char buf[1024] = {0};  static int num = 0;  sprintf(buf, "hello,world-%d\n", num++);  write(fd, buf, strlen(buf)+1);  sleep(1);  
}  // 写管道  
int main(int argc, const char* argv[])  
{  // open file  //int fd = open("myfifo", O_WRONLY | O_NONBLOCK);  int fd = open("myfifo", O_WRONLY);  if(fd == -1)  {  perror("open error");  exit(1);  }  // 写管道  struct event_base* base = NULL;  base = event_base_new();  // 创建事件  struct event* ev = NULL;  // 检测的写缓冲区是否有空间写  //ev = event_new(base, fd, EV_WRITE , write_cb, NULL);  ev = event_new(base, fd, EV_WRITE | EV_PERSIST, write_cb, NULL);  // 添加事件  event_add(ev, NULL);  // 事件循环  event_base_dispatch(base);  // 释放资源  event_free(ev);  event_base_free(base);  close(fd);  return 0;  
}  

未决和非未决

非未决: 没有资格被处理

未决: 有资格被处理,但尚未被处理

event_new --> event ---> 非未决 --> event_add --> 未决 --> dispatch() && 监听事件被触发 --> 激活态 --> 执行回调函数 --> 处理态 --> 非未决 event_add && EV_PERSIST --> 未决 --> event_del --> 非未决

在这里插入图片描述

bufferevent特性

带缓冲区的事件 bufferevent#include <event2/bufferevent.h> 
读:有数据-->读回调函数被调用-->bufferevent_read()-->读数据
写:使用bufferevent_write()-->向写缓冲中写数据-->该缓冲区有数据自动写出-->写完,回调函数被调用

在这里插入图片描述

bufferevent函数

创建、销毁bufferevent:struct bufferevent *ev;struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, enum bufferevent_options options);base: event_basefd:	封装到bufferevent内的 fdoptions:BEV_OPT_CLOSE_ON_FREE返回: 成功创建的 bufferevent事件对象。void  bufferevent_socket_free(struct bufferevent *ev);
给bufferevent设置回调:对比event:	event_new( fd, callback );  	event_add() -- 挂到 event_base 上。bufferevent_socket_new(base,fd)  bufferevent_setcb( callback )void bufferevent_setcb(struct bufferevent * bufev,bufferevent_data_cb readcb,bufferevent_data_cb writecb,bufferevent_event_cb eventcb,void *cbarg );bufev: bufferevent_socket_new() 返回值readcb: 设置 bufferevent 读缓冲,对应回调  read_cb{  bufferevent_read() 读数据  }writecb: 设置 bufferevent 写缓冲,对应回调 write_cb {  } -- 给调用者,发送写成功通知。  可以 NULLeventcb: 设置 事件回调。   也可传NULLevents: BEV_EVENT_CONNECTEDcbarg:	上述回调函数使用的 参数。event回调函数类型typedef void (*bufferevent_event_cb)(struct bufferevent *bev,  short events, void *ctx);void event_cb(struct bufferevent *bev,  short events, void *ctx){...}read 回调函数类型:typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void*ctx);void read_cb(struct bufferevent *bev, void *cbarg ){.....bufferevent_read();   --- read();}bufferevent_read()函数的原型:size_t bufferevent_read(struct bufferevent *bev, void *buf, size_t bufsize);write 回调函数类型:int bufferevent_write(struct bufferevent *bufev, const void *data,  size_t size);
启动、关闭 bufferevent的 缓冲区:默认:新建的bufferevent的写缓冲是 enable、读缓冲是 disablevoid bufferevent_enable(struct bufferevent *bufev, short events);   启动	通常用来启动bufferevent的read缓冲void bufferevent_disable(struct bufferevent *bufev, short events); 禁用events: EV_READ、EV_WRITE、EV_READ|EV_WRITEvoid bufferevent_get_enabled(struct bufferevent *bufev);获取缓冲区的禁用状态,需要借助&来得到

客户端和服务器连接和监听

客户端:socket();connect(fd,addr,addr_len);int bufferevent_socket_connect(struct bufferevent *bev, struct sockaddr *address, int addrlen);bev: bufferevent 事件对象(其中封装了fd)address、addresslen:等同于 connect() 的第二个参数和第三个参数创建监听服务器:#include<event2/listener.h>//这个函数相当于socket、bind、listen、accept的作用struct evconnlistener *evconnlistener_new_bind (	struct event_base *base,evconnlistener_cb cb, void *ptr, unsigned flags,int backlog,const struct sockaddr *sa,int socklen);base: event_basecb: 回调函数。 一旦被回调,说明在其内部应该与客户端完成数据读写操作,进行通信。ptr: 回调函数的参数flags: 可识别的标志 LEV_OPT_CLOSE_ON_FREE:释放bufferevent时关闭底层传输端口。这将关闭底层套接字、释放底层bufferevent等LEV_OPT_REUSEABLE:端口服用backlog: listen()的第2个参数。 -1 表最大值sa:服务器自己的地址结构体socklen:服务器自己的地址结构体大小。返回值:成功创建的监听器。回调函数类型:typedef void(*evconnlistner_cb)(struct evconnlistener *listener, evutil_socket_t sock,struct sockaddr* addr, int len, void *ptr);listener: evconnlistener_new_bind函数返回值sock: 用于通信的文件描述符addr: 客户端的IP+端口len: addr的lenptr: 外部ptr传递进来的值释放监听服务器:void evconnlistener_free(struct evconnlistener *lev);

libevent实现socket通信

服务器端
在这里插入图片描述
server.c

#include <stdio.h>  
#include <unistd.h>  
#include <stdlib.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <string.h>  
#include <event2/event.h>  
#include <event2/listener.h>  
#include <event2/bufferevent.h>  // 读缓冲区回调  
void read_cb(struct bufferevent *bev, void *arg)  
{  char buf[1024] = {0};     bufferevent_read(bev, buf, sizeof(buf));  printf("client say: %s\n", buf);  char *p = "我是服务器, 已经成功收到你发送的数据!";  // 发数据给客户端  bufferevent_write(bev, p, strlen(p)+1);  sleep(1);  
}  // 写缓冲区回调  
void write_cb(struct bufferevent *bev, void *arg)  
{  printf("I'm服务器, 成功写数据给客户端,写缓冲区回调函数被回调...\n");   
}  // 事件  
void event_cb(struct bufferevent *bev, short events, void *arg)  
{  if (events & BEV_EVENT_EOF)  {  printf("connection closed\n");    }  else if(events & BEV_EVENT_ERROR)     {  printf("some other error\n");  }  bufferevent_free(bev);      printf("buffevent 资源已经被释放...\n");   
}  void cb_listener(  struct evconnlistener *listener,   evutil_socket_t fd,   struct sockaddr *addr,   int len, void *ptr)  
{  printf("connect new client\n");  struct event_base* base = (struct event_base*)ptr;  // 通信操作  // 添加新事件  struct bufferevent *bev;  bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);  // 给bufferevent缓冲区设置回调  bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);  bufferevent_enable(bev, EV_READ);  
}  int main(int argc, const char* argv[])  
{  // init server   struct sockaddr_in serv;  memset(&serv, 0, sizeof(serv));  serv.sin_family = AF_INET;  serv.sin_port = htons(9876);  serv.sin_addr.s_addr = htonl(INADDR_ANY);  struct event_base* base;  base = event_base_new();  // 创建套接字  // 绑定  // 接收连接请求  struct evconnlistener* listener;  listener = evconnlistener_new_bind(base, cb_listener, base,   LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,   36, (struct sockaddr*)&serv, sizeof(serv));  event_base_dispatch(base);  evconnlistener_free(listener);  event_base_free(base);  return 0;  
}  

客户端
在这里插入图片描述

#include <stdio.h>  
#include <unistd.h>  
#include <stdlib.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <string.h>  
#include <event2/bufferevent.h>  
#include <event2/event.h>  
#include <arpa/inet.h>  void read_cb(struct bufferevent *bev, void *arg)  
{  char buf[1024] = {0};   bufferevent_read(bev, buf, sizeof(buf));  printf("fwq say:%s\n", buf);  bufferevent_write(bev, buf, strlen(buf)+1);  sleep(1);  
}  void write_cb(struct bufferevent *bev, void *arg)  
{  printf("----------我是客户端的写回调函数,没卵用\n");   
}  void event_cb(struct bufferevent *bev, short events, void *arg)  
{  if (events & BEV_EVENT_EOF)  {  printf("connection closed\n");    }  else if(events & BEV_EVENT_ERROR)     {  printf("some other error\n");  }  else if(events & BEV_EVENT_CONNECTED)  {  printf("已经连接服务器...\\(^o^)/...\n");  return;  }  // 释放资源  bufferevent_free(bev);  
}  // 客户端与用户交互,从终端读取数据写给服务器  
void read_terminal(evutil_socket_t fd, short what, void *arg)  
{  // 读数据  char buf[1024] = {0};  int len = read(fd, buf, sizeof(buf));  struct bufferevent* bev = (struct bufferevent*)arg;  // 发送数据  bufferevent_write(bev, buf, len+1);  
}  int main(int argc, const char* argv[])  
{  struct event_base* base = NULL;  base = event_base_new();  int fd = socket(AF_INET, SOCK_STREAM, 0);  // 通信的fd放到bufferevent中  struct bufferevent* bev = NULL;  bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);  // init server info  struct sockaddr_in serv;  memset(&serv, 0, sizeof(serv));  serv.sin_family = AF_INET;  serv.sin_port = htons(9876);  inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);  // 连接服务器  bufferevent_socket_connect(bev, (struct sockaddr*)&serv, sizeof(serv));  // 设置回调  bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);  // 设置读回调生效  // bufferevent_enable(bev, EV_READ);  // 创建事件  struct event* ev = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST,  read_terminal, bev);  // 添加事件                       event_add(ev, NULL);  event_base_dispatch(base);  event_free(ev);  event_base_free(base);  return 0;  
}  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/671179.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

涛哥聊Python | pyspider,一个超酷的 Python 库!

本文来源公众号“涛哥聊Python”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;pyspider&#xff0c;一个超酷的 Python 库&#xff01; 大家好&#xff0c;今天为大家分享一个超酷的 Python 库 - pyspider。 Github地址&#xf…

在线抠图去背景;修改图片尺寸

免费抠背景图网站&#xff1a; 免费改图网站&#xff1a;

Python深度学习基于Tensorflow(3)Tensorflow 构建模型

文章目录 数据导入和数据可视化数据集制作以及预处理模型结构低阶 API 构建模型中阶 API 构建模型高阶 API 构建模型保存和导入模型 这里以实际项目CIFAR-10为例&#xff0c;分别使用低阶&#xff0c;中阶&#xff0c;高阶 API 搭建模型。 这里以CIFAR-10为数据集&#xff0c;C…

企业网站 | 被攻击时该怎么办?

前言 每天&#xff0c;数以千计的网站被黑客入侵。发生这种情况时&#xff0c;被入侵网站可用于从网络钓鱼页面到SEO垃圾邮件或者其它内容。如果您拥有一个小型网站&#xff0c;很容易相信黑客不会对它感兴趣。不幸的是&#xff0c;通常情况并非如此。 黑客入侵网站的动机与所…

ArcGIS中SHP转CAD如何分图层以及颜色等(保留属性信息)

很多小伙伴在使用ArcGIS时&#xff0c;想要将SHP图层转成CAD&#xff0c;但结果发现生成的CAD数据在打开时只保留了线条或者面块&#xff0c;其余的属性信息全部丢失&#xff0c;甚至无法做到分层&#xff0c;分颜色。在ArcGIS中想要实现SHP分图层以及颜色转CAD需要对CAD的字段…

数据分析之Tebleau可视化:树状图、日历图、气泡图

树状图&#xff08;适合子分类比较多的&#xff09; 1.基本树状图的绘制 同时选择产品子分类和销售金额----选择智能推荐----选择树状图 2.双层树状图的绘制 将第二个维度地区拖到产品分类的下面---大的划分区域是上面的维度&#xff08;产品分类&#xff09;&#xff0c;看着…

设计模式之传输对象模式

在编程江湖里&#xff0c;有一种模式&#xff0c;它如同数据的“特快专递”&#xff0c;穿梭于系统间&#xff0c;保证信息的快速准确送达&#xff0c;它就是——传输对象模式&#xff08;Data Transfer Object, DTO&#xff09;。这不仅仅是数据的搬运工&#xff0c;更是提升系…

小程序激励广告视频多次回调问题

1.问题 2. 激励视频使用及解决方案 官方文档 let videoAd null; // 在页面中定义激励视频广告 Page({/*** 页面的初始数据*/data: {},/*** 生命周期函数--监听页面加载*/onLoad(options) {let that this;// 创建激励视频广告实例if (wx.createRewardedVideoAd) {videoAd w…

打破 AI 算力天花板,Meta超大规模AI基础设施架构解读

Meta超大规模AI智算基础设施架构设计 摘要 双重 GPU 集群&#xff0c;每群配备 2.4 万个 H100 芯片&#xff0c;分别采用 RoCE 和 InfiniBand 网络连接。LLaMA3 就是在这两个集群上训练出来的&#xff1b;Meta AI 将部署庞大算力集群&#xff0c;拥有 35 万张 H100 GPU&#x…

(数据分析方法)长期趋势分析

目录 一、定义 二、目的 三、方法 1、移动平均法 (1)、简单移动平均法 (2)、加权移动平均法 (3)、指数平滑法 2、最小二乘法 3、线性回归 1、数据预处理 2、观察数据分布建立假设模型 3、定义损失函数 4、批量梯度下降 5、优化 4、LSTM 时序分析 5、特征工程 一…

分布式事务了解吗?你们是如何解决分布式事务问题的?(文末有福利)

目录 一、面试官心理分析 二、面试题剖析 1.XA 方案&#xff08;两阶段提交方案&#xff09; 2.TCC 方案 3.Saga方案 4.本地消息表 5.可靠消息最终一致性方案 6.最大努力通知方案 7.你们公司是如何处理分布式事务的? 福利放送&#xff1a; 一、面试官心理分析 只要聊…

cmake进阶:文件操作之读文件

一. 简介 cmake 提供了 file() 命令可对文件进行一系列操作&#xff0c;譬如读写文件、删除文件、文件重命名、拷贝文件、创建目录等等。 接下来 学习这个功能强大的 file() 命令。 前一篇文章学习了 CMakeLists.txt语法中写文件操作。文章如下&#xff1a; cmake进阶&…