使用c++实现简单的reactor模式

reactor是一种高效的服务端模式,实质是使用io多路复用,比如select、epoll、poll等,关于epoll和reactor的详细介绍往上也有很多的博文,这里贴出reactor的一篇博客:reactor模型

reactor模型的几种角色:

  1. handle 即文件描述符
  2. Synchronous Event Demultiplexer 即IO多路复用
  3. Event Handler 即处理事件的回调函数
  4. Concrete Event Handler 即具体的实现逻辑处理的函数
  5. Initiation Dispatcher 即初始分发器 也是reactor角色,提供了注册、删除与转发event handler的方法

处理流程:

  1. 当应用向Initiation Dispatcher注册Concrete Event Handler时,应用会标识出该事件处理器希望Initiation Dispatcher在某种类型的事件发生发生时向其通知,事件与handle关联

  2. Initiation Dispatcher要求注册在其上面的Concrete Event Handler传递内部关联的handle,该handle会向操作系统标识

  3. 当所有的Concrete Event Handler都注册到 Initiation Dispatcher上后,应用会调用handle_events方法来启动Initiation Dispatcher的事件循环,这时Initiation Dispatcher会将每个Concrete Event Handler关联的handle合并,并使用Synchronous Event Demultiplexer来等待这些handle上事件的发生

  4. 当与某个事件源对应的handle变为ready时,Synchronous Event Demultiplexer便会通知 Initiation Dispatcher。比如tcp的socket变为ready for reading

  5. Initiation Dispatcher会触发事件处理器的回调方法。当事件发生时, Initiation Dispatcher会将被一个“key”(表示一个激活的handle)定位和分发给特定的Event Handler的回调方法

  6. Initiation Dispatcher调用特定的Concrete Event Handler的回调方法来响应其关联的handle上发生的事件

基于上面的理解 ,我用c++实现了一个简单的reactor模式 只是简单的连接然后客服端发生数据给服务器,服务器暂时还没有回发数据给客户端。

下面是具体的代码:

Epoll类实现IO多路复用,监听客户端新的连接以及已经连接fd的读写事件

//=======Epoll===============================================
//IO多路复用
class Epoll{
public:
    Epoll();
    ~Epoll();


    using NewConnectionCallback = std::function<void()>; 
    using HandleReadCallback = std::function<void(TcpTool*)>;
    using HandleWriteCallback = std::function<void(TcpTool*)>;
    using CloseConnectionCallback = std::function<void(TcpTool*)>;
    int add(int fd,TcpTool * tool,int events);
    int mod(int fd,TcpTool * tool,int events);
    int del(int fd,TcpTool * tool,int events);
    int wait(int timeouts);

    void handleEvents(int listenFd,int eventsNum);


    //事件回调函数
    void setNewConnection(const NewConnectionCallback &cb){
        _connentioncb = cb;
    }
    void setCloseConnection(const CloseConnectionCallback &cb){
        _closecb = cb;
    }
    void setWriteCb(const HandleWriteCallback &cb){
        _writecb = cb;
    }
    void setReadCb(const HandleReadCallback &cb){
        _readcb = cb;
    }
private:
    using eventList = std::vector<struct epoll_event>;

    int _epollFd;
    eventList  _events;  //事件集合
    NewConnectionCallback _connentioncb;
    HandleReadCallback  _readcb;
    HandleWriteCallback _writecb;
    CloseConnectionCallback _closecb;
};

//epoll

Epoll::Epoll():_epollFd(::epoll_create(MAXEVENTS)),_events(MAXEVENTS){
    assert(_epollFd >=0);
}

Epoll::~Epoll(){
    ::close(_epollFd);      
}

int Epoll::add(int fd,TcpTool * tool,int events){
    struct epoll_event event;
    event.data.ptr = (void *) tool;
    event.events = events;

    int ret = ::epoll_ctl(_epollFd,EPOLL_CTL_ADD,fd,&event);
    return ret;
}

