epoll反应堆模型简单实现C/S模型----多路IO转接之高并发

epoll 反应堆模型:

epoll ET模式 + 非阻塞、轮询 + void *ptr。

普通epoll模型创建流程

原来: socket、bind、listen – epoll_create 创建监听 红黑树 – 返回 epfd – epoll_ctl() 向树上添加一个监听fd – while(1)
{ ---- epoll_wait 监听 – 对应监听fd有事件产生 – 返回 监听满足数组。 – 判断返回数组元素 – lfd满足 – Accept – cfd 满足 – read() — 小->大 – write回去 }

epoll反应堆模型创建流程

socket、bind、listen – epoll_create 创建监听 红黑树 – 返回 epfd – epoll_ctl() 向树上添加一个监听fd – while(1)
{ ---- epoll_wait 监听 – 对应监听fd有事件产生 – 返回 监听满足数组。 – 判断返回数组元素 – lfd满足 – Accept – cfd 满足
– read() — 小->大 – cfd从监听红黑树上摘下 – EPOLLOUT – 回调函数 – epoll_ctl() – EPOLL_CTL_ADD 重新放到红黑上监听写事件
– 等待 epoll_wait 返回 – 说明 cfd 可写 – write回去 – cfd从监听红黑树上摘下 – EPOLLIN
– epoll_ctl() – EPOLL_CTL_ADD 重新放到红黑上监听读事件 – epoll_wait 监听 }

总结: 也就是说反应堆做的事是**不但要监听 cfd 的读事件、还要监听cfd的写事件。
**

反应堆的理解:加入IO转接之后,有了事件,server才去处理,这里反应堆也是这样,由于网络环境复杂,服务器处理数据之后,可能并不能直接写回去,比如遇到网络繁忙或者对方缓冲区已经满了这种情况,就不能直接写回给客户端。反应堆就是在处理数据之后,监听写事件,能写会客户端了,才去做写回操作。写回之后,再改为监听读事件。如此循环。

实现代码:

#include "wrap.h"                                                                                                                                                                                                                                                                                                                           
#include <sys/types.h>
#include <sys/time.h>
#include <ctype.h>
#include <sys/epoll.h>
#include <time.h>
#include <fcntl.h>

#define MAX_EVENTS 1024
#define SERV_PORT 9527
#define BUFLEN 1024

void revcdata(int fd, int events, void *arg);
void senddata(int fd, int events, void *arg);

//定义 my_events结构体
struct myevent_s
{
   
    int fd;             //要监听的文件描述符
    int events;         //对应的监听事件
    void *arg;          //泛型参数
    void (*call_back)(int fd, int events, void *arg);   //回调函数
    int status;         //是否在监听,1->在红黑树上监听, 0->不在
    char buf[BUFLEN];   //处理缓冲区的数组
    int len;            //从缓冲区读到的长度
    long last_active;   //记录每次加入红黑树 g_efd的时间, 
};

//定义epoll_create返回的文件描述符
int g_efd = 0;
struct myevent_s g_events[MAX_EVENTS+1];        //自定义结构体类型数组,最后一个指向listenfd

//将结构体 myevent_s 成员变量初始化
void eventset(struct myevent_s *ev, int fd, void (*call_back)(int, int, void *), void *arg)
{
   
    ev->fd = fd; 
    ev->call_back = call_back;
    ev->events = 0;
    ev->arg = arg;
    ev->status = 0;
    //memset(ev->buf, 0, sizeof(ev->buf));
    //ev->len = 0;
    ev->last_active = time(NULL);         //调用eventset函数的时间
}

//向epoll监听的红黑树 g_efd中添加一个文件描述符
void eventadd(struct myevent_s *ev, int efd, int events)
{
   
    struct epoll_event tempEp = {
   0, {
   0}};

    int op; 
    tempEp.data.ptr = ev;                   //有事件加入时, ptr就=ev 可以事件调用的函数
    tempEp.events = ev->events = events;

    //如果不在红黑树中
	if (ev->status == 1) {
   
		op = EPOLL_CTL_MOD;
	}
	else {
   
		op = EPOLL_CTL_ADD;
		ev->status = 1;
	}
    if(epoll_ctl(efd, op, ev->fd, &tempEp) < 0)
        perr_exit("epoll_ctl faild\n");
    else
        printf("event add ok [fd=%d], op=%d, event[%0X]\n", ev->fd, op, events);
    
}
//从红黑树中删除节点
void eventdel(int efd, struct myevent_s *ev)
{
   
    struct epoll_event epv = {
   0, {
   0}};

    if(ev->status == 0)
        return ;
    
    epv.data.ptr = ev;
    ev->status = 0;
    epoll_ctl(efd, EPOLL_CTL_DEL, ev->fd, &epv);
    printf("del fd:%d  things:%d\n", ev->fd, ev->events);
}
//读事件 调用函数
void recvdata(int fd, int events, void *arg)
{
   
    struct myevent_s *ev = (struct myevent_s *)arg;
    int len;

    len = recv(fd, ev->buf, sizeof(ev->buf), 0);    //最后参数为0 可以把函数recv看作 read
    eventdel(g_efd, ev);        //将该节点从红黑树中删除

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coison_z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值