Ru-Brd |
7.2 The ACE_Svc_Handler ClassMotivationChapter 2 defined a service as a set of functionality offered to a client by a server. A service handler is the portion of a networked application that either implements or accesses (or both, in the case of a peer-to-peer arrangement) a service. Connection-oriented networked applications require at least two communicating service handlers梠ne for each end of every connection. Incidentally, applications using multicast or broadcast communication may have multiple service handlers. Although these connectionless communication protocols don't cleanly fit the Acceptor-Connector model, the ACE_Svc_Handler class is often a good choice for implementing a service handler and should be considered. 第二章,我们将服务定义为服务器提供给客户的一组功能。服务处理器则是网络化应用,应用与实现和访问服务的一部分。面向连接的网络应用程序需要至少两个通讯服务处理器,分别用于每一端的连接。顺便说一下,应用程序使用多点传送或者广播通信的应用可能会有多个服务处理器。尽管这些无连接的通信协议并不完全适合Acceptor-Connector模型,ACE_Svc_Handler类常常是实现服务处理器的一种好的选择。 When designing the service handlers involved in a service, developers should also take into account the communication design dimensions discussed in Chapter 1 of C++NPv1. In general, the application functionality defined by a service handler can be decoupled from the following design aspects: 在设计服务中所涉及的服务处理器时,开发着还应该注意第一章中所讨论的通信设计维度。一般而言,服务处理器所定义的应用功能可以与下列设计方面解除耦合。
In general, connection/authentication protocols and service initialization strategies change less frequently than the service handler functionality implemented by an application. To separate these concerns and allow developers to focus on the functionality of their service handlers, the ACE Acceptor-Connector framework defines the ACE_Svc_Handler class. 通常,连接认证协议和服务初始化策略的变动频度比应用实现的服务处理器功能变动的频度要大。为了分离这些事务,并让开发者专注与他们的服务处理器的功能,ACE Acceptor_Connector框架定义了Svc_Handler类。 Class CapabilitiesACE_Svc_Handler is the basis of ACE's synchronous and reactive data transfer and service processing mechanisms. This class provides the following capabilities: ACE_Svc_Handler是ACE的同步和反应式数据传输和服务处理机制的基础。这个类提供以下性能:
The interface for ACE_Svc_Handler is shown in Figure 7.2 (page 208). As shown in the figure, this class template is parameterized by: ACE_Svc_Handler的接口显示如下。 Figure 7.2. The ACE_Svc_Handler Class
Sidebar 40 (page 165) describes the C++ traits and traits class idioms. Since ACE_Svc_Handler is a descendant of ACE_Event_Handler, an instance of it can be registered with the ACE Reactor framework for various types of events. For example, it can be registered to handle READ and WRITE events. Its handle_input() and handle_output() hook methods will then be dispatched automatically by a reactor when its data-mode socket handle is ready to receive or send data, respectively. 因为ACE_Svc_Handler是ACE_Event_Handler的后代,他的实例可以注册到ACE Reactor框架中来处理各种类型事件。例如,他可以注册来处理READ和WRITE事件。他的handle_input()和handle_output() hook方法将被自动分发当他的数据模式的socket句柄准备接收或者发送数据。 The ACE_Svc_Handler class has a rich interface that exports both its capabilities and the capabilities of its parent classes. We therefore group the description of its methods into the three categories described below. ACE_Svc_Handler类拥有丰富的接口,引出了他和他父类的能力。 1. Service creation and activation methods. The ACE Acceptor-Connector framework can modify various service handler creation and initialization aspects at compile time and at run time. By default, an ACE_Svc_Handler subclass is allocated dynamically by an acceptor or connector factory, which use the following methods to create and activate it: 服务创建和激活方法。ACE Acceptor-Connector框架可以在编译时和运行时修改不同的服务处理器创建和初始化。默认,一个ACE_Svc_Handler的子类被acceptor或者connector工厂动态分配,使用下面的方法来创建和激活他:
Sidebar 47 explains why the ACE Acceptor-Connector framework decouples service handler creation from activation. Sidebar 47解释了为什么ACE_Accepotr-Connector框架从服务激活decouple服务创建。 Pointers to ACE_Thread_Manager, ACE_Message_Queue, and ACE_Reactor objects can be passed to the ACE_Svc_Handler constructor to override its defaults. The open() hook method can perform activities that initialize a service handler, such as: ACE_Thread_Manager,ACE_Message_Queue,和ACE_Reactor对象可以被传递到ACE_Svc_Handler构造器来重载他的默认值。
If these initialization activities complete successfully, open() returns 0. If a failure occurs and the service cannot or should not continue, however, open() must report this event to its caller by returning -1. Since the service handler doesn't control how it was instantiated, a failure in open() must be reported back to the caller so that cleanup activities can be performed. By default, the service handler is deleted automatically if open() returns -1, as shown in the various activate_svc_handler() methods of the acceptor and connector factories (page 221). 如果初始化动作全部成功,open()会返回0。如果失败发生服务不能继续,open()必须返回-1报告这个事件给他的调用者。因为服务处理器不能控制他自己如何被初始化,open()方法中的失败必须报告回调用者, The ACE_Svc_Handler defines a default implementation of open() that performs the common set of operations shown below: ACE_Svc_Handler定义了一个默认的open()实现执行如下操作: template <class PEER_STREAM, class SYNCH_STRATEGY> int ACE_Svc_Handler<PEER_STREAM, SYNCH_STRATEGY>::open (void *factory) { if (reactor () && reactor ()->register_handler (this, ACE_Event_Handler::READ_MASK) == -1) return -1; else return 0; } The void * parameter to open() is a pointer to the acceptor or connector factory that created the service handler. By default, a service handler registers itself with a reactor and processes incoming events reactively. The Example part of this section (page 214) illustrates a service handler that activates itself in its open() method to become an active object and process incoming events concurrently. Since a service handler is responsible for its life cycle management after being activated successfully, it rarely interacts with the acceptor that created and activated it. As shown in the Example part of Section 7.4, however, a service handler often uses a connector to reestablish connections if failures occur. open()中void*参数指向创建服务处理器的accepotr或者connector工厂。默认,服务处理器注册他自己到reacotr和处理到达的事件。这章的例子214页描述了服务处理器激活在open()方法中激活他自己,成为主动对象,处理到达的并发的事件。因为服务处理器负责他自己的生命周期管理在激活成功后,他很少影响创建他和激活他的acceptor。7.4章中的例子,一个服务处理器经常使用connector来重新连接,如果失败发生。 2. Service processing methods. As outlined above, a service handler can perform its processing in several ways. For example, it can process events reactively using a reactor or it can process them concurrently via one or more processes or threads. The following methods inherited from ACE_Svc_Handler's ancestors can be overridden by its subclasses and used to perfo=rm service handler processing: 服务处理方法。一个服务处理器可以有几种方法来执行他的处理。例如,他可以通过reactor处理事件或者他可以通过一个或多个线程并发处理。下面的方法继承自ACE_Svc_Handler的祖先可以被他的子类重载和用于执行服务处理器处理:
Although the ACE_Svc_Handler SYNCH_STRATEGY template argument parameterizes the ACE_Message_Queue inherited from ACE_Task, it has no effect on the PEER_STREAM IPC endpoint. It's inappropriate for the ACE Acceptor-Connector framework to unilaterally serialize use of the IPC endpoint since it's often not accessed concurrently. For example, a service handler may run as an active object with a single thread or be driven entirely by callbacks from a reactor in a single-threaded configuration. 尽管ACE_Svc_Handler SYNCH_STRATEGY模板参数化ACE_Message_Queue继承自ACE_Task,他没有影响PEER_STREAM_IPC端点。ACE Acceptor-Connector框架不太合适单方面序列化IPC端点因为他并不被并发的访问.例如一个服务处理器可以作为主动对象运行在单线程中或者由reactor中的回调来驱动在单线程的配置中。 It is possible, however, for a service handler's open() hook method to spawn multiple threads that access its IPC endpoint concurrently. In such cases, the application code in the service handler must perform any necessary synchronization. Chapter 10 of C++NPv1 describes ACE synchronization mechanisms that applications can use. For example, if more than one thread writes to the same socket handle, it's a good idea to serialize it with an ACE_Thread_Mutex to avoid interleaving data from different send() calls into the same TCP bytestream. 一个服务处理器的open() hook方法来产生多个并发访问他的IPC端点线程。在这些情况,应用程序的代码在服务处理器中必须执行任何必须的同步。C++NPv1 的 Chapter 10 描述了应用程序可以使用的ACE同步的机制。例如,如果超过一个线程写到同一个socket句柄,用ACE_Thread_Mutex序列化避免不同的send()造成交错的数据写到相同的TCP字节流,是一个好主意。 3. Service shutdown methods. A service handler can be used in many ways. For example, it can be dispatched by a reactor, run in its own thread or process, or form part of a thread pool. The ACE_Svc_Handler class therefore provides the following methods to shut a service handler down: 服务关闭方法。一个服务处理器可以用在许多地方,可以被reactor分发运行在他的自己的线程或者进程,或者线程池中。ACE_Svc_Handler类因此提供一下方法来关闭服务处理器:
Service handlers are often closed in accordance with an application-defined protocol, such as when a peer service handler closes a connection or when a serious communication error occurs. Regardless of the particular circumstance, however, a service handler's shutdown processing usually undoes the actions performed by the service handler's open() hook method, and deletes the service handler when needed. The shutdown methods listed in the table above can be divided into the following three categories: 服务处理器经常关闭与应用程序定义的协议一致,例如当一个peer服务处理器关闭一个连接或者当严重的通讯错误发生。不管这个特定的环境,然而,一个服务处理器关闭处理通常取消在open()中执行的动作,必要时删除服务处理器。在表中的关闭方法被分割成下面3个类别:
Chapter 3 of C++NPv1 explained why the destruction of an ACE_SOCK-derived object doesn't close the encapsulated socket. ACE_Svc_Handler is at a higher level of abstraction, however, and, because it's part of a framework, it codifies common usage patterns. Since closing the socket is such a common part of shutting down a service handler, the ACE Acceptor-Connector framework performs this task automatically. C++NPv1 Chapter 3解释为什么ACE-derived对象不关闭封装的socket。ACE_Svc_Handler是更高的抽象层,而且,因为他是框架的一部分,他代码化常用的使用模式。因为关闭服务处理器中关闭socket是常见的部分,所以ACE Acceptor-Connector框架回自动执行这个任务。 ACE_Svc_Handler uses the Storage Class Tracker C++ idiom described in Sidebar 48 (page 212) to check if it was allocated dynamically or statically. Its destroy() method can therefore tell if a service handler was allocated dynamically and, if so, delete it. If the service handler was not allocated dynamically, destroy() doesn't delete it. ACE_Svc_Handerl使用Storage Class Tracker C++ 惯用手法来检查是否被动态或者静态分配。destory()方法因此告诉服务处理器删除他。如果服务处理器不是动态分配的,destory()不删除他。 If a service handler is registered with a reactor, it's best to not call destroy() from a thread that's not running the reactor event loop. Doing so could delete the service handler object out from under a reactor that's dispatching events to it, causing undefined (and undesired) behavior (this is similar to the issue discussed in Sidebar 46 on page 196). Rather than calling destroy() directly, therefore, use the ACE_Reactor::notify() method (page 77) to transfer control to a thread dispatching reactor events, where destroy() is safer to use. An even better approach, however, is to alter the design to use the reactive shutdown technique described next. 如果一个服务处理器注册到reactor,最好不要从没有运行reactor事件循环的线程中调用destroy()。这样做可能删除服务处理器脱离分派事件给它的反应器的控制,导致不确定的行为。Rather than 直接调用destory(),因而,使用ACE_Reactor::notify 方法转移控制到一个分发reactor事件的线程,那里destroy()可以安全的使用。an even better 方法,然而,下面描述的改变使用反应式关闭技术的设计。
ExampleThis example illustrates how to use the ACE_Svc_Handler class to implement a logging server based on the thread-per-connection concurrency model described in Chapter 5 of C++NPv1. The example code is in the TPC_Logging_Server.cpp and TPC_ Logging_Server.h files. The header file declares the example classes, and starts by including the necessary header files. #include "ace/Acceptor.h" #include "ace/INET_Addr.h" #include "ace/Reactor.h" #include "ace/Svc_Handler.h" #include "ace/FILE_IO.h" #include "Logging_Handler.h" The TPC_Logging_Handler shown below inherits from ACE_Svc_Handler. class TPC_Logging_Handler : public ACE_Svc_Handler<ACE_SOCK_Stream, ACE_NULL_SYNCH> { We parameterize the ACE_Svc_Handler template with an ACE_SOCK_Stream data transfer class and the ACE_NULL_SYNCH traits class, which designates a no-op synchronization strategy. Sidebar 49 (page 214) explains how ACE handles C++ compilers that don't support traits classes in templates. TPC_Logging_Handler defines the following two data members that are initialized in its constructor. protected: ACE_FILE_IO log_file_; // File of log records. // Connection to peer service handler. Logging_Handler logging_handler_; public: TPC_Logging_Handler (): logging_handler_ (log_file_) {} As usual, we reuse the Logging_Handler from Chapter 4 of C++NPv1 to read a log record out of the socket handle parameter and store it into an ACE_Message_Block.
Each instance of TPC_Logging_Handler is allocated dynamically by the TPC_ Logging_Acceptor (page 222) when a connection request arrives from a peer connector. TPC_Logging_Handler overrides the ACE_Svc_Handler::open() hook method to initialize the handler, as shown below: 1 virtual int open (void *) { 2 static const ACE_TCHAR LOGFILE_SUFFIX[] = ACE_TEXT (".log"); 3 ACE_TCHAR filename[MAXHOSTNAMELEN + sizeof (LOGFILE_SUFFIX)]; 4 ACE_INET_Addr logging_peer_addr; 5 6 peer ().get_remote_addr (logging_peer_addr); 7 logging_peer_addr.get_host_name (filename, MAXHOSTNAMELEN); 8 ACE_OS_String::strcat (filename, LOGFILE_SUFFIX); 9 10 ACE_FILE_Connector connector; 11 connector.connect (log_file_, 12 ACE_FILE_Addr (filename), 13 0, // No timeout. 14 ACE_Addr::sap_any, // Ignored. 15 0, // Don't try to reuse the addr. 16 O_RDWR|O_CREAT|O_APPEND, 17 ACE_DEFAULT_FILE_PERMS); 18 19 logging_handler_.peer ().set_handle (peer ().get_handle ()); 20 21 return activate (THR_NEW_LWP | THR_DETACHED); 22 } Lines 2?7 Initialize a log file using the same logic described in the Logging_Event_ Handler::open() method (page 59). Line 19 Borrow the socket handle from the service handler and assign it to logging_ handler_, which is then used to receive and process client log records. Line 21 Convert TPC_Logging_Handler into an active object. The newly spawned detached thread runs the following TPC_Logging_Handler::svc() hook method: virtual int svc () { for (;;) switch (logging_handler_.log_record ()) { case -1: return -1; // Error. case 0: return 0; // Client closed connection. default: continue; // Default case. } /* NOTREACHED */ return 0; } }; This method focuses solely on reading and processing client log records. We break out of the for loop and return from the method when the log_record() method detects that its peer service handler has closed the connection or when an error occurs. Returning from the method causes the thread to exit, which in turn triggers ACE_Task::svc_run() to call the inherited ACE_Svc_Handler::close() method on the object. By default, this method closes the peer stream and deletes the service handler if it was allocated dynamically, as described in the table on page 210. Since the thread was spawned using the THR_DETACHED flag, there's no need to wait for it to exit. You may notice that TPC_Logging_Handler::svc() provides no way to stop the thread's processing if the server is somehow asked to shut down before the peer closes the socket. Adding this capability is left as an exercise for the reader. Some common techniques for providing this feature are described in Sidebar 50.
| ||||||||||||||||||||||||||||||||||
Ru-Brd |