int Epoll::del(int fd,TcpTool * tool,int events){
    struct epoll_event event;
    event.data.ptr = (void *) tool;
    event.events = events;

    int ret = ::epoll_ctl(_epollFd,EPOLL_CTL_DEL,fd,&event);
    return ret;
}

int Epoll::mod(int fd,TcpTool * tool,int events){
    struct epoll_event event;
    event.data.ptr = (void *) tool;
    event.events = events;

    int ret = ::epoll_ctl(_epollFd,EPOLL_CTL_MOD,fd,&event);
    return ret;
}

int Epoll::wait(int timeouts){
    int eventsNum = ::epoll_wait(_epollFd,&*_events.begin(), static_cast<int>(_events.size()),timeouts);
    if(eventsNum == 0){

    }else if(eventsNum < 0){

    }

    return eventsNum;       
}

//监听事件并分发
void Epoll::handleEvents(int listenFd,int eventsNum){
    assert(eventsNum > 0);
    for(int i = 0;i < eventsNum;++i){
        TcpTool * tcptool = (TcpTool *)(_events[i].data.ptr);
        int fd = tcptool->getFd();

        if(fd == listenFd){
            _connentioncb();  //回调
        }else if(_events[i].events & EPOLLIN){  // 可读
            _readcb(tcptool);
            
        }else if(_events[i].events & EPOLLOUT){  //可写
            _writecb(tcptool);
        }
        
    }

    return ;
}

Epoll类就是实现了IO多路复用的基本功能

TcpServer类:充当了reactor角色的类
其中最重要的是run函数:注册回调----->循环监听------>事件分发
其他的函数相当于是Event Handler的角色,即事件回调函数

//========server===================================================================

//起到了一个reactor角色作用

class TcpServer{
public:
    TcpServer(int port):_port(port),  //6666
    _listenFd(createListenFd(_port)),
    _listenTool(new TcpTool(_listenFd)),
    _epoll(new Epoll()){
        //非阻塞
    assert(_listenFd >= 0);
    }



    void run();

private:
//连接、断开
    void _acceptConnection();
    void _closeConnection(TcpTool *tool);
    //处理读写
    void _handleWrite(TcpTool *tool);
    void _handleRead(TcpTool * tool);
private:
    using EpollPtr = std::unique_ptr<Epoll>;
    using ListenTcpToolPtr = std::unique_ptr<TcpTool>;
private:
    int _port;
    int _listenFd;
    ListenTcpToolPtr _listenTool;   //
    EpollPtr _epoll;  //只能有一个epoll实例,所以使用unique_ptr  自动释放

    
};


void TcpServer::_acceptConnection (){
    while(1){
        struct sockaddr_in  clitaddr;
        socklen_t len ;
        int acceptFd = ::accept(_listenFd,(struct sockaddr*)&clitaddr,&len);
        if(acceptFd == -1){
            if(errno == EAGAIN){
                break;
            }
            std::cout<<"accept error!"<<endl;
    }

    TcpTool * tool = new TcpTool(acceptFd);  //
    _epoll->add(acceptFd,tool,(EPOLLIN|EPOLLET));

    }
}

void TcpServer::_closeConnection (TcpTool *tool){
    int fd = tool->getFd();
    _epoll->del(fd,tool,0);
    delete tool;
    tool = nullptr;
}

void TcpServer::_handleWrite (TcpTool * tool){
    tool->write();
}

void TcpServer::_handleRead  (TcpTool * tool){
    int fd = tool->getFd();
    int nRead = tool->read();
    if(nRead == 0 || (nRead <0 && (errno != EAGAIN))){
        _closeConnection(tool);
        return ;
    }

    if(nRead < 0 && errno == EAGAIN){
        _epoll->mod(fd,tool,(EPOLLIN));
        return ; 
    }
}


void TcpServer::run(){
//注册回调函数
    _epoll->add(_listenFd,_listenTool.get(),(EPOLLIN | EPOLLET));
    _epoll->setNewConnection(std::bind(&TcpServer::_acceptConnection,this));
    _epoll->setWriteCb(std::bind(&TcpServer::_handleWrite,this,std::placeholders::_1));
    _epoll->setReadCb(std::bind(&TcpServer::_handleRead,this,std::placeholders::_1));

//开始事件循环
    while(1){
        int eventsNum = _epoll->wait(-1);
        if(eventsNum > 0){
        //事件分发
            _epoll->handleEvents(_listenFd,eventsNum);
        }
    }
}

