【libevent】reactor模式--模拟服务器客户端

libevent

可以处理I/O事件,信号事件(转化为I/O事件进行处理),以及定时事件。
I/O框架库:封装select,poll,epoll,提供了一个更加便捷的接口。(更高效,更稳定。)

  • 基于Reactor模式实现:
    (共有5各组件)
    1.句柄 (学习,学语文,学数学,学英语,对应处理语文课本,数学课本,英语课本)
    I/O框架要处理的对象有: (I/O事件,信号事件,定时时间),统称:事件源
    I/O事件对应的句柄是文件描述符,信号事件对应的句柄是信号值。
    2.事件多路分发器(事件的到来时随机,异步的,无法程序何时收到一个客户的连接请求,获取这暂停信号),最好的办法就是,程序一直循环等待处理事件,每隔一段时间就访问一下,简单说就是通过统一接口调用select,poll,epoll_wait
    来等待事件。(demultiplex方法就是这些函数的调用接口)
    还需要:register_event,remove_event方法往事件多路分发器中,添加事件和删除事件。
    3.事件处理器和具体事件处理器
    事件处理器的功能:即执行回调(一个或多个)函数(handle_event)在事件循环中,I/O框架提供的事件处理器通常是接口,需要用户自己继承它,来实现自己的事件处理器,也就是具体的事件处理器。因此定义为虚函数,方便扩展。
    (还提供了get_handle),返回和事件处理器相关的句柄。
    (当事件多路分发器检测到有事件产生,会通过句柄来通知应用程序,接下来事件处理器才会去循环处理这些事件),因此句柄和事件处理器是绑定在一块的。
    举个例子:比如I/O事件对应句柄是文件描述,而通过回调函数获取的文件描述符是就绪文件描述符,
    举个例子,比如I/O函数检测到内核中有就绪事件,回调函数就去处理这些就绪时间,比如回调函数中封装了recv,就是通过recv去接收数据。
    5.Reator(整个I/O框架库的核心)
    提供的主要方法:
    1.handle_event:执行事件的循环,重复:等待事件,依次处理就绪事件对应的事件处理器。
    2.register_handler: 该方法通过调用时间多路分发器中的register_event方法来往时间多路分发器中添加事件
    3.remove——hander:该方法通过调用时间多路分发器中的remove_event方法来往时间多路分发器中删除事件。
    整个I/O框架处理事件如下图:
    请添加图片描述

执行:stu@stu-virtual-machine:~/linux/day22$ gcc -o main main.c -levent请添加图片描述
请添加图片描述
ctrl + \ 结束程序
创建具体事件处理器(利用回调函数)在循环事件中执行

  • 信号事件

//struct event* sig_ev = evsignal_new(base,SIGINT,sig_cb,NULL);实际就是调用的是event_new
struct event *sig_ev = event_new(base,SIGINT,EV_SIGNAL|EV_PERSIST,sig_cb,    NULL);                              

如果没有EV_PERSIT
**sig只介入一次。**信号只触发一次请添加图片描述

定时事件

//struct event*time_ev = evtimer_new(base,time_cb,NULL);实际就是调用的是event_new
struct event *time_ev = event_new(base,-1,EV_TIMEOUT|EV_PERSIST,time_cb,N    ULL);

EV_PERSIST,可以一直处理定时事件
请添加图片描述
主动退出事件:

event_base_loopexit(base,&delay);

请添加图片描述

  1. 初始化一个libevent库,并保存返回指针
    //定义实例框架,创建event_base对象,就可以使用Reactor中的方法
   struct event_base *base = event_init();
   assert(base != NULL)
  1. 首先对应用程序进行初始化event,设置好事件类型和回调函数
    如信号事件,定时事件:
 //struct event* sig_ev = evsignal_new(base,SIGINT,sig_cb,NULL);
   struct event *sig_ev = event_new(base,SIGINT,EV_SIGNAL,sig_cb,NULL);//EV_PERSIST作用:事件触发后,自动重新对这个event调用event_add函数

也可以是:

void event_set(struct event *ev, int fd, short event, void (*cb)(int, 
short, void *), void *arg)

ev:执行要初始化的 event 对象;
fd:该 event 绑定的“句柄”,对于信号事件,它就是关注的信号;
event:在该 fd 上关注的事件类型,它可以是 EV_READ, EV_WRITE, EV_SIGNAL
cb:这是一个函数指针,当 fd 上的事件 event 发生时,调用该函数执行处理,它有三个参数,
调用时由 event_base 负责传入,按顺序,实际上就是 event_set 时的 fd, event 和 arg;
arg:传递给 cb 函数指针的参数;

