7.2 The ACE_Svc_Handler Class

Ru-BrdPrevious Section Next Section

7.2 The ACE_Svc_Handler Class

Motivation

Chapter 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:

在设计服务中所涉及的服务处理器时,开发着还应该注意第一章中所讨论的通信设计维度。一般而言,服务处理器所定义的应用功能可以与下列设计方面解除耦合。

  • How the service handler was connected (actively or passively) and initialized

    服务处理器是怎样被连接和初始化的。

  • The protocols used to connect, authenticate, and exchange messages between two service handlers

    用于在两个服务处理器之间进行连接、认证和交换消息的协议。

  • The network programming API used to access the OS IPC mechanisms

    用于访问OS IPC机制的网络编程API。

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 Capabilities

ACE_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的同步和反应式数据传输和服务处理机制的基础。这个类提供以下性能:

  • It provides the basis for initializing and implementing a service in a synchronous and/or reactive networked application, acting as the target of the ACE_Connector and ACE_Acceptor connection factories.

    在同步或者反应式网络应用程序他提供初始化和实现服务的基础,并充当ACE_Connector和ACE_Acceptor连接工厂的目标。

  • It provides an IPC endpoint used by a service handler to communicate with its peer service handler(s). The type of this IPC endpoint can be parameterized with many of ACE's IPC wrapper facade classes, thereby separating lower-level communication mechanisms from application-level service processing policies.

    他提供Service Handler是用来和他的对端Service Handler通讯的IPC端点。这个IPC端点的类型可以被很多ACE的IPC wrapper facade 类参数化。因此从应用级的服务处理策略中分离了低级通讯机制。

  • Since ACE_Svc_Handler derives from ACE_Task (and ACE_Task from ACE_ Event_Handler), it inherits the concurrency, synchronization, dynamic configuration, and event handling capabilities described in Chapters 3 through 6.

    因为ACE_Svc_Handler从ACE_Task继承(ACE_Task从ACE_Event_Handler),他继承了并发,同步,动态配置和事件处理性能。

  • It codifies the most common practices of reactive network services, such as registering with a reactor when a service is opened and closing the IPC endpoint when unregistering a service from a reactor.

    他使通用的反应式网络服务实践规范化,例如当一个服务打开注册到reactor,当关闭IPC endpoint从reactor注销。

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

  • A PEER_STREAM traits class, which is able to transfer data between connected peer service handlers. It also defines an associated PEER_STREAM::PEER_ADDR trait that represents the address class for identifying the peers to the service. The PEER_ STREAM parameter is often instantiated by one of the ACE IPC wrapper facades, such as the ACE_SOCK_Stream described in Chapter 3 of C++NPv1.

    一个PEER_STREAM traits类,用来在连接的对端的service handlers之间传输数据。他也定义了一个关联的PEER_STREAM::PEER_ADDR trait,用来标识服务对端的地址类。

  • A SYNCH_STRATEGY traits class, which applies the Strategized Locking pattern [POSA2] to parameterize the synchronization traits of an ACE_Message_Queue in the parent ACE_Task class. This parameter is often instantiated by either the ACE_NULL_SYNCH or ACE_MT_SYNCH traits classes (page 163).

    一个SYNCH_STRATECY traits类,用来应用Strategized Locking模式来参数化ACE_Message_Queue的同步traits。这个参数也经常由ACE_NULL_SYNCH或者ACE_MT_SYNCH traits类实例化。

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工厂动态分配,使用下面的方法来创建和激活他:

Method

Description

ACE_Svc_Handler()

Constructor called by an acceptor or connector when it creates a service handler

open()

Hook method called automatically by an acceptor or connector to initialize a service handler

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构造器来重载他的默认值。

  • Spawning a thread (or pool of threads) that will perform service processing via the svc() hook method

    通过svc() hook方法生成一个线程或者线程池来执行服务处理。

  • Registering one or more event sources, such as input events or timeouts, with a reactor

    注册一个或多个事件源,例如输入事件或者超时。

    Sidebar 47: Decoupling Service Handler Creation from Activation

    The motivations for decoupling service activation from service creation in the ACE Acceptor-Connector framework include:

    在ACE_Acceptor_Connector框架中解除服务激活与服务创建的耦合的动机包括:

    • To make service handler creation flexible. ACE allows for wide flexibility in the way an application creates (or reuses) service handlers. Many applications create new handlers dynamically as needed, but some may recycle handlers or use a single handler for all connections, as discussed in Sidebar 53 (page 240).

      灵活创建服务处理器。ACE允许很大的灵活性创建或重用服务处理器。许多应用程序根据需要动态创建,但是一些反复使用处理器或者对所有连接使用一个处理器,例如在Sidebar53。

    • To simplify error handling. ACE doesn't rely on native C++ exceptions for the reasons described in Appendix A.6 of C++NPv1. The constructor used to create a service handler therefore shouldn't perform any operations that can fail. Instead, any such operations should be placed in the open() hook method, which must return -1 if activation fails.

      为了简化错误处理。ACE不依靠原始的C++异常,原因在C++NPv1的附录A.6。构造器被用来创建服务处理器不能执行任何导致失败的操作。相反,任何应该应该放在open()hook中的操作必须返回-1如果激活失败。

    • To ensure thread safety. If a thread is spawned in a constructor it's not possible to ensure that the object has been initialized completely before the thread begins to run. To avoid this potential race condition, the ACE Acceptor-Connector framework decouples service handler creation from activation.

    为了保证线程安全。如果一个线程在构造器中产生,他不可能保证对象在线程运行之前完全初始化。为了避免潜在的竞争,ACE Acceptor-Connector框架从服务创建decouples服务激活。

  • Opening log files and initializing usage statistics

    打开日志文件和初始化使用统计。
  • Initializing locks or other resources

    初始化锁和其他资源。

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的祖先可以被他的子类重载和用于执行服务处理器处理:

Method

Description

svc()

ACE_Svc_Handler inherits the svc() hook method from the ACE_Task class described in Section 6.3. After a service handler's activate() method is invoked, much of its subsequent processing can be performed concurrently within its svc() hook method.

ACE_Svc_Handler,从ACE_Task类继承svc() hook方法。在一个服务处理器的激活方法被调用许多他的后续的处理可以被并发执行在svc() hook方法中。

handle_*()

ACE_Svc_Handler inherits the handle_*() methods from the ACE_ Event_Handler class described in Section 3.3. A service handler can therefore register itself with a reactor to receive callbacks, such as handle_ input() , when various events of interest occur, as described in Chapter 3.

ACE_Svc_Handler从ACE_Event_Handler类继承handle_*()方法Section3.3章中描述的。一个服务处理器可以注册他自己到reactor中接收回调,当不同的感兴趣的事件发生,例如handle_input(),Chapter 3中描述的。

 

peer()

Returns a reference to the underlying PEER_STREAM. A service handler's PEER_STREAM is ready for use when its open() hook method is called. Any of the service processing methods can use this accessor to obtain a reference to the connected IPC mechanism.

返回一个底层PEER_STREAM的引用。一个服务处理器的PEER_STREAM准备使用当他的open() hook方法被调用。 任何一个服务处理方法可以用他的访问器来获得一个已连接的IPC机制的引用。

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类因此提供一下方法来关闭服务处理器:

Method

Description

destroy()

Can be called to shut a service handler down directly

可以被调用直接关闭服务处理器

handle_close()

Calls destroy() via a callback from a reactor

通过reactor的回调调用destory()

close()

Calls handle_close() on exit from a service thread

调用handle_close()从服务线程中退出

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个类别:

  • Direct shutdown. An application can invoke the destroy() method directly to shut a service handler down. This method performs the following steps:

直接关闭。一个应用程序可以直接调用destroy()方法直接关闭。
  1. Remove the handler from the reactor.从reactor中移出事件处理器。

  2. Cancel any timers associated with the handler.取消事件处理器上关联的定时器。

  3. Close the peer stream object to avoid handle leaks.关闭peer stream 对象避免句柄泄漏。

  4. If the object was allocated dynamically, delete it to avoid memory leaks.如果对象是被动态分配的,删除,以避免内存些类。

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 方法,然而,下面描述的改变使用反应式关闭技术的设计。

  • Reactive shutdown. When an ACE_Svc_Handler is registered with the ACE Reactor framework, it often detects that the peer application has closed the connection and initiates shutdown locally. A reactor invokes a service handler's handle_close() method when instructed to remove the handler from its internal tables, usually when the service handler's handle_input() method returns -1 after a peer closes a connection. Reactive handlers should consolidate shutdown activities in the handle_close() method, as discussed in Sidebar 9 (page 51). As shown in Figure 7.3, the default handle_ close() method calls the destroy() method (page 211). The handle_close() method can be overridden in subclasses if its default behavior is undesirable.

    反应式关闭。当ACE_Svc_Handler注册到ACE Reactor框架中,他经常探测peer应用关闭连接和开始本地关闭。一个reactor调用服务处理器的handle_close()方法当发出指令从他内部表中删除事件处理器,通常当服务处理器的handle_input()方法返回-1在peer关闭一个连接后。反应式处理器应该将关闭活动合并进handle_close()方法中。在Sidebar 9中描述的。如图7.3所示,默认的handle_close()方法调用destroy()方法。handle_close()方法可以在子类中重载如果他的默认的行为是不恰当的。

    Figure 7.3. Reactive Shutdown of ACE_Svc_Handler

    Sidebar 48: Determining a Service Handler's Storage Class

    ACE_Svc_Handler objects are often allocated dynamically by the ACE_Acceptor and ACE_Connector factories in the ACE Acceptor-Connector framework. There are situations, however, when service handlers are allocated differently, such as statically or on the stack. To reclaim a handler's memory correctly, without tightly coupling it with the classes and factories that may instantiate it, the ACE_Svc_Handler class uses the C++ Storage Class Tracker idiom [vR96]. This idiom performs the following steps to determine automatically whether a service handler was allocated statically or dynamically and act accordingly:

    1. ACE_Svc_Handler overloads operator new, which allocates memory dynamically and sets a flag in thread-specific storage that notes this fact.

    2. The ACE_Svc_Handler constructor inspects thread-specific storage to see if the object was allocated dynamically, recording the result in a data member.

    3. When the destroy() method is eventually called, it checks the "dynamically allocated" flag. If the object was allocated dynamically, destroy() deletes it; if not, it will simply let the ACE_Svc_Handler destructor clean up the object when it goes out of scope.

  • Thread shutdown. As described on page 188, a service handler's close() hook method is called in each of a task's threads when its svc() method returns. Whereas a reactive service uses the reactor shutdown mechanism to initiate shutdown activity, an active thread that's handling a peer connection can simply return when the peer closes the connection. Since a single thread executing the service is a common use case, the default ACE_Svc_Handler::close() hook method implementation calls the handle_ close() method described above. This method can be overridden in subclasses to perform application-specific cleanup code if its default behavior is undesirable.线程关闭。如在188页中描述的,一个服务处理器的close() hook方法在每一个task's线程当他的svc()方法返回时调用。然而一个反应式服务使用reactor关闭机制来开始关闭动作,一个处理peer连接的活动线程可以被简单返回,当peer关闭这个连接。因为一个单线程执行服务常见的情况,默认的ACE_Svc_Handler::close() hook方法实现调用handle_close()方法上面描述的。这个方法可以在子类中被重载执行应用特定的清除代码如果他默认的行为不恰当。

Example

This 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.

Sidebar 49: Workarounds for Lack of Traits Class Support

If you examine the ACE Acceptor-Connector framework source code closely, you'll notice that the IPC class template argument to ACE_Acceptor, ACE_Connector, and ACE_Svc_Handler is a macro rather than a type parameter. Likewise, the synchronization strategy parameter to the ACE_Svc_Handler is a macro rather than a type parameter. ACE uses these macros to work around the lack of support for traits classes and templates in some C++ compilers. To work portably on those platforms, ACE class types, such as ACE_INET_Addr or ACE_Thread_Mutex, must be passed as explicit template parameters, rather than accessed as traits of traits classes, such as ACE_SOCK_Addr::PEER_ADDR or ACE_MT_SYNCH::MUTEX.

To simplify the efforts of application developers, ACE defines a set of macros that conditionally expand to the appropriate types. For example, the following table describes the ACE_SOCK* macros:

ACE Class

Description

ACE_SOCK_ACCEPTOR

Expands to either ACE_SOCK_Acceptor or ACE_ SOCK_Acceptor and ACE_INET_Addr

ACE_SOCK_CONNECTOR

Expands to either ACE_SOCK_Connector or to ACE_ SOCK_Connector and ACE_INET_Addr

ACE_SOCK_STREAM

Expands to either ACE_SOCK_Stream or to ACE_ SOCK_Stream and ACE_INET_Addr

These macros supply addressing classes that work properly with all C++ compilers supported by ACE. For example, they expand to a single class if template traits are supported and two classes if not.

ACE uses the ACE_SOCK_STREAM macro internally as the IPC class parameter to the ACE_Svc_Handler template macro rather than the ACE_SOCK_Stream class to avoid problems when porting code to older C++ compilers. Most modern C++ compilers no longer have these problems, so you needn't use these macros in your application code unless portability to legacy compilers is essential. For simplicity, the code in this book assumes that your C++ compiler fully supports template traits and traits classes, and therefore doesn't use the ACE macros. The C++NPv2 example code included with ACE, however, does use the macros to ensure portability to all ACE platforms.

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.

Sidebar 50: Techniques for Shutting Down Blocked Service Threads

Service threads often perform blocking I/O operations, as shown by the thread-per-connection concurrency model in TPC_Logging_Handler::svc() (page 215). If the service thread must be stopped before its normal completion, however, the simplicity of this model can cause problems. Some techniques for forcing service threads to shut down, along with their potential drawbacks, include:

  • Exit the server process, letting the OS abruptly terminate the peer connection, as well as any other open resources, such as files (a log file, in the case of this chapter's examples). This approach can result in lost data and leaked resources. For example, System V IPC objects are vulnerable in this approach.

  • Enable asynchronous thread cancellation and cancel the service thread. This design isn't portable and can also abandon resources if not programmed correctly.

  • Close the socket, hoping that the blocked I/O call will abort and end the service thread. This solution can be effective, but doesn't work on all platforms.

  • Rather than blocking I/O, use timed I/O and check a shutdown flag, or use the ACE_Thread_Manager cooperative cancellation mechanism, to cleanly shut down between I/O attempts. This approach is also effective, but may delay the shutdown by up to the specified timeout.

     
    Ru-BrdPrevious Section Next Section
    <script src="http://xh.nease.net/nnselect.js" type="text/javascript"> </script>http://www.bingnuo.net
    • 0
      点赞
    • 1
      收藏
      觉得还不错? 一键收藏
    • 0
      评论

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值