TcpTool类:具体的事件回调逻辑处理函数,相当于一个Concrete Event Handler角色

//==================handle==================================
//处理读写的class   具体的handle实例
class TcpTool{
public:
    TcpTool(int fd):_fd(fd){
        assert(_fd >= 0);
    }
    ~TcpTool(){close(_fd);}


public:
    int getFd(){return _fd;}
    int read();
    int write();

private:
    int _fd;
};
//TCPTOOL
int TcpTool::read(){
    char buffer[BUFFERSIE];
    memset(buffer,0,sizeof(buffer));
    int ret = recv(_fd,buffer,sizeof(buffer),0);

    if(ret == 0){ //断开连接
        std::cout<<"link error!"<<std::endl;
    }else if(ret > 0)
    {
         cout<<"recv from clit:"<<buffer<<endl;
    }else if(ret == -1){
        cout<<"recv error!"<<endl;
        cout<<strerror(errno)<<endl;
    }

    return ret;
}

int TcpTool::write(){
    char buffer[BUFFERSIE];
    memset(buffer,0,sizeof(buffer));
    const char * str = "This is csl sever!";
    memcpy(buffer,str,sizeof(str));
    int ret = send(_fd,buffer,sizeof(buffer),0);
    if(ret > 0)
    {
         cout<<"send success!"<<endl;
    }else if(ret == -1){
        cout<<"send error!"<<endl;
    }

    return ret;
}

由于写的比较仓促,没有分多文件,所有的代码都在一个文件里面。同时也借鉴了网上大佬的一些代码,希望涉及侵权联系我删除
代码涉及到一些c++11,不懂的可以查一下,比较好理解

如果代码有错误或者不符合reactor模型的地方还望指出

下面是整体代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


using namespace std;
#define  MAXEVENTS  1024  //events
#define  BUFFERSIE  1024
class TcpServer;
class Epoll;

//==================handle==================================
//处理读写的class   具体的handle实例
class TcpTool{
public:
    TcpTool(int fd):_fd(fd){
        assert(_fd >= 0);
    }
    ~TcpTool(){close(_fd);}


public:
    int getFd(){return _fd;}
    int read();
    int write();

private:
    int _fd;
};
//TCPTOOL
int TcpTool::read(){
    char buffer[BUFFERSIE];
    memset(buffer,0,sizeof(buffer));
    int ret = recv(_fd,buffer,sizeof(buffer),0);

    if(ret == 0){ //断开连接
        std::cout<<"link error!"<<std::endl;
    }else if(ret > 0)
    {
         cout<<"recv from clit:"<<buffer<<endl;
    }else if(ret == -1){
        cout<<"recv error!"<<endl;
        cout<<strerror(errno)<<endl;
    }

    return ret;
}

int TcpTool::write(){
    char buffer[BUFFERSIE];
    memset(buffer,0,sizeof(buffer));
    const char * str = "This is csl sever!";
    memcpy(buffer,str,sizeof(str));
    int ret = send(_fd,buffer,sizeof(buffer),0);
    if(ret > 0)
    {
         cout<<"send success!"<<endl;
    }else if(ret == -1){
        cout<<"send error!"<<endl;
    }

    return ret;
}


//=======Epoll===============================================
//IO多路复用
class Epoll{
public:
    Epoll();
    ~Epoll();


    using NewConnectionCallback = std::function<void()>;  //相当于定义了一种函数类型 返回值为void 没有参数
    using HandleReadCallback = std::function<void(TcpTool*)>;
    using HandleWriteCallback = std::function<void(TcpTool*)>;
    using CloseConnectionCallback = std::function<void(TcpTool*)>;
    int add(int fd,TcpTool * tool,int events);
    int mod(int fd,TcpTool * tool,int events);
    int del(int fd,TcpTool * tool,int events);
    int wait(int timeouts);

