基于C++的epoll反应堆

将经典C语言epoll反应堆封装到一个类中

#include<stdio.h>
#include<sys/epoll.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<time.h>
#include<errno.h>
#include<string.h>        //strerror

//epoll反应堆
//传统epoll模型 传入ev.data=lfd或cfd;
//epoll反应堆中用自定义的结构体替代了lfd和cfd,自定义结构体包括监听的文件描述符lfd或cfd,回调函数和参数指针
//epoll反应堆中 传入联合体(ev.data)的是一个自定义结构体指针myev 即 ev.data.ptr=myev;
//相应事件发生 通过指针调用相应的回调函数 events[i].data.ptr->call_back(arg);

//epoll反应堆 工作流程
// 监听可读事件(ET) ⇒ 数据到来 ⇒ 触发读事件 ⇒
// epoll_wait()返回 ⇒
// read完数据; 节点下树; 设置监听写事件和对应写回调函数; 节点上树(可读事件回调函数内)

// ⇒ 监听可写事件(ET) ⇒ 对方可读 ⇒ 触发事件 ⇒
// epoll_wait()返回 ⇒
// write数据; 节点下树; 设置监听读事件和对应可读回调函数; 节点上树(可写事件回调函数内)
// ⇒ 直到程序停止前一直这么交替循环


#define MAX_EVENTS 1024  //监听上限
#define SERVER_PORT 8888 //服务器端口号
#define BUFLEN 4096      //缓存区大小

//回调函数格式
typedef void(*CALL_BACK)(void* erptr,void* m_arg);

//自定义任务类
class My_events{
public:

    //更改
    void Myev_set(CALL_BACK callback){
        call_back=callback;
    }
    //重载更改
    void Myev_set(int fd,CALL_BACK callback){
        call_back=callback;
        m_fd=fd;
    }

    int m_fd;       //监听的文件描述符
    CALL_BACK call_back;  //回调函数

    int m_events=0;   //监听事件类型 EPOLLIN EPOLLOUT
    int m_status=0;   //是否在监听 1在树上,0 不在
    char m_buf[BUFLEN]="";
    int m_buf_len=0;
    time_t last_active;     //每次加入红黑树时间
};


class Epoll_reactor{
public:
    //构造函数,启动服务器
    Epoll_reactor(){
        server_run();
    }

    //添加事件
    void eventadd(int events,My_events* myev);
    //删除一个事件
    void eventdel(My_events* myev);
    //初始化套接字
    void initlistensockte();

    //三个回调函数
    static void acceptcoonect(void* erptr,void* arg);

    //可读事件回调函数 接受客户端发送的数据 
    static void recevdata(void* erptr,void* arg);

    //可写事件回调函数 发送数据
    static void senddata(void* erptr,void* arg);

    //服务器运行
    void server_run();


    //红黑树文件描述符
    int epfd;         
    //自定义任务类数组
    My_events mg_events[MAX_EVENTS];
    // 接受epoll_wait 返回
    struct epoll_event evs[MAX_EVENTS];
private:
    
};

//添加一个文件描述符
void Epoll_reactor::eventadd(int events,My_events* myev){
    struct epoll_event ev={0,{0}};
    //传统epoll模型传入lfd或cfd,epoll反应堆传入自定义结构体指针
    ev.data.ptr=myev; 
    ev.events=myev->m_events=events;  //EPOLLIN 或EPOLLOUT
    int op=0;
    if(myev->m_status==0){   //若原来不再树上
        op=EPOLL_CTL_ADD;
    }
    else{
        printf("add error:already on tree\n");
        return;
    }

    if(epoll_ctl(epfd,op,myev->m_fd,&ev)<0){
        printf("\n event add/mod false [fd= %d] [events= %d] \n", myev->m_fd, myev->m_events);
    }
    else{
        myev->m_status=1;
        printf("event add succeed[fd=%d][events=%d]\n",myev->m_fd,myev->m_events);
    }
}

//删除一个文件描述符
void Epoll_reactor::eventdel(struct My_events* myev){
    if(myev->m_status!=1){     //树上没有这个文件操作符
        return;
    }
    epoll_ctl(epfd,EPOLL_CTL_DEL,myev->m_fd,NULL);
    myev->m_status=0;
}

