summaryrefslogtreecommitdiff
path: root/docs/tutorials/006/page05.html
diff options
context:
space:
mode:
Diffstat (limited to 'docs/tutorials/006/page05.html')
-rw-r--r--docs/tutorials/006/page05.html348
1 files changed, 0 insertions, 348 deletions
diff --git a/docs/tutorials/006/page05.html b/docs/tutorials/006/page05.html
deleted file mode 100644
index af803d9a54a..00000000000
--- a/docs/tutorials/006/page05.html
+++ /dev/null
@@ -1,348 +0,0 @@
-<!-- $Id$ -->
-<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.&nbsp; 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,
- <font color=#008888>ACE_Event_Handler::READ_MASK</font>
- | <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 *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 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. */</font>
- Client_Acceptor *acceptor = (Client_Acceptor *) void_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 (THR_DETACHED);
-
- <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[BUFSIZ];
-
- <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&lt;> is ultimately derived from ACE_Task&lt;>. 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[BUFSIZ];
-
- <font color=red>// Forever...</font>
- while( 1 )
- {
- <font color=red>/* Invoke the process() 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>
- Did you notice the <i>THR_DETACHED</i> flag on the call to
- <i>activate()</i>? Threads, like any system resource, are a
- limited resource. Unless we intend to <i>join()</i> or
- <i>wait()</i> for the new thread later, we want use THR_DETACHED
- so that we don't cause a leak. In fact, in most cases, you'll
- want to specify THR_DETACHED because it's just easier.
-<p>
- Another handy flag for use with <i>activate()</i> is
- <i>THR_NEW_LWP</i>. That's short for <i>Light Weight
- Process</i>. If you've got a multiprocessor, this flag will
- allocate a new schedulable process and decrease the odds of your
- threads all fighting for the same process. Of course, if you
- have a uni-processor, it will neither help nor hurt. Since I
- developed these on a uni-processor, I've been a bit inconsistent
- in the use of <i>THR_NEW_LWP</i>.
-
-<P>Well, that's it!&nbsp; After all the talk &amp; the hype, you would
-have expected it to be more difficult to create a multi-threaded server.&nbsp;
-Surprise!&nbsp; It really is that easy.&nbsp; You still have to handle
-contention issues which we haven't addressed here and that is a rather
-nasty topic.&nbsp; Still, for the simple case, this is all you have to
-do.
-
-<P>The next page is the last for this tutorial.&nbsp; Head on over there
-&amp; 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>