C++开发框架 -ACE(反应器)
1 事件处理器
反应器模式在 ACE 中被实现为 ACE_Reactor 类,它提供反应器框架的功能接口。
该框架提供名为 ACE_Event_Handler 的抽象接口类,所有应用特有的事件处理器都必须由此派生(这使得应用特有的处理器都具有相同的类型,即 ACE_Event_Handler,所以它们可以相互替换)。
ACE_Event_Handler 类拥有若干不同的“handle”(处理)方法,每个处理方法被用于处理不同种类的事件。当应用程序员对特定事件感兴趣时,他就对 ACE_Event_Handler 类进行子类化,并实现他感兴趣的处理方法。如上面所提到的,随后他就在反应器上为特定事件“登记”他的事件处理器类。于是反应器就会保证在此事件发生时,自动回调在适当的事件处理器对象中的适的”handle”方法。
使用 ACE_Reactor 基本上有三个步骤:
- 创建 ACE_Event_Handler 的子类,并在其中实现适当的“handle_”方法,以处理你想要此事件处理器为之服务的事件类型。(参看表 1 来确定你需要实现哪一个“handle_”方法。注意你可以使用同一个事件处理器对象处理多种类型的事件,因而可以重载多于一个的“handle_”方法。)
- 通过调用反应器对象的 register_handler(),将你的事件处理器登记到反应器。
- 在事件发生时,反应器将自动回调相应的事件处理器对象的适当的“handle_”方法。
下面的简单例子可以帮助我们更好地理解这些步骤:
#include <signal.h>
#include "ace/Reactor.h"
#include "ace/Log_Msg.h"
#include "ace/Event_Handler.h"
//Create our subclass to handle the signal events
//that we wish to handle. Since we know that this particular
//event handler is going to be using signals we only overload the
//handle_signal method.
class MyEventHandler: public ACE_Event_Handler
{
int handle_signal(int signum, siginfo_t*,ucontext_t*)
{
switch(signum)
{
case SIGWINCH:
ACE_DEBUG((LM_DEBUG, "You pressed SIGWINCH \n"));
break;
case SIGINT:
ACE_DEBUG((LM_DEBUG, "You pressed SIGINT \n"));
break;
}
return 0;
}
};
int main(int argc, char *argv[])
{
// 实例化事件处理器
MyEventHandler *eh =new MyEventHandler;
// 注册信号处理器
ACE_Reactor::instance()->register_handler(SIGWINCH,eh);
ACE_Reactor::instance()->register_handler(SIGINT,eh);
// 启动 Reactor 事件循环
while(1)
//Start the reactors event loop
ACE_Reactor::instance()->handle_events();
}
在上面的例子中,我们首先创建了一个 ACE_Event_Handler 的子类,在其中我们重载了 handle_signal()方法,因为我们想要使用此处理器来处理多种类型的信号。在主函数中,我们对我们的处理器进行实例化,随后调用 ACE_Reactor 单体(Singleton)的 register_handler,指明我们希望在 SIGWINCH(终端窗口改变信号)或 SIGINT(中断信号,通常是^C)发生时,事件处理器“eh”会被回调。然后,我们通过调用在无限循环中调用 handle_events()来启动反应器的事件循环。无论是发生哪一个事件,反应器都将自动回调 eh->handle_signal()方法,将引发回调的信号号码、以及 siginfo_t 结构(有关 siginfo_t 的更多信息,参见 siginfo.h)传给它。
注意程序是怎样使用单体模式(Singleton Pattern)来获取全局反应器对象的引用的。大多数应用都只需要一个反应器,因而 ACE_Reactor::instance()会确保无论何时此方法被调用,都会返回同一个ACE_Reactor 实例。
ACE_Event_Handler 中的处理方法 | 在子类中重载,所处理事件的类型: |
---|---|
handle_signal() | 信号。当任何在反应器上登记的信号发生时,反应器自动回调该方法。 |
handle_input() | 来自 I/O 设备的输入。当 I/O 句柄(比如 UNIX 中的文件描述符)上的输入可用时,反应器自动回调该方法。 |
handle_exception() | 异常事件。当已在反应器上登记的异常事件发生时(例如,如果收到 SIGURG(紧急信号)),反应器自动回调该方法。 |
handle_timeout() | 定时器。当任何已登记的定时器超时的时候,反应器自动回调该方法。 |
handle_output() | I/O 设备输出。当 I/O 设备的输出队列有可用空间时,反应器自动回调该方法。 |
1.1 事件处理器登记
如我们在上面的例子中所看到的,登记事件处理器、以处理特定事件,是在反应器上调用register_handler()方法来完成的。register_handler()方法是重载方法,就是说,实际上有若干方法可用于登记不同的事件类型,每个方法都叫做 register_handler()。但是它们有着不同的特征:它们的参数各不相同。基本上,register_handler()方法采用 handle/event_handle 元组或 signal/event_handler 元组作为参数,并将它们加入反应器的内部分派表。当有事件在 handle 上发生时,反应器在它的内部分派表中查找相应的event_handler,并自动在它找到的 event_handler 上回调适当的方法。
1.2 事件处理器的拆除和生存期管理
一旦所需的事件被处理后,可能就无需再让事件处理器登记在反应器上。因而,反应器提供了从它的内部分派表中拆除事件处理器的技术。一旦事件处理器被拆除,它就不再会被反应器回调。
为多个客户服务的服务器是这种情况的一个例子。客户连接到服务器,让它完成一些工作,然后从服务器断开。
当有新的客户连接到服务器时,一个事件服务器对象被实例化,并登记到服务器的反应器上,以处理所有与此客户有关的 I/O。当客户断开时,服务器必须将事件处理器从反应器的分派队列中拆
除,因为它将不再进行任何与此客户有关的 I/O。
在此例中,客户/服务器连接可能会被关闭,使得 I/O 句柄(UNIX 中的文件描述符)变得无效。把这样的死掉的句柄从反应器里拆除是很重要的,因为,如果不这样做,反应器将会把此句柄标记为“读就绪”,并会持续不断地回调此事件处理器的 handle_input()方法。
1.2.1 从反应器内部分派表中隐式拆除事件处理器
隐式拆除是更为常用的从反应器中拆除事件处理器的技术。事件处理器的每个“handle_”方法都会返回一个整数给反应器。如果此整数为 0,在处理器方法完成后、事件处理器将保持在反应器上的登记。
但是,如果“handle_”方法返回的整数<0,反应器将自动回调此事件处理器的 handle_close()方法,并将它从自己的内部分派表中拆除。handle_close()方法用于执行处理器特有的任何清除工作,它们需要在事件处理器被拆除前完成;其中可以包括像删除处理器申请的动态内存、或关闭日志文件这样的工作。
在上面所描述的例子中,必须将事件处理器从内存中实际清除。这样的清除也可以发生在具体事件处理器类的 handle_close()方法中。设想下面的具体事件处理器:
class MyEventHandler: public ACE_Event_Handler
{
public:
MyEventHandler()
{
//construct internal data members
}
virtual int handle_close(ACE_HANDLE handle, ACE_Reactor_Mask mask) override
{
delete this; //commit suicide
}
~MyEventHandler()
{
//destroy internal data members
}
private:
//internal data members
};
在从反应器注销、以及 handle_close()挂钩方法被调用时,该类将自己删除。但是,必须保证MyEventHandler 总是动态分配的,否则,全局内存堆可能会崩溃。确保类总是动态地创建的一种办法是将析构器移动到类的私有区域去。例如:
class MyEventHandler: public ACE_Event_Handler
{
public:
MyEventHandler()
{
//construct internal data members
}
virtual int handle_close(ACE_HANDLE handle, ACE_Reactor_Mask mask) override
{
delete this; //commit suicide
}
private:
//class must be allocated dynamically
~MyEventHandler()
{
//destroy internal data members
}
};
1.2.2 从反应器内部分派表中显式拆除事件处理器
另一种从反应器的内部表中拆除事件处理器的方法是显式地调用反应器的 remove_ha