    void handleEvents(int listenFd,int eventsNum);


    //事件回调函数
    void setNewConnection(const NewConnectionCallback &cb){
        _connentioncb = cb;
    }
    void setCloseConnection(const CloseConnectionCallback &cb){
        _closecb = cb;
    }
    void setWriteCb(const HandleWriteCallback &cb){
        _writecb = cb;
    }
    void setReadCb(const HandleReadCallback &cb){
        _readcb = cb;
    }
private:
    using eventList = std::vector<struct epoll_event>;

    int _epollFd;
    eventList  _events;  //事件集合
    NewConnectionCallback _connentioncb;
    HandleReadCallback  _readcb;
    HandleWriteCallback _writecb;
    CloseConnectionCallback _closecb;
};

//epoll

Epoll::Epoll():_epollFd(::epoll_create(MAXEVENTS)),_events(MAXEVENTS){
    assert(_epollFd >=0);
}

Epoll::~Epoll(){
    ::close(_epollFd);      
}

int Epoll::add(int fd,TcpTool * tool,int events){
    struct epoll_event event;
    event.data.ptr = (void *) tool;
    event.events = events;

    int ret = ::epoll_ctl(_epollFd,EPOLL_CTL_ADD,fd,&event);
    return ret;
}

int Epoll::del(int fd,TcpTool * tool,int events){
    struct epoll_event event;
    event.data.ptr = (void *) tool;
    event.events = events;

    int ret = ::epoll_ctl(_epollFd,EPOLL_CTL_DEL,fd,&event);
    return ret;
}

int Epoll::mod(int fd,TcpTool * tool,int events){
    struct epoll_event event;
    event.data.ptr = (void *) tool;
    event.events = events;

    int ret = ::epoll_ctl(_epollFd,EPOLL_CTL_MOD,fd,&event);
    return ret;
}

int Epoll::wait(int timeouts){
    int eventsNum = ::epoll_wait(_epollFd,&*_events.begin(), static_cast<int>(_events.size()),timeouts);
    if(eventsNum == 0){

    }else if(eventsNum < 0){

    }

    return eventsNum;       
}


void Epoll::handleEvents(int listenFd,int eventsNum){
    assert(eventsNum > 0);
    for(int i = 0;i < eventsNum;++i){
        TcpTool * tcptool = (TcpTool *)(_events[i].data.ptr);
        int fd = tcptool->getFd();

        if(fd == listenFd){
            _connentioncb();  //回调
        }else if(_events[i].events & EPOLLIN){  // 可读
            _readcb(tcptool);
            
        }else if(_events[i].events & EPOLLOUT){  //可写
            _writecb(tcptool);
        }
        
    }

    return ;
}




//========server===================================================================
int createListenFd(int port){
    //防止port
    int _listenFd  = 0;
     _listenFd =  ::socket(AF_INET,SOCK_STREAM | SOCK_NONBLOCK,0);
    struct sockaddr_in  _servAddr;
    port = ((port <= 1024) || (port >=65536)) ? 6666:port;
    ::memset(&_servAddr,0,sizeof(_servAddr));
    _servAddr.sin_family = AF_INET;
    _servAddr.sin_port = ::htons((unsigned short)port);
    _servAddr.sin_addr.s_addr = ::htonl(INADDR_ANY);
    if((::bind(_listenFd,(struct sockaddr*) &_servAddr,sizeof(_servAddr))) == -1){
        std::cout<<"bind error!"<<std::endl;
        return -1;
    }

    if((::listen(_listenFd,5)) == -1){
        std::cout<<"listen error!"<<std::endl;
        return -1;
    }

    return _listenFd;
}

//起到了一个reactor角色作用

class TcpServer{
public:
    TcpServer(int port):_port(port),  //6666
    _listenFd(createListenFd(_port)),
    _listenTool(new TcpTool(_listenFd)),
    _epoll(new Epoll()){
        //非阻塞
    assert(_listenFd >= 0);
    }



