reactor是一种高效的服务端模式,实质是使用io多路复用,比如select、epoll、poll等,关于epoll和reactor的详细介绍往上也有很多的博文,这里贴出reactor的一篇博客:reactor模型
reactor模型的几种角色:
处理流程:
当应用向Initiation Dispatcher注册Concrete Event Handler时,应用会标识出该事件处理器希望Initiation Dispatcher在某种类型的事件发生发生时向其通知,事件与handle关联
Initiation Dispatcher要求注册在其上面的Concrete Event Handler传递内部关联的handle,该handle会向操作系统标识
当所有的Concrete Event Handler都注册到 Initiation Dispatcher上后,应用会调用handle_events方法来启动Initiation Dispatcher的事件循环,这时Initiation Dispatcher会将每个Concrete Event Handler关联的handle合并,并使用Synchronous Event Demultiplexer来等待这些handle上事件的发生
当与某个事件源对应的handle变为ready时,Synchronous Event Demultiplexer便会通知 Initiation Dispatcher。比如tcp的socket变为ready for reading
Initiation Dispatcher会触发事件处理器的回调方法。当事件发生时, Initiation Dispatcher会将被一个“key”(表示一个激活的handle)定位和分发给特定的Event Handler的回调方法
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;
}
}
}