diff options
Diffstat (limited to 'docs/tutorials/006/page05.html')
-rw-r--r-- | docs/tutorials/006/page05.html | 366 |
1 files changed, 0 insertions, 366 deletions
diff --git a/docs/tutorials/006/page05.html b/docs/tutorials/006/page05.html deleted file mode 100644 index ce9d99ce0b6..00000000000 --- a/docs/tutorials/006/page05.html +++ /dev/null @@ -1,366 +0,0 @@ -<HTML> -<HEAD> - <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> - <META NAME="GENERATOR" CONTENT="Mozilla/4.04 [en] (X11; I; Linux 2.0.32 i486) [Netscape]"> - <META NAME="Author" CONTENT="James CE Johnson"> - <META NAME="Description" CONTENT="A first step towards using ACE productively"> - <TITLE>ACE Tutorial 006</TITLE> -</HEAD> -<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> - -<CENTER><B><FONT SIZE=+2>ACE Tutorial 006</FONT></B></CENTER> - -<CENTER><B><FONT SIZE=+2>Creating a thread-per-connection server</FONT></B></CENTER> - - -<P> -<HR WIDTH="100%"> -<P><A HREF="client_handler.cpp">client_handler.cpp</A> exposes all the -things I've been hinting at. Pay special attention to the decision -made in open() as well as the bit of cleverness in svc(). - -<P> -<HR WIDTH="100%"> -<PRE> - -<font color=red>// $Id$</font> - -<font color=red>/* - 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. - - 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. - - On the other hand, we don't directly include any ACE header files here. - */</font> -<font color=blue>#include</font> "<font color=green>client_acceptor.h</font>" -<font color=blue>#include</font> "<font color=green>client_handler.h</font>" - -<font color=red>/* - 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... - */</font> -<font color=#008888>Client_Handler::Client_Handler</font> (void) -{ -} - -<font color=red>/* - 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. - */</font> -<font color=#008888>Client_Handler::~Client_Handler</font> (void) -{ -} - -<font color=red>/* - 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. - */</font> -void <font color=#008888>Client_Handler::destroy</font> (void) -{ - <font color=red>/* - 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! - */</font> - this->reactor ()->remove_handler (this, - ACE_Event_Handler:: READ_MASK | <font color=#008888>ACE_Event_Handler::DONT_CALL</font>); - - <font color=red>/* - 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. - */</font> - delete this; -} - -<font color=red>/* - 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... - */</font> -int <font color=#008888>Client_Handler::open</font> (void *_acceptor) -{ - <font color=red>/* - 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. - */</font> - ACE_INET_Addr addr; - - <font color=red>/* - 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. - */</font> - if (this->peer ().get_remote_addr (addr) == -1) - { - return -1; - } - - <font color=red>/* - Convert the void* to a Client_Acceptor*. You should probably use those - fancy new C++ cast operators 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. - */</font> - Client_Acceptor *acceptor = (Client_Acceptor *) _acceptor; - - <font color=red>/* - 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. - - If we are told to use the single-threaded strategy, there is no difference - between this and the Tutorial 5 implementation. - - 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. - */</font> - if( acceptor->thread_per_connection() ) - { - return this->activate(); - } - - <font color=red>// ************************************************************************</font> - <font color=red>// From here on, we're doing the traditional reactor thing. If</font> - <font color=red>// you're operating in thread-per-connection mode, this code does</font> - <font color=red>// not apply.</font> - <font color=red>// ************************************************************************</font> - - <font color=red>/* - Our reactor reference will be set when we register ourselves but I decided - to go ahead and set it here. No good reason really... - */</font> - this->reactor (acceptor->reactor ()); - - <font color=red>/* - 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. - */</font> - if (this->reactor ()->register_handler (this, <font color=#008888>ACE_Event_Handler::READ_MASK</font>) == -1) - { - ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>(%P|%t) can't register with reactor\n</font>"), -1); - } - - <font color=red>/* - 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. - */</font> - ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) connected with %s\n</font>", addr.get_host_name ())); - - <font color=red>/* - Always return zero on success. - */</font> - return 0; -} - -<font color=red>/* - 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. - */</font> -int <font color=#008888>Client_Handler::close</font>(u_long flags) -{ - ACE_UNUSED_ARG(flags); - - <font color=red>/* - 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. - */</font> - this->destroy(); - - <font color=red>/* - 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! - */</font> - return 0; -} - -<font color=red>/* - 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. - - Again, this is not used if we're in thread-per-connection mode. - */</font> -int <font color=#008888>Client_Handler::handle_input</font> (ACE_HANDLE _handle) -{ - <font color=red>/* - Some compilers don't like it when you fail to use a parameter. This macro - will keep 'em quiet for you. - */</font> - ACE_UNUSED_ARG (_handle); - - <font color=red>/* - 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. - */</font> - char buf[128]; - <font color=#008888>ACE_OS::memset</font> (buf, 0, sizeof (buf)); - - <font color=red>/* - 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. - */</font> - return this->process (buf, sizeof (buf)); -} - -<font color=red>/* - 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. - */</font> -int <font color=#008888>Client_Handler::handle_close</font> (ACE_HANDLE _handle, ACE_Reactor_Mask _mask) -{ - ACE_UNUSED_ARG (_handle); - ACE_UNUSED_ARG (_mask); - - this->destroy (); - return 0; -} - -<font color=red>/* - 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. - - Of course, this is only valid if we're in thread-per-connection - mode. If we're using the reactor model, then svc() never comes - into play. - */</font> -int <font color=#008888>Client_Handler::svc</font>(void) -{ - <font color=red>/* - Like handle_input(), we create a buffer for loading the data. Doing so - in handle_input() doesn't help any but there is a small performance increase - by doing this here: the buffer is created once when the thread is created - instead of for each invocation of process(). - */</font> - char buf[128]; - - <font color=red>// Forever...</font> - while( 1 ) - { - <font color=red>// Clean the buffer...</font> - <font color=#008888>ACE_OS::memset</font> (buf, 0, sizeof (buf)); - - <font color=red>/* - Invoke the proces() method to read and process the data. This is - exactly the way it is used by handle_input(). That's the reason I - created process() in the first place: so that it can be used in either - concurrency strategy. Since process() has all of our application-level - logic, it's nice that it doesn't have to change when we decide to go - multi-threaded. - - Notice that since the recv() method call in process() blocks until - there is data ready, this thread doesn't consume any CPU time until - there is actually data sent from the client. - */</font> - if( this->process(buf,sizeof(buf)) == -1 ) - { - return(-1); - } - } - - return(0); -} - -<font color=red>/* - And, at last, we get to the application-logic level. Out of everything - we've done so far, this is the only thing that really has anything to do - with what your application will do. In this method we will read and process - the client's data. In a real appliation, you will probably have a bit more - in main() to deal with command line options but after that point, all of the - action takes place here. - */</font> -int <font color=#008888>Client_Handler::process</font> (char *_rdbuf, int _rdbuf_len) -{ - <font color=red>/* - Using the buffer provided for us, we read the data from the client. If - there is a read error (eg -- recv() returns -1) then it's a pretty good - bet that the connection is gone. Likewise, if we read zero bytes then - something wrong has happened. The reactor wouldn't have called us if - there wasn't some kind of read activity but there wouldn't be activity if - there were no bytes to read... - - On the other hand, if we got some data then we can display it in a debug - message for everyone to see. - */</font> - switch (this->peer ().recv (_rdbuf, _rdbuf_len)) - { - case -1: - ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>(%P|%t) %p bad read\n</font>", "<font color=green>client</font>"), -1); - case 0: - ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>(%P|%t) closing daemon (fd = %d)\n</font>", this->get_handle ()), -1); - default: - ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) from client: %s</font>", _rdbuf)); - } - - return 0; -} -</PRE> -<HR WIDTH="100%"> - -<P>Well, that's it! After all the talk & the hype, you would -have expected it to be more difficult to create a multi-threaded server. -Surprise! It really is that easy. You still have to handle -contention issues which we haven't addressed here and that is a rather -nasty topic. Still, for the simple case, this is all you have to -do. - -<P>The next page is the last for this tutorial. Head on over there -& we'll round up the file list one last time. - -<P> -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page06.html">Continue This Tutorial</A>]</CENTER> |