    void run();

private:
    void _acceptConnection();
    void _closeConnection(TcpTool *tool);
    //处理读写
    void _handleWrite(TcpTool *tool);
    void _handleRead(TcpTool * tool);
private:
    using EpollPtr = std::unique_ptr<Epoll>;
    using ListenTcpToolPtr = std::unique_ptr<TcpTool>;
private:
    int _port;
    int _listenFd;
    ListenTcpToolPtr _listenTool;   //
    EpollPtr _epoll;  //只能有一个epoll实例,所以使用unique_ptr  自动释放

    
};


void TcpServer::_acceptConnection (){
    while(1){
        struct sockaddr_in  clitaddr;
        socklen_t len ;
        int acceptFd = ::accept(_listenFd,(struct sockaddr*)&clitaddr,&len);
        if(acceptFd == -1){
            if(errno == EAGAIN){
                break;
            }
            std::cout<<"accept error!"<<endl;
    }

    TcpTool * tool = new TcpTool(acceptFd);  //
    _epoll->add(acceptFd,tool,(EPOLLIN|EPOLLET));

    }
}

void TcpServer::_closeConnection (TcpTool *tool){
    int fd = tool->getFd();
    _epoll->del(fd,tool,0);
    delete tool;
    tool = nullptr;
}

void TcpServer::_handleWrite (TcpTool * tool){
    tool->write();
}

void TcpServer::_handleRead  (TcpTool * tool){
    int fd = tool->getFd();
    int nRead = tool->read();
    if(nRead == 0 || (nRead <0 && (errno != EAGAIN))){
        _closeConnection(tool);
        return ;
    }

    if(nRead < 0 && errno == EAGAIN){
        _epoll->mod(fd,tool,(EPOLLIN));
        return ; 
    }
}


void TcpServer::run(){
    _epoll->add(_listenFd,_listenTool.get(),(EPOLLIN | EPOLLET));
    _epoll->setNewConnection(std::bind(&TcpServer::_acceptConnection,this));
    _epoll->setWriteCb(std::bind(&TcpServer::_handleWrite,this,std::placeholders::_1));
    _epoll->setReadCb(std::bind(&TcpServer::_handleRead,this,std::placeholders::_1));

//开始事件循环
    while(1){
        int eventsNum = _epoll->wait(-1);
        if(eventsNum > 0){
            _epoll->handleEvents(_listenFd,eventsNum);
        }
    }
}


int main(int argc ,char ** argv){
    int port = 6666;
    if(argc >= 2){
        port = atoi(argv[1]);
    }

    TcpServer serv(port);
    serv.run();

    return 0;


客户端代码:


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include   //htons
#include  // memset
#include  // inet_addr
using namespace std;

int main(){
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
 //   assert(sockfd == 0);

    struct sockaddr_in servAddr;
    memset(&servAddr,0,sizeof(servAddr));

    servAddr.sin_family = AF_INET;
    servAddr.sin_port = htons(6666);
    servAddr.sin_addr.s_addr = inet_addr("192.168.10.128");

    int ret = connect(sockfd,(struct sockaddr*)&servAddr,sizeof(servAddr));
    if(ret == -1){
       cout<<"connect eerror"<<endl;
       cout<<strerror(errno)<<endl;
    }
   // assert(ret == 0);

    while(1){
    char sendbuffer[1024];
    memset(sendbuffer,0,sizeof(sendbuffer));
   // int ret = recv(sockfd,buffer,sizeof(buffer),0);
   //
   cout<<"Please Input:"<<endl;
   cin>>sendbuffer;
   send(sockfd,sendbuffer,sizeof(sendbuffer),0);
   char recvbuf[1024];
   int ret = recv(sockfd,recvbuf,sizeof(recvbuf),0);
   if(ret){
	   cout<<"recv serv:"<<recvbuf<<endl;
   }else if(ret == 0){
	   cout<<"link failure!"<<endl;  //duankai
	   close(sockfd);
	   break;
   }

     }

}

`实现结果:
使用c++实现简单的reactor模式_第1张图片

你可能感兴趣的:(x小实验,epoll)