//回调函数 接受连接
void Epoll_reactor::acceptcoonect(void* erptr,void* arg){

    Epoll_reactor* er_ptr=(Epoll_reactor*)erptr;
    My_events* myev=(My_events*)arg;

    int flag = 0;
    int i;
    int cfd;
    struct sockaddr_in cliaddr;
    socklen_t len=sizeof(cliaddr);
    
    if((cfd=accept(myev->m_fd,(struct sockaddr*)&cliaddr,&len))==-1) //注意:括号不能少了一对
    {
        if(errno!=EAGAIN&&errno!=EINTR){    //暂不处理
            sleep(1);
        }
        printf("%s:accept,%s",__func__,strerror(errno));
        return;
    }

    do
    {  //从全局数组mg_events中找一个空闲位置i
        for(i=0;i<MAX_EVENTS;++i){
            if(er_ptr->mg_events[i].m_status==0) break;
        }

        if(i==MAX_EVENTS){
            printf("%s: max connect limit[%d]\n", __func__, MAX_EVENTS);
            break;
        }

        if((flag = fcntl(cfd, F_SETFL, O_NONBLOCK)) < 0) //将cfd设置为非阻塞
        {
            printf("%s: fcntl nonblocking failed, %s\n", __func__, strerror(errno));
            break;
        }

        //找到合适的自定义结构体后,将其添加到树上,监听读事件

        //eventset(&mg_events[i],cfd,recevdata);

        er_ptr->mg_events[i].Myev_set(cfd,recevdata); //必须将recevdata声明为static
        
        er_ptr->eventadd(EPOLLIN,&er_ptr->mg_events[i]);
    }while(0);

    printf("new connect[%s:%d],[time:%ld],pos[%d]\n",inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), er_ptr->mg_events[i].last_active, i);
}

//接受客户端发送的数据 可读事件回调函数
void Epoll_reactor::recevdata(void* erptr,void* arg){

    printf("recevdata      \n");

    Epoll_reactor* er_ptr=(Epoll_reactor*)erptr;
    My_events* myev=(My_events*)arg;

    int len;

    //printf("cfd: %d,myev.m_fd: %d\n",cfd,myev->m_fd);

    len=recv(myev->m_fd,myev->m_buf,sizeof(myev->m_buf),0);

    //1.将该事件从红黑树删除
    er_ptr->eventdel(myev);

    if(len>0){
        myev->m_buf_len=len;
        myev->m_buf[len]='\0';//手动添加结束标记
        printf("CLient[%d]: %s",myev->m_fd,myev->m_buf);

        //2.设置cfd对应的回调函数为发送数据
        myev->Myev_set(senddata);
        //3.将cfd上树,监听写事件
        er_ptr->eventadd(EPOLLOUT,myev);
    }
    else if(len==0){   //客户端关闭连接
        close(myev->m_fd);
        er_ptr->eventdel(myev);
        printf("\n [Client:%d] disconnection \n", myev->m_fd);
    }
    else
  {
      close(myev->m_fd);
      er_ptr->eventdel(myev);
      printf("\n error: [Client:%d] disconnection\n", myev->m_fd);
  }
}

//发送数据,可写事件回调函数
void Epoll_reactor::senddata(void* erptr,void* arg){
    printf("senddata      \n");

    Epoll_reactor* er_ptr=(Epoll_reactor*)erptr;
    My_events* myev=(My_events*)arg;

    int len;

    len=send(myev->m_fd,myev->m_buf,myev->m_buf_len,0); //回写

    //1.将该事件从红黑树删除
    er_ptr->eventdel(myev);
    if(len>0){

        //2.设置cfd对应的回调函数为读取数据
        myev->Myev_set(recevdata);
        //3.cfd上树,监听读事件
        er_ptr->eventadd(EPOLLIN,myev);
    }
    else{
        close(myev->m_fd);
        er_ptr->eventdel(myev);
        printf("\n send[fd=%d] error \n", myev->m_fd);
    }
}

//初始化监听套接字
void Epoll_reactor::initlistensockte(){
    int lfd;
    struct sockaddr_in seraddr;

    int opt=1;
    setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); //端口复用 消除TIME_WAIT
    //创建监听套接字
    lfd=socket(AF_INET,SOCK_STREAM,0);
    //将监听套接字设置非阻塞
    fcntl(lfd,F_SETFL,O_NONBLOCK);    

    bzero(&seraddr,sizeof(seraddr));
    seraddr.sin_family=AF_INET;
    seraddr.sin_addr.s_addr=INADDR_ANY;
    seraddr.sin_port=htons(SERVER_PORT);

    //绑定
    bind(lfd,(struct sockaddr*)&seraddr,sizeof(seraddr));

    //监听上限
    listen(lfd,128);

    printf("Server Running:port[%d]\n", SERVER_PORT);

    //lfd 初始化
    mg_events[MAX_EVENTS-1].Myev_set(lfd,acceptcoonect);

    //监听事件上树
    eventadd(EPOLLIN,&mg_events[MAX_EVENTS-1]);

}

//服务器运行
void Epoll_reactor::server_run(){
    //创建epoll对象
    epfd=epoll_create(MAX_EVENTS);
    if(epfd<0)
        printf("create epfd in %s failed: %s \n",__func__,strerror(errno));

    //初始化监听套接字
    initlistensockte();

    while(1){
        int n_ready=epoll_wait(epfd,evs,MAX_EVENTS,-1);
        if(n_ready<0){
            printf("epoll_wait error!\n");
            break;
        }
        for(int i=0;i<n_ready;++i){
            My_events* myev=(My_events*)evs[i].data.ptr;
            if(evs[i].events&EPOLLIN&&myev->m_events&EPOLLIN)
                myev->call_back(this,myev);
            if(evs[i].events&EPOLLOUT&&myev->m_events&EPOLLOUT)
                myev->call_back(this,myev);
        }
    }
}

int main(){
    Epoll_reactor epractr;
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值