定时器的实现
通过Reactor机制,还可以很容易的实现定时器的功能,使用方式如下。
下面的代码简单的实现了一个定时器,并具有基本的开启,关闭功能。
#include
<ace/OS.h>
#include
<ace/Reactor.h>
class MyTimerHandler : public ACE_Event_Handler
{
private:
int inteval; //执行时间间隔
int delay; //延迟执行时间
int timerid;
public:
MyTimerHandler(int delay,int inteval)
{
this->delay=delay;
this->inteval=inteval;
}
int open() //注册定时器
{
ACE_Time_Value delaytime(inteval);
ACE_Time_Value intevaltime(inteval);
timerid = reactor()->schedule_timer(this,
0, //传递handle_timeout给的参数
delaytime,
intevaltime);
return timerid;
}
int close() //取消定时器
{
return reactor()->cancel_timer(timerid);
}
//定时器回调函数
int handle_timeout (const ACE_Time_Value ¤t_time,
const
void * = 0)
{
time_t epoch = ((timespec_t)current_time).tv_sec;
ACE_DEBUG ((LM_INFO,
ACE_TEXT ("handle_timeout: %s\n"),
ACE_OS::ctime (&epoch)));
return 0;
}
};
int main(int argc, char *argv[])
{
MyTimerHandler * timer = new MyTimerHandler (3,5);
timer->reactor(ACE_Reactor::instance());
timer->open();
for(int i=0;i<2;i++) //触发次handle_timeout事件
{
ACE_OS::printf("\n%d\n",i);
ACE_Reactor::instance()->handle_events();
}
timer->close();
ACE_OS::printf("cancel timer");
while(true)
ACE_Reactor::instance()->handle_events();
return 0;
}
代码功能比较简单,这里就不多做介绍了。
在服务器端使用Reactor框架
使用Reactor框架的服务器端结构如下:
服务器端注册两种事件处理器,ClientAcceptor和ClientService ,ClientService类负责和客户端的通信,每一个ClientService对象对应一个客户端的Socket连接。 ClientAcceptor专门负责被动接受客户端的连接,并创建ClientService对象。这样,在一个N个Socket连接的服务器程序中,将存在1个ClientAcceptor对象和N个ClientService对象。
整个服务器端流程如下:
代码如下:
#include
<ace/OS.h>
#include
<ace/Reactor.h>
#include
<ace/SOCK_Connector.h>
#include
<ace/SOCK_Acceptor.h>
#include
<ace/Auto_Ptr.h>
class ClientService : public ACE_Event_Handler
{
public:
ACE_SOCK_Stream &peer (void) { return
this->sock_; }
int open (void)
{
//注册读就绪回调函数
return
this->reactor ()->register_handler(this, ACE_Event_Handler::READ_MASK);
}
virtual ACE_HANDLE get_handle (void) const { return
this->sock_.get_handle (); }
virtual
int handle_input (ACE_HANDLE fd )
{
//一个简单的EchoServer,将客户端的信息返回
int rev = peer().recv(buf,100);
if(rev<=0)
return -1;
peer().send(buf,rev);
return 0;
}
// 释放相应资源
virtual
int handle_close (ACE_HANDLE, ACE_Reactor_Mask mask)
{
if (mask == ACE_Event_Handler::WRITE_MASK)
return 0;
mask = ACE_Event_Handler::ALL_EVENTS_MASK |
ACE_Event_Handler::DONT_CALL;
this->reactor ()->remove_handler (this, mask);
this->sock_.close ();
delete
this; //socket出错时,将自动删除该客户端,释放相应资源
return 0;
}
protected:
char buf[100];
ACE_SOCK_Stream sock_;
};
class ClientAcceptor : public ACE_Event_Handler
{
public:
virtual ~ClientAcceptor (){this->handle_close (ACE_INVALID_HANDLE, 0);}
int open (const ACE_INET_Addr &listen_addr)
{
if (this->acceptor_.open (listen_addr, 1) == -1)
{
ACE_OS::printf("open port fail");
return -1;
}
//注册接受连接回调事件
return
this->reactor ()->register_handler(this, ACE_Event_Handler::ACCEPT_MASK);
}
virtual ACE_HANDLE get_handle (void) const
{ return
this->acceptor_.get_handle (); }
virtual
int handle_input (ACE_HANDLE fd )
{
ClientService *client = new ClientService();
auto_ptr<ClientService> p (client);
if (this->acceptor_.accept (client->peer ()) == -1)
{
ACE_OS::printf("accept client fail");
return -1;
}
p.release ();
client->reactor (this->reactor ());
if (client->open () == -1)
client->handle_close (ACE_INVALID_HANDLE, 0);
return 0;
}
virtual
int handle_close (ACE_HANDLE handle,
ACE_Reactor_Mask close_mask)
{
if (this->acceptor_.get_handle () != ACE_INVALID_HANDLE)
{
ACE_Reactor_Mask m = ACE_Event_Handler::ACCEPT_MASK |
ACE_Event_Handler::DONT_CALL;
this->reactor ()->remove_handler (this, m);
this->acceptor_.close ();
}
return 0;
}
protected:
ACE_SOCK_Acceptor acceptor_;
};
int main(int argc, char *argv[])
{
ACE_INET_Addr addr(3000,"192.168.1.142");
ClientAcceptor server;
server.reactor(ACE_Reactor::instance());
server.open(addr);
while(true)
{
ACE_Reactor::instance()->handle_events();
}
return 0;
}
代码功能比较简单,需要注意以下几点:
在Socket编程中,常见的事件就是"读就绪","写就绪",通过对这两个事件的捕获分发,可以实现Socket中的异步操作。
Socket编程中的事件处理器
在前面我们已经介绍过,在ACE反应器框架中,任何都必须派生自ACE_Event_Handler类,并通过重载其相应会调事件处理函数来实现相应的回调处理的。在Socket编程中,我们通常需要重载的函数有
此外,为了使Reactor能通过I/O句柄找到对应的事件处理器,还必须重载其get_handle()方法以使得Reactor建立起I/O句柄和事件处理器的关联。
使用Reactor框架。
下面我们将以一个客户端的程序为例,介绍如何在Socket编程中使用Reactor框架。
一.建立一个客户端对象(事件处理器)。
客户端对象就是一个事件处理器,其声明如下:
class Client:public ACE_Event_Handler
{
public:
ACE_HANDLE get_handle(void) const;
int handle_input (ACE_HANDLE fd);
int handle_close (ACE_HANDLE handle,
ACE_Reactor_Mask close_mask);
ACE_SOCK_Stream& Peer();
private:
ACE_SOCK_Stream peer;
};
在Client端中我只关心"读就绪"事件,故只重载了handle_input函数(大多数应用下只需要重载handle_input函数)。另外,在客户端还保存了一个ACE_SOCK_Stream的peer对象用来进行Socket通信,同时封装了一个Peer()函数返回它的引用。
二.重载相应回调处理函数
ACE_SOCK_Stream& Client::Peer()
{
return peer;
}
ACE_HANDLE Client::get_handle(void) const
{
return peer.get_handle();
}
int Client::handle_input (ACE_HANDLE fd)
{
int rev=0;
if((rev = peer.recv(buffer,1000))>0)
{
buffer[rev]='\0';
cout<<endl<<"rev:\t"<<buffer<<endl;
return 0;
}
else //Socket连接发生错误,返回-1,在Reactor中注销事件,触发handle_close函数
{
return -1;
}
}
int Client::handle_close (ACE_HANDLE handle,
ACE_Reactor_Mask close_mask)
{
cout<<endl<<"connecetd closed";
return ACE_Event_Handler::handle_close(handle,close_mask);
}
几个函数的功能都非常简单,这里就不多做介绍了。
三.在Reactor中注册事件
首先让我们来看看相应的main函数的代码:
int main(int argc, char *argv[])
{
Client client;
ACE_SOCK_Connector connector;
ACE_INET_Addr addr(3000,"127.0.0.1");
ACE_Time_Value timeout(5,0);
if(connector.connect(client.Peer(),addr,&timeout) != 0)
{
cout<<endl<<"connecetd fail";
return 0;
}
ACE_Reactor::instance()->register_handler(&client,ACE_Event_Handler::READ_MASK);
while(true)
{
ACE_Reactor::instance()->handle_events();
}
return 0;
}
在这里可以看到,使用Reactor框架后,依然首先通过ACE_SOCK_Connector的connect函数来建立连接。建立连接后,可以通过ACE_Reactor::instance()->register_handler函数来实现Reactor的注册,实现I/O事件和Client对象的handle_input方法相关联,它的第一个参数是事件处理器的地址,第二个参数是事件类型,由于这里只关心读就绪事件,故注册的事件类型是ACE_Event_Handler::READ_MASK。
四.启动Reactor事件循环
通过如上设置后,我们就可以通过ACE_Reactor::instance()->handle_events()启动Reactor循环了,这样,每当服务器端有数据发送给客户端时,当客户端的数据就绪时,就回触发Client对象的handle_input函数,将接收的数据打印出来。
通常的做法是,将Reactor事件循环作为一个单独的线程来处理,这样就不会阻塞main函数。
五.注销Reactor事件
Reactor事件的注销一般有两种方式,显式和隐式,下面将分别给予介绍。
在这个示例程序里,连接方只有一个Socket连接,Reactor的优势并没有体现出来,但在一些网络管理系统里,连接方需要对多个需要管理的设备(服务器端)进行连接,在这种情况下使用Reactor模式,只需要多开一个Reactor事件循环线程就能实现事件多路分发复用,并且不会阻塞,通过面向对象的回调方式管理,使用起来非常方便。
Reactor框架的另外一个常用的地方就是服务器端,一般是一个服务器端对应多个客户端,这样用Reactor模式能大幅提高并发能力,这方面的编程方法将在下一章给与介绍。