void sig_cb(int fd,short ev,void *arg)
{
        printf("sig =%d\n",fd);
}
void time_cb(int fd,short ev,void *arg)
{

        printf("time out\n");
}
int main()
{
          //定义实例框架,创建event_base对象,就可以使用Reactor中的方法
   struct event_base *base = event_init();
   assert(base != NULL);
   //IO,sig,timeout
 //  struct event* sig_ev = evsignal_new(base,SIGINT,sig_cb,NULL);
   //                                     定时器传入的是-1
  struct event *sig_ev = event_new(base,SIGINT,EV_SIGNAL,sig_cb,NULL);//EV_PERSIST作用:事件触发后,自动重新对这个event调用event_add函数
   assert(sig_ev!=NULL);
  event_add(sig_ev,NULL);//null:定时时间
  
  
  struct timeval tv = {2,0};
  //struct event*time_ev = evtimer_new(base,time_cb,NULL);

  struct event *time_ev = event_new(base,-1,EV_TIMEOUT|EV_PERSIST,time_cb,NULL);
  //吃触发一次就停下来,还是一直处罚,由需求决定。
  event_add(time_ev,&tv);  //注册事件

  event_base_dispatch(base); //事件循环->select poll epoll 阻塞
  event_free(time_ev);
  event_free(sig_ev);
  event_base_free(base);
  exit(0);
}

回调函数还是
请添加图片描述

  • 没有就绪描述符的时候,主动关闭描述符
struct event** p_ev = (struct event**)malloc(sizeof(struct event*));
                  
                   //,将当前文件描述符进行设置,传给p_ev指针
                   //通用事件处理器(设置事件类型,回调函数)
                 *p_ev = event_new(base,c,EV_READ|EV_PERSIST,recv_cb,p_ev);
                  if(*p_ev == NULL)
                  {
                           return ;
                  }
                  //将事件添加到libevent中
                  event_add(*p_ev,NULL);
  //注销事件 ,关闭描述符
                   event_free(*p);
                   free(p);

请添加图片描述

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<event.h>
#include<assert.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>

//回调函数

int socket_init();
void recv_cb(int fd,short ev,void *arg)
{
        struct event **p =  (struct event**)arg;//将p_ev强转过来 
        if(p == NULL)
        {
                return ;
        }
        if(ev & EV_READ)
        {
          char buff[128] = {0};
          int n = recv(fd,buff,127,0);
          if(n <= 0)
          {
                   //注销事件 ,关闭描述符
                   event_free(*p);
                   free(p);
                   close(fd);
                   printf("client over\n");
          }
          else
          {
                   printf("buff(%d) == %s\n",fd,buff);
                   send(fd,"ok",2,0);
          }
        }
}

//回调函数

void accept_cb(int fd,short ev,void *arg)
{
        struct event_base *base = (struct event_base*)arg;
        //将base强转为base;
        //如果是读事件
        if(ev & EV_READ)
        {
                //对就绪事件进行处理。
                    struct sockaddr_in caddr;
                    int len = sizeof(caddr);
                    int c = accept(fd,(struct sockaddr*)&caddr,&len);
                    //得到一个新的文件描述符
                   if(c<0)
                   {
                            return ;
                   }
                     //如何将c存起来‘
                
                   printf("accept c =%d\n",c);
                   struct event** p_ev = (struct event**)malloc(sizeof(struct event*));
                  
                   //,将当前文件描述符进行设置,传给p_ev指针
                   //通用事件处理器(设置事件类型,回调函数)
                 *p_ev = event_new(base,c,EV_READ|EV_PERSIST,recv_cb,p_ev);
                  if(*p_ev == NULL)
                  {
                           return ;
                  }
                  //将事件添加到libevent中
                  event_add(*p_ev,NULL);
        }
    //    if(ev&EV_TIMEOUT)
}
int main()
{
         int sockfd = socket_init();
         assert(sockfd != -1);
         //创建一个libevent库
         struct event_base *base = event_init(); //不能在多线程中使用,因为会使用到同一个全局变量,释放时造成内存泄漏  event_base_new  取消全局变量
         assert(base != NULL);
         //设置好事件类型,设置回调函数(监听套接子)
         struct event *sock_ev = event_new(base,sockfd,EV_READ|EV_PERSIST,accept_cb,base); // 传入base,因为添加连接套接子还要用到。
      assert(sock_ev != NULL);

      //将事件添加到libevent库中
      event_add(sock_ev,NULL);
      //程序进入无限循环,等待就绪事件被select/poll/epoll_wait处理。(如果有就绪事件,(读写信号)启用回调函数)
      //ecent_base_loop循环遍历
      event_base_dispatch(base);
      event_free(sock_ev);
      event_base_free(base);//free实例
      exit(0);
}
int socket_init()
{
         int sockfd = socket(AF_INET,SOCK_STREAM,0);
         if(sockfd == -1)
         {
                  return sockfd;
         }
         struct sockaddr_in saddr;
         memset(&saddr,0,sizeof(saddr));
         saddr.sin_family = AF_INET;
         saddr.sin_port = htons(6000);
         saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
         int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
         if(res == -1)
         {
                  return -1;
         }
         res = listen(sockfd,5);
         if(res == -1)
         {
                 return -1;
         }
         return sockfd;  //获取监听套接子
}

启动 :gcc -o ser ser.c -levent

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值