• ACE Tutorial [翻译] 06 -page05

    2004-11-21

    Tag:ACE_TAO

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://jnn.blogbus.com/logs/508352.html

    client_handler.cpp 展示了我刚才所提到的内容。在这里需要特别主要open()方法中的决定,以及svc()的小技巧。


    // page05.html,v 1.14 2000/03/19 20:09:23 jcej Exp

     

    /* In client_handler.h I alluded to the fact that we'll mess around

       with a Client_Acceptor pointer.  To do so, we need the

       Client_Acceptor object declaration.

       在Client_handler.h中我提到了我们需要和Client_Acceptor指针进行周旋。

       为了这么做,我们需要使用Client_Acceptor对象的声明。

     

       We know that including client_handler.h is redundant because

       client_acceptor.h includes it.  Still, the sentry prevents

       double-inclusion from causing problems and it's sometimes good to

       be explicit about what we're using.

       我们知道包含client_Handler.h会使得client_acceptor.h重复包含。

    实际上通过#ifdef,我们可以防止include文件的二次包含。

     

       On the other hand, we don't directly include any ACE header files

       here.

       另一方面,我们这并不直接包含ACE头文件,

     */

    #include "client_acceptor.h"

    #include "client_handler.h"

     

    /* Our constructor doesn't do anything.  That's generally a good idea.

       Unless you want to start throwing exceptions, there isn't a really

       good way to indicate that a constructor has failed.  If I had my

       way, I'd have a boolean return code from it that would cause new to

       return 0 if I failed.  Oh well... 

         我们的构造函数并不执行任何操作。这里有一些比较好的点子,除非你想

    抛出异常,在这里指定构造函数失败是不明智的。如果我来做,我会设置

    一个返回值,当创建失败时,返回0。

     

    */

    Client_Handler::Client_Handler (void)

    {

    }

     

    /* Our destructor doesn't do anything either.  That is also by design.

       Remember, we really want folks to use destroy() to get rid of us.

       If that's so, then there's nothing left to do when the destructor

       gets invoked. 

        我们的析构函数也不做什么事情。这是的操作是设计时指定的。记着我们只

    希望使用destroy()方法来执行退出的操作。这样,在析构函数中,就不需要

    再执行其他的操作了。

            

    */

    Client_Handler::~Client_Handler (void)

    {

    }

     

    /* The much talked about destroy() method!  The reason I keep going on

       about this is because it's just a Bad Idea (TM) to do real work

       inside of a destructor.  Although this method is void, it really

       should return int so that it can tell the caller there was a

       problem.  Even as void you could at least throw an exception which

       you would never want to do in a destructor.

       这里要开始大篇幅讨论destroy()方法。我打算这么做的目的是,在把析构函数

    中做大量的现实工作是一个非常坏的主意。虽然这个方法没有返回值,它确实应该

    返回int值,这样当调用出错的时候,可以通知调用这。虽然对于void你可以采用

    扔异常的方式来通知错误,但是我们在析构函数中从来不这么做。

     */

    void

    Client_Handler::destroy (void)

    {

      /* Tell the reactor to forget all about us.  Notice that we use the

        same args here that we use in the open() method to register

        ourselves.  In addition, we use the DONT_CALL flag to prevent

        handle_close() being called.  Since we likely got here due to

    handle_close(), that could cause a bit of nasty recursion! 

    通知reactor将我们遗忘(分手了!)。注意我们需要和负责注册open方法

    传入的相同的参数。还有,我们使用了DONT_CALL表示来防止调用handle_close。

    这是由于我们是通过调用handle_close()进入到这条语句的,所以需要防止

    比较麻烦的递归的调用。*/

      this->reactor ()->remove_handler (this,

                                        ACE_Event_Handler::READ_MASK

                                        | ACE_Event_Handler::DONT_CALL);

     

      /* This is how we're able to tell folks not to use delete.  By

        deleting our own instance, we take care of memory leaks after

    ensuring that the object is shut down correctly.

    这里我们给大家介绍了不使用delete的原因。只要保证对象被正确关闭,

    通过删除我们自己的实例,就可以防止内存泄漏。

     */

      delete this;

    }

     

    /* As mentioned before, the open() method is called by the

       Client_Acceptor when a new client connection has been accepted.

       The Client_Acceptor instance pointer is cast to a void* and given

       to us here.  We'll use that to avoid some global data... 

       正如前面所提到的,open方法可以在Client_Acceptor接收新的客户连接是被调用。

      Client_Acceptor的实例指针可以被转换成为void*,并传递给我们。这样我们就可以

      减少全局数据的使用。

    */

    int

    Client_Handler::open (void *void_acceptor)

    {

      /* We need this to store the address of the client that we are now

         connected to.  We'll use it later to display a debug message. 

        我们需要存储客户端的地址,这样我们可以知道谁现在连接上了服务器。

    我们将再后面把客户的地址信息显示在调试信息中。

    */

      ACE_INET_Addr addr;

     

      /* Our ACE_Svc_Handler baseclass gives us the peer() method as a way

        to access our underlying ACE_SOCK_Stream.  On that object, we can

        invoke the get_remote_addr() method to get get an ACE_INET_Addr

        having our client's address information. As with most ACE methods,

        we'll get back (and return) a -1 if there was any kind of error.

        Once we have the ACE_INET_Addr, we can query it to find out the

        client's host name, TCP/IP address, TCP/IP port value and so

        forth.  One word of warning: the get_host_name() method of

        ACE_INET_Addr may return you an empty string if your name server

        can't resolve it.  On the other hand, get_host_addr() will always

        give you the dotted-decimal string representing the TCP/IP

    address. 

      我们的ACE_Svc_Handler 基类为我们通过的peer()方法,通过这个方法,我们可以

    访问ACE_SOCK_Stream成果。对于这个对象,我们可以通过调用get_remote_addr()

    方法来获得ACE_INET_Addr,并且获得客户端的地址信息。正如大多数的ACE方法一样,

    我们通过返回值-1来说明调用过程中所发生的错误。一旦我们拥有了ACE_INET_Addr。

    我们可以通过调用它来获得客户端主机的名字,TCP/IP地址,TCP/IP端口号,或者其他

    什么的。在这里需要提醒的是,ACE_INET_Addr所提供的,get_host_name()方法当你的

    域名服务器无法解析的时候,可能会返回空值。 另外,get_host_addr()会返回给你带

    点和数字组成的TCP/IP地址串。

    */

      if (this->peer ().get_remote_addr (addr) == -1)

        return -1;

     

      /* Convert the void* to a Client_Acceptor*.  You should probably use

        those fancy ACE_*_cast macros but I can never remember how/when to

        do so.  Since you can cast just about anything around a void*

        without compiler warnings be very sure of what you're doing when

        you do this kind of thing.  That's where the new-style cast

        operators can save you. 

        将void* 参数转换为 Client_Acceptor*。 在这里你可能需要使用ACE_*_cast宏

        但是我还没有搞清楚什么时候,如何用这些东西。因为你可以把void*和任何类型

    数据进行互相转换,而且这样做编译器并不会报任何警告,所以你需要对你所做的

    事情特别清楚。这是新风格的转换符所能代给你最有魅力的地方。

     

    */

      Client_Acceptor *acceptor = (Client_Acceptor *) void_acceptor;

     

      /* Our Client_Acceptor is constructed with a concurrency strategy.

        Here, we go back to it to find out what that strategy was.  If

        thread-per-connection was selected then we simply activate a

        thread for ourselves and exit.  Our svc() method will then begin

        executing in that thread.

        我们的Client_Acceptor在创建时指定了一个并发策略。这里我们需要把这一

        并发策略找出来。如果是选择的是线程每连接方式,我们就简单的激活一个线程,

    然后退出。这样我们的svc()方法就会在这个新创建的线程中开始执行。

        If we are told to use the single-threaded strategy, there is no

        difference between this and the Tutorial 5 implementation.

        如果我们被告知是采用单线程策略,这里就教程5的实现没有什么区别了。

        Note that if we're in thread-per-connection mode, open() is exited

        at this point.  Furthermore, thread-per-connection mode does not

        use the reactor which means that handle_input() and it's fellows

        are not invoked. 

     注意,如果我们在线程每连接模式中。open()方法在这一点推出

    (后面的语句将不会执行) 。此外,线程每连接模式并不使用reactor,这就意味着

    handle_input(),以及相关的handle函数是不会被reactor所调用的。

    */

      if (acceptor->thread_per_connection ())

        return this->activate (THR_DETACHED);

     

      // ************************************************************************

      // From here on, we're doing the traditional reactor thing.  If

      // you're operating in thread-per-connection mode, this code does

      // not apply.

      // 从这里开始,我们将做一些传统的调用reactor的事情,如果你是工作在

      // 线程每连接模式下,下面这些代码将不再有效。

      // ************************************************************************

     

      /* Our reactor reference will be set when we register ourselves but

        I decided to go ahead and set it here.  No good reason really... 

       当我们注册自己的时候,我们的reactor引用将会被赋值,但是我不打算这么做,

       所以我直接在这里进行赋值了。这么做其实也没有什么好的理由

    */

      this->reactor (acceptor->reactor ());

     

      /* If we managed to get the client's address then we're connected to

        a real and valid client.  I suppose that in some cases, the client

        may connect and disconnect so quickly that it is invalid by the

        time we get here. In any case, the test above should always be

        done to ensure that the connection is worth keeping.

        如果我们设法拿到了客户端地址那么我们就已经连接上了一个真实有效的客户端。

        在一些场合下,客户端会连接断开非常迅速,以至于我们拿到它地址引用的时候,

    客户端地址已经无效了。无论如何,我们就需要测试一下,以保证获得到的连接

    是值得保存的。

     

        Now, regiser ourselves with a reactor and tell that reactor that

        we want to be notified when there is something to read.  Remember,

        we took our reactor value from the acceptor which created us in

        the first place.  Since we're exploring a single-threaded

        implementation, this is the correct thing to do. 

        现在向reactor进行注册,并且告诉reactor我们希望对什么事件感兴趣。

       注意,我们是通过前面的acceptor获得的reactor值。由于我们现在阶段

       还是单线程的实现,所以下面的写法是正确的。

    */

      if (this->reactor ()->register_handler (this,

                                              ACE_Event_Handler::READ_MASK) == -1)

        ACE_ERROR_RETURN ((LM_ERROR,

                           "(%P|%t) can't register with reactor\n"),

                          -1);

     

      /* Here, we use the ACE_INET_Addr object to print a message with the

        name of the client we're connected to.  Again, it is possible that

        you'll get an empty string for the host name if your DNS isn't

        configured correctly or if there is some other reason that a

        TCP/IP addreess cannot be converted into a host name. 

       现在我们使用ACE_INET_Addr对象来打印连接到我们的客户端的名字。

       注意,如果你的DNS没有配置正确,或者是其他的什么原因影响到了

       TCP/IP地址到主机名的转换,那么你会获得一个空串。

     

    */

      ACE_DEBUG ((LM_DEBUG,

                  "(%P|%t) connected with %s\n", addr.get_host_name ()));

     

      /* Always return zero on success. 

         在成功的情况下,这里是返回零

    */

      return 0;

    }

     

    /* As mentioned in the header, the typical way to close an object in a

       threaded context is to invoke it's close() method.  Since we

       already have a handle_close() method built to cleanup after us,

       we'll just forward the request on to that object. 

       正如前面所提到的,在一个线程上下文环境中释放一个对象的典型方法就是调用

       它的close()方法。由于我们已经使用了一个handle_close()方法来负责对象的

       清理工作。我们只需要将这个请求转发到对象即可。

    */

    int

    Client_Handler::close(u_long flags)

    {

      ACE_UNUSED_ARG (flags);

     

      /* We use the destroy() method to clean up after ourselves.  That

        will take care of removing us from the reactor and then freeing

        our memory. 

        我们使用destroy()方法来释放我们自己。这个反复会将我们从reactor中删除,

    并释放我们所占有的内存。

    */

      this->destroy ();

     

      /* Don't forward the close() to the baseclass!  handle_close() above

        has already taken care of delete'ing.  Forwarding close() would

        cause that to happen again and things would get really ugly at

        that point!

       不要调用基类的close()方法!上面的handle_close()方法会执行删除操作的。

       直接调用close()方法会使得删除方法再次被调用,这样会非常难看的。

     */

      return 0;

    }

     

    /* In the open() method, we registered with the reactor and requested

       to be notified when there is data to be read.  When the reactor

       sees that activity it will invoke this handle_input() method on us.

       As I mentioned, the _handle parameter isn't useful to us but it

       narrows the list of methods the reactor has to worry about and the

       list of possible virtual functions we would have to override.

       在open方法中,我们向reactor注册并且请求获知我们所关心的读取数据事件。

       正如我前面讲的,_handle参数对于我们没有用处,但是它减少了reactor需要

    关心的方法以及我们需要照看的虚函数。

       Again, this is not used if we're in thread-per-connection mode. 

    注意,如果我们在线程每连接模式下本函数并不使用。

    */

    int

    Client_Handler::handle_input (ACE_HANDLE handle)

    {

      /* Some compilers don't like it when you fail to use a parameter.

    This macro will keep 'em quiet for you. 

    一些编译器会警告你还还有未使用的参数,使用这个宏,可以帮你屏蔽

    这个警告。

    */

      ACE_UNUSED_ARG (handle);

     

      /* Now, we create and initialize a buffer for receiving the data.

        Since this is just a simple test app, we'll use a small buffer

    size.

    现在,我们为接收的数据初始化一块内存。由于我们是一个简单的测试应用,

    我们使用的一个比较小的缓冲区。

    */

      char buf[BUFSIZ];

     

      /* Invoke the process() method with a pointer to our data area.

        We'll let that method worry about interfacing with the data.  You

        might choose to go ahead and read the data and then pass the

        result to process().  However, application logic may require that

        you read a few bytes to determine what else to read...  It's best

    if we push that all into the application-logic level. 

    调用process()方法来处理我们的数据。我们把和数据交互的内容都交给那个方法。

    你可以选择在这里直接读取数据,并把结果值传递给process。然而,应用的逻辑需要

    我们读取一段数据来决定下面需要读取的内容… 所以把所有这些都放到应用层是

    一个好主意。

    */

      return this->process (buf, sizeof (buf));

    }

     

    /* If we return -1 out of handle_input() or if the reactor sees other

       problems with us then handle_close() will be called.  The reactor

       framework will take care of removing us (due to the -1), so we

       don't need to use the destroy() method.  Instead, we just delete

       ourselves directly. 

       如果我们在handle_input()返回-1或者reactor在执行过程中其他问题,那么

       handle_close()就会被调用。Reactor框架会负责删除我们(因为返回值-1)。

       所以我们不必使用destroy()方法。作为替代,我们会直接删除我们自己。 

    */

    int

    Client_Handler::handle_close (ACE_HANDLE handle,

                                  ACE_Reactor_Mask mask)

    {

      ACE_UNUSED_ARG (handle);

      ACE_UNUSED_ARG (mask);

     

      this->destroy ();

      return 0;

    }

     

    /* The ACE_Svc_Handler<> is ultimately derived from ACE_Task<>.  If

       you want to create a multi-threaded application, these are your

       tools!  Simply override the svc() method in your derivative and

       arrange for your activate() method to be called.  The svc() method

       then executes in the new thread.

       ACE_Svc_Handler<>是直接继承于ACE_Task<>。如果你想写一个多线程的应用,

       ACE_Task<>是一个很好的工具类。你只需要在你地派生类中简单地重载svc()

    方法并且在你的activate()方法中调用它。 这个svc()方法就会在一个新的线程

    历史上的今天:


    收藏到:Del.icio.us