diff options
Diffstat (limited to 'docs/tutorials/006')
-rw-r--r-- | docs/tutorials/006/006.dsp | 112 | ||||
-rw-r--r-- | docs/tutorials/006/Makefile | 114 | ||||
-rw-r--r-- | docs/tutorials/006/client_acceptor.h | 74 | ||||
-rw-r--r-- | docs/tutorials/006/client_handler.cpp | 291 | ||||
-rw-r--r-- | docs/tutorials/006/client_handler.h | 118 | ||||
-rw-r--r-- | docs/tutorials/006/combine.shar | 508 | ||||
-rw-r--r-- | docs/tutorials/006/page01.html | 62 | ||||
-rw-r--r-- | docs/tutorials/006/page02.html | 145 | ||||
-rw-r--r-- | docs/tutorials/006/page03.html | 112 | ||||
-rw-r--r-- | docs/tutorials/006/page04.html | 153 | ||||
-rw-r--r-- | docs/tutorials/006/page05.html | 348 | ||||
-rw-r--r-- | docs/tutorials/006/page06.html | 46 | ||||
-rw-r--r-- | docs/tutorials/006/server.cpp | 112 |
13 files changed, 0 insertions, 2195 deletions
diff --git a/docs/tutorials/006/006.dsp b/docs/tutorials/006/006.dsp deleted file mode 100644 index fe6c488995d..00000000000 --- a/docs/tutorials/006/006.dsp +++ /dev/null @@ -1,112 +0,0 @@ -# Microsoft Developer Studio Project File - Name="006" - Package Owner=<4>
-# Microsoft Developer Studio Generated Build File, Format Version 6.00
-# ** DO NOT EDIT **
-
-# TARGTYPE "Win32 (x86) Console Application" 0x0103
-
-CFG=006 - Win32 Debug
-!MESSAGE This is not a valid makefile. To build this project using NMAKE,
-!MESSAGE use the Export Makefile command and run
-!MESSAGE
-!MESSAGE NMAKE /f "006.mak".
-!MESSAGE
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "006.mak" CFG="006 - Win32 Debug"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "006 - Win32 Release" (based on "Win32 (x86) Console Application")
-!MESSAGE "006 - Win32 Debug" (based on "Win32 (x86) Console Application")
-!MESSAGE
-
-# Begin Project
-# PROP Scc_ProjName ""
-# PROP Scc_LocalPath ""
-CPP=cl.exe
-RSC=rc.exe
-
-!IF "$(CFG)" == "006 - Win32 Release"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir "Release"
-# PROP BASE Intermediate_Dir "Release"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 0
-# PROP Output_Dir "Release"
-# PROP Intermediate_Dir "Release"
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\.." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD BASE RSC /l 0x409 /d "NDEBUG"
-# ADD RSC /l 0x409 /d "NDEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
-# ADD LINK32 ace.lib /nologo /subsystem:console /machine:I386 /libpath:"..\..\..\ace"
-
-!ELSEIF "$(CFG)" == "006 - Win32 Debug"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir "Debug"
-# PROP BASE Intermediate_Dir "Debug"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 1
-# PROP Output_Dir "Debug"
-# PROP Intermediate_Dir "Debug"
-# PROP Ignore_Export_Lib 0
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /W3 /GX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /MDd /W3 /GX /Od /I "..\..\.." /D "WIN32" /D "_DEBUG" /YX /FD /c
-# ADD BASE RSC /l 0x409 /d "_DEBUG"
-# ADD RSC /l 0x409 /d "_DEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
-# ADD LINK32 aced.lib /nologo /subsystem:console /debug /machine:I386 /out:"server.exe" /pdbtype:sept /libpath:"..\..\..\ace"
-
-!ENDIF
-
-# Begin Target
-
-# Name "006 - Win32 Release"
-# Name "006 - Win32 Debug"
-# Begin Group "Source Files"
-
-# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
-# Begin Source File
-
-SOURCE=.\client_handler.cpp
-# End Source File
-# Begin Source File
-
-SOURCE=.\server.cpp
-# End Source File
-# End Group
-# Begin Group "Header Files"
-
-# PROP Default_Filter "h;hpp;hxx;hm;inl"
-# Begin Source File
-
-SOURCE=.\client_acceptor.h
-# End Source File
-# Begin Source File
-
-SOURCE=.\client_handler.h
-# End Source File
-# End Group
-# Begin Group "Resource Files"
-
-# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
-# End Group
-# End Target
-# End Project
diff --git a/docs/tutorials/006/Makefile b/docs/tutorials/006/Makefile deleted file mode 100644 index 57a90088d72..00000000000 --- a/docs/tutorials/006/Makefile +++ /dev/null @@ -1,114 +0,0 @@ -#---------------------------------------------------------------------------- -# $Id$ -#---------------------------------------------------------------------------- - -#---------------------------------------------------------------------------- -# Local macros -#---------------------------------------------------------------------------- - -# You can generally find a Makefile in the ACE examples, tests or the library -# itself that will satisfy our application needs. This one was taken from -# one of the examples. - - # Define the name of the binary we want to create. There has to be - # a CPP file $(BIN).cpp but it doesn't necessarily have to have your - # main() in it. Most of the time, though, it will. -BIN = server - - # Few applications will have a single source file. We use the FILES - # macro to build up a list of additional files to compile. Notice - # that we leave off the extension just as with BIN -FILES = -FILES += client_handler - - # The BUILD macro is used by the ACE makefiles. Basically, it tells - # the system what to build. I don't really know what VBIN is other - # than it is constructed from the value of BIN. Just go with it... -BUILD = $(VBIN) - - # Here we use some GNU make extensions to build the SRC macro. Basically, - # we're just adding .cpp to the value of BIN and for each entry of the - # FILES macro. -SRC = $(addsuffix .cpp,$(BIN)) $(addsuffix .cpp,$(FILES)) - - # This is used by my Indent target below. It's not a part of standard - # ACE and you don't need it yourself. -HDR = *.h - -#---------------------------------------------------------------------------- -# Include macros and targets -#---------------------------------------------------------------------------- - - # This is where the real power lies! These included makefile components - # are similar to the C++ templates in ACE. That is, they do a tremendous - # amount of work for you and all you have to do is include them. - # As a matter of fact, in our project, I created a single file named - # "app.mk" that includes all of these. Our project makefiles then just - # need to include app.mk to get everything they need. - -include $(ACE_ROOT)/include/makeinclude/wrapper_macros.GNU -include $(ACE_ROOT)/include/makeinclude/macros.GNU -include $(ACE_ROOT)/include/makeinclude/rules.common.GNU -include $(ACE_ROOT)/include/makeinclude/rules.nonested.GNU -include $(ACE_ROOT)/include/makeinclude/rules.bin.GNU -include $(ACE_ROOT)/include/makeinclude/rules.local.GNU - -#---------------------------------------------------------------------------- -# Local targets -#---------------------------------------------------------------------------- - - # Sometimes I like to reformat my code to make it more readable. This is - # more useful for the comments than anything else. Unfortunately, the - # "indent" program doesn't quite grok C++ so I have to post-process it's - # output just a bit. -Indent : # - for i in $(SRC) $(HDR) ; do \ - indent -npsl -l80 -fca -fc1 -cli0 -cdb < $$i | \ - sed -e 's/: :/::/g' \ - -e 's/^.*\(public:\)/\1/' \ - -e 's/^.*\(protected:\)/\1/' \ - -e 's/^.*\(private:\)/\1/' \ - -e 's/:\(public\)/ : \1/' \ - -e 's/:\(protected\)/ : \1/' \ - -e 's/:\(private\)/ : \1/' \ - > $$i~ ;\ - mv $$i~ $$i ;\ - done - - # One of the targets in the ACE makefiles is "depend". It will invoke - # your compiler in a way that will generate a list of dependencies for - # you. This is a great thing! Unfortunately, it puts all of that mess - # directly into the Makefile. I prefer my Makefile to stay clean and - # uncluttered. The perl script referenced here pulls the dependency - # stuff back out of the Makefile and into a file ".depend" which we then - # include just like the makefile components above. -Depend : depend - perl ../fix.Makefile - -.depend : # - touch .depend - - -HTML : # - [ -f hdr ] || $(MAKE) UNSHAR - perl ../combine *.pre ; chmod +r *.html - -SHAR : # - [ ! -f combine.shar ] || exit 1 - shar -T hdr bodies *.pre *.pst > combine.shar && $(RM) hdr bodies *.pre *.pst - -UNSHAR : # - sh combine.shar - -CLEAN : realclean - $(RM) hdr bodies *.pre *.pst .depend - -#---------------------------------------------------------------------------- -# Dependencies -#---------------------------------------------------------------------------- - - # Don't put anything below here. Between the "depend" target and fix.Makefile - # it's guaranteed to be lost! - - # This is inserted by the fix.Makefile script -include .depend diff --git a/docs/tutorials/006/client_acceptor.h b/docs/tutorials/006/client_acceptor.h deleted file mode 100644 index 4a50de72952..00000000000 --- a/docs/tutorials/006/client_acceptor.h +++ /dev/null @@ -1,74 +0,0 @@ -// $Id$ - -#ifndef CLIENT_ACCEPTOR_H -#define CLIENT_ACCEPTOR_H - -/* The ACE_Acceptor<> template lives in the ace/Acceptor.h header - file. You'll find a very consistent naming convention between the - ACE objects and the headers where they can be found. In general, - the ACE object ACE_Foobar will be found in ace/Foobar.h. */ - -#include "ace/Acceptor.h" - -#if !defined (ACE_LACKS_PRAGMA_ONCE) -# pragma once -#endif /* ACE_LACKS_PRAGMA_ONCE */ - -/* Since we want to work with sockets, we'll need a SOCK_Acceptor to - allow the clients to connect to us. */ -#include "ace/SOCK_Acceptor.h" - -/* The Client_Handler object we develop will be used to handle clients - once they're connected. The ACE_Acceptor<> template's first - parameter requires such an object. In some cases, you can get by - with just a forward declaration on the class, in others you have to - have the whole thing. */ -#include "client_handler.h" - -/* Parameterize the ACE_Acceptor<> such that it will listen for socket - connection attempts and create Client_Handler objects when they - happen. In Tutorial 001, we wrote the basic acceptor logic on our - own before we realized that ACE_Acceptor<> was available. You'll - get spoiled using the ACE templates because they take away a lot of - the tedious details! */ -typedef ACE_Acceptor <Client_Handler, ACE_SOCK_ACCEPTOR> Client_Acceptor_Base; - -/* Here, we use the parameterized ACE_Acceptor<> as a baseclass for - our customized Client_Acceptor object. I've done this so that we - can provide it with our choice of concurrency strategies when the - object is created. Each Client_Handler it creates will use this - information to determine how to act. If we were going to create a - system that was always thread-per-connection, we would not have - bothered to extend Client_Acceptor. */ -class Client_Acceptor : public Client_Acceptor_Base -{ -public: - /* - This is always a good idea. If nothing else, it makes your code more - orthogonal no matter what baseclasses your objects have. - */ - typedef Client_Acceptor_Base inherited; - - /* - Construct the object with the concurrency strategy. Since this tutorial - is focused on thread-per-connection, we make that the default. We could - have chosen to omitt the default and populate it in main() instead. - */ - Client_Acceptor (int thread_per_connection = 1) - : thread_per_connection_ (thread_per_connection) - { - } - - /* Return the value of our strategy flag. This is used by the - Client_Handler to decide how to act. If 'true' then the handler - will behave in a thread-per-connection manner. */ - int thread_per_connection (void) - { - return this->thread_per_connection_; - } - -protected: - int thread_per_connection_; -}; - -#endif /* CLIENT_ACCEPTOR_H */ diff --git a/docs/tutorials/006/client_handler.cpp b/docs/tutorials/006/client_handler.cpp deleted file mode 100644 index 9ea01d64cda..00000000000 --- a/docs/tutorials/006/client_handler.cpp +++ /dev/null @@ -1,291 +0,0 @@ -// $Id$ - -/* 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. */ -#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... */ -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. */ -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. */ -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! */ - 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 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... */ -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. */ - 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. */ - 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. - - 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. */ - 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. - // ************************************************************************ - - /* Our reactor reference will be set when we register ourselves but - I decided to go ahead and set it here. No good reason really... */ - 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. */ - 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_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. */ -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. */ - 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! */ - 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. - - 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. */ - 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. */ -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. - - 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. */ -int -Client_Handler::svc(void) -{ - /* 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(). */ - char buf[BUFSIZ]; - - // Forever... - while( 1 ) - { - /* 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. */ - if (this->process(buf, sizeof (buf)) == -1) - return -1; - } - - return 0; -} - -/* 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. */ -int -Client_Handler::process (char *rdbuf, - int rdbuf_len) -{ - /* 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. */ - switch (this->peer ().recv (rdbuf, rdbuf_len)) - { - case -1: - ACE_ERROR_RETURN ((LM_ERROR, - "(%P|%t) %p bad read\n", - "client"), - -1); - case 0: - ACE_ERROR_RETURN ((LM_ERROR, - "(%P|%t) closing daemon (fd = %d)\n", - this->get_handle ()), - -1); - default: - ACE_DEBUG ((LM_DEBUG, - "(%P|%t) from client: %s", - rdbuf)); - } - - return 0; -} diff --git a/docs/tutorials/006/client_handler.h b/docs/tutorials/006/client_handler.h deleted file mode 100644 index e8d3695d37c..00000000000 --- a/docs/tutorials/006/client_handler.h +++ /dev/null @@ -1,118 +0,0 @@ -// $Id$ - -#ifndef CLIENT_HANDLER_H -#define CLIENT_HANDLER_H - -/* Our client handler must exist somewhere in the ACE_Event_Handler - object hierarchy. This is a requirement of the ACE_Reactor because - it maintains ACE_Event_Handler pointers for each registered event - handler. You could derive our Client_Handler directly from - ACE_Event_Handler but you still have to have an ACE_SOCK_Stream for - the actually connection. With a direct derivative of - ACE_Event_Handler, you'll have to contain and maintain an - ACE_SOCK_Stream instance yourself. With ACE_Svc_Handler (which is - a derivative of ACE_Event_Handler) some of those details are - handled for you. - - */ - -#include "ace/Svc_Handler.h" - -#if !defined (ACE_LACKS_PRAGMA_ONCE) -# pragma once -#endif /* ACE_LACKS_PRAGMA_ONCE */ - -#include "ace/SOCK_Stream.h" - -/* Another feature of ACE_Svc_Handler is it's ability to present the - ACE_Task<> interface as well. That's what the ACE_NULL_SYNCH - parameter below is all about. If our Client_Acceptor has chosen - thread-per-connection then our open() method will activate us into - a thread. At that point, our svc() method will execute. We still - don't take advantage of the things ACE_NULL_SYNCH exists for but - stick around for Tutorial 7 and pay special attention to the - Thread_Pool object there for an explanation. */ -class Client_Handler : public ACE_Svc_Handler <ACE_SOCK_STREAM, ACE_NULL_SYNCH> -{ -public: - typedef ACE_Svc_Handler <ACE_SOCK_STREAM, ACE_NULL_SYNCH> inherited; - - // Constructor... - Client_Handler (void); - - /* The destroy() method is our preferred method of destruction. We - could have overloaded the delete operator but that is neither easy - nor intuitive (at least to me). Instead, we provide a new method - of destruction and we make our destructor protected so that only - ourselves, our derivatives and our friends can delete us. It's a - nice compromise. */ - void destroy (void); - - /* Most ACE objects have an open() method. That's how you make them - ready to do work. ACE_Event_Handler has a virtual open() method - which allows us to create this overrride. ACE_Acceptor<> will - invoke this method after creating a new Client_Handler when a - client connects. Notice that the parameter to open() is a void*. - It just so happens that the pointer points to the acceptor which - created us. You would like for the parameter to be an - ACE_Acceptor<>* but since ACE_Event_Handler is generic, that would - tie it too closely to the ACE_Acceptor<> set of objects. In our - definition of open() you'll see how we get around that. */ - int open (void *acceptor); - - /* When an ACE_Task<> object falls out of the svc() method, the - framework will call the close() method. That's where we want to - cleanup ourselves if we're running in either thread-per-connection - or thread-pool mode. */ - int close (u_long flags = 0); - - /* When there is activity on a registered handler, the - handle_input() method of the handler will be invoked. If that - method returns an error code (eg -- -1) then the reactor will - invoke handle_close() to allow the object to clean itself - up. Since an event handler can be registered for more than one - type of callback, the callback mask is provided to inform - handle_close() exactly which method failed. That way, you don't - have to maintain state information between your handle_* method - calls. The <handle> parameter is explained below... As a - side-effect, the reactor will also invoke remove_handler() for the - object on the mask that caused the -1 return. This means that we - don't have to do that ourselves! */ - virtual int handle_close (ACE_HANDLE handle = ACE_INVALID_HANDLE, - ACE_Reactor_Mask mask = ACE_Event_Handler::ALL_EVENTS_MASK); - -protected: - - /* If the Client_Acceptor which created us has chosen a - thread-per-connection strategy then our open() method will - activate us into a dedicate thread. The svc() method will then - execute in that thread performing some of the functions we used to - leave up to the reactor. */ - int svc (void); - - /* When we register with the reactor, we're going to tell it that we - want to be notified of READ events. When the reactor sees that - there is read activity for us, our handle_input() will be - invoked. The _handleg provided is the handle (file descriptor in - Unix) of the actual connection causing the activity. Since we're - derived from ACE_Svc_Handler<> and it maintains it's own peer - (ACE_SOCK_Stream) object, this is redundant for us. However, if - we had been derived directly from ACE_Event_Handler, we may have - chosen not to contain the peer. In that case, the <handle> would - be important to us for reading the client's data. */ - int handle_input (ACE_HANDLE handle); - - /* This has nothing at all to do with ACE. I've added this here as - a worker function which I will call from handle_input(). As - promised in Tutorial 5 I will use this now to make it easier to - switch between our two possible concurrency strategies. */ - int process (char *rdbuf, int rdbuf_len); - - /* We don't really do anything in our destructor but we've declared - it to be protected to prevent casual deletion of this object. As - I said above, I really would prefer that everyone goes through the - destroy() method to get rid of us. */ - ~Client_Handler (void); -}; - -#endif /* CLIENT_HANDLER_H */ diff --git a/docs/tutorials/006/combine.shar b/docs/tutorials/006/combine.shar deleted file mode 100644 index 48385d7b563..00000000000 --- a/docs/tutorials/006/combine.shar +++ /dev/null @@ -1,508 +0,0 @@ -#!/bin/sh -# This is a shell archive (produced by GNU sharutils 4.2). -# To extract the files from this archive, save it to some FILE, remove -# everything before the `!/bin/sh' line above, then type `sh FILE'. -# -# Made on 1999-09-21 22:49 EDT by <jcej@chiroptera.tragus.org>. -# Source directory was `/home/jcej/projects/ACE_wrappers/docs/tutorials/006'. -# -# Existing files will *not* be overwritten unless `-c' is specified. -# -# This shar contains: -# length mode name -# ------ ---------- ------------------------------------------ -# 605 -rw-rw-r-- hdr -# 72 -rw-rw-r-- bodies -# 1974 -rw-rw-r-- page01.pre -# 252 -rw-rw-r-- page02.pre -# 507 -rw-rw-r-- page03.pre -# 227 -rw-rw-r-- page04.pre -# 231 -rw-rw-r-- page05.pre -# 697 -rw-rw-r-- page06.pre -# 89 -rw-rw-r-- page02.pst -# 168 -rw-rw-r-- page03.pst -# 175 -rw-rw-r-- page04.pst -# 1420 -rw-rw-r-- page05.pst -# -save_IFS="${IFS}" -IFS="${IFS}:" -gettext_dir=FAILED -locale_dir=FAILED -first_param="$1" -for dir in $PATH -do - if test "$gettext_dir" = FAILED && test -f $dir/gettext \ - && ($dir/gettext --version >/dev/null 2>&1) - then - set `$dir/gettext --version 2>&1` - if test "$3" = GNU - then - gettext_dir=$dir - fi - fi - if test "$locale_dir" = FAILED && test -f $dir/shar \ - && ($dir/shar --print-text-domain-dir >/dev/null 2>&1) - then - locale_dir=`$dir/shar --print-text-domain-dir` - fi -done -IFS="$save_IFS" -if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED -then - echo=echo -else - TEXTDOMAINDIR=$locale_dir - export TEXTDOMAINDIR - TEXTDOMAIN=sharutils - export TEXTDOMAIN - echo="$gettext_dir/gettext -s" -fi -touch -am 1231235999 $$.touch >/dev/null 2>&1 -if test ! -f 1231235999 && test -f $$.touch; then - shar_touch=touch -else - shar_touch=: - echo - $echo 'WARNING: not restoring timestamps. Consider getting and' - $echo "installing GNU \`touch', distributed in GNU File Utilities..." - echo -fi -rm -f 1231235999 $$.touch -# -if mkdir _sh04795; then - $echo 'x -' 'creating lock directory' -else - $echo 'failed to create lock directory' - exit 1 -fi -# ============= hdr ============== -if test -f 'hdr' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'hdr' '(file already exists)' -else - $echo 'x -' extracting 'hdr' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'hdr' && -<HTML> -<HEAD> -X <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> -X <META NAME="GENERATOR" CONTENT="Mozilla/4.04 [en] (X11; I; Linux 2.0.32 i486) [Netscape]"> -X <META NAME="Author" CONTENT="James CE Johnson"> -X <META NAME="Description" CONTENT="A first step towards using ACE productively"> -X <TITLE>ACE Tutorial 006</TITLE> -</HEAD> -<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> -X -<CENTER><B><FONT SIZE=+2>ACE Tutorial 006</FONT></B></CENTER> -X -<CENTER><B><FONT SIZE=+2>Creating a thread-per-connection server</FONT></B></CENTER> -X -X -<P> -<HR WIDTH="100%"> -SHAR_EOF - $shar_touch -am 0118202399 'hdr' && - chmod 0664 'hdr' || - $echo 'restore of' 'hdr' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'hdr:' 'MD5 check failed' -3289bf210fdf2f4b9d0a23b69c79a82f hdr -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'hdr'`" - test 605 -eq "$shar_count" || - $echo 'hdr:' 'original size' '605,' 'current size' "$shar_count!" - fi -fi -# ============= bodies ============== -if test -f 'bodies' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'bodies' '(file already exists)' -else - $echo 'x -' extracting 'bodies' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'bodies' && -PAGE=2 -server.cpp -client_acceptor.h -client_handler.h -client_handler.cpp -SHAR_EOF - $shar_touch -am 0118202399 'bodies' && - chmod 0664 'bodies' || - $echo 'restore of' 'bodies' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'bodies:' 'MD5 check failed' -1f7383474ecfc75883354e67afaf1b3b bodies -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'bodies'`" - test 72 -eq "$shar_count" || - $echo 'bodies:' 'original size' '72,' 'current size' "$shar_count!" - fi -fi -# ============= page01.pre ============== -if test -f 'page01.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page01.pre' '(file already exists)' -else - $echo 'x -' extracting 'page01.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page01.pre' && -X -<P>In this tutorial, we're going to extend Tutorial 5 to create a thread-per-connection -server. This implementation will create a new thread for each client -which connects to us. The ACE_Reactor is still used but only for -accepting new connections. The Client_Handler objects won't be registered -with the reactor. Instead, they'll be responsible for monitoring -their peer() directly. -<P> -Abstract:<sup>*</sup> -<ul> -Here, we build a thread-per-connection simple server. This is the next -step from a simple single-threaded server towards a multithreaded -server. -<p> -We make use of the Strategy Pattern in this example. -The ACE_Acceptor inherits form the ACE_Acceptor_Base class which -facilitates us to implement various different concurrency strategies -depending on whether the server is single-threaded or the server creates -a new thread per connection. This also allows us to extend the -capabilities of the server in the future by implementing a different -strategy. -<p> -This information is passed on to the Client_Handler -(remember ACE_Acceptor < Client_Handler, ACE_SOCK_ACCEPTOR > ?). -The Client_Handler is an ACE_Svc_Handler as the Svc_Handler is a -derivative of the Event_Handler and is associated with -ACE_Sock_Stream. It is also derived form the ACE_Task class which allows -us to have a thread per connection. -<p> -We incorporate the data processing in the svc() method, which will be -called per thread for the thread-per-connection server. -<p> -Note that here all the Client_Handler objects aren't registered with the -reactor. The Reactor is only used to accept client connections. Once a -thread has been deicated per connection, the Client Handler object -reponsible for that client connection now takes up the job of the -reactor and handles future events. -<p> -Thus a simple, thread-per-connection server has been built which doesnt -delve too deeply into mutli-threading issues. -</ul> -<font size=-1>* Abstract by Kirthika as always</font> -SHAR_EOF - $shar_touch -am 0118203099 'page01.pre' && - chmod 0664 'page01.pre' || - $echo 'restore of' 'page01.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page01.pre:' 'MD5 check failed' -b66014851d6db1c8d89a07d024be2ecb page01.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page01.pre'`" - test 1974 -eq "$shar_count" || - $echo 'page01.pre:' 'original size' '1974,' 'current size' "$shar_count!" - fi -fi -# ============= page02.pre ============== -if test -f 'page02.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page02.pre' '(file already exists)' -else - $echo 'x -' extracting 'page02.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page02.pre' && -<P>Again, we begin with <A HREF="server.cpp">server.cpp.</A> If you -look closely you will see that the only difference between this and the -Tutorial 5 implementation is a single comment. -X -<P> -<HR WIDTH="100%"><FONT FACE="Arial,Helvetica"></FONT> -SHAR_EOF - $shar_touch -am 0118202399 'page02.pre' && - chmod 0664 'page02.pre' || - $echo 'restore of' 'page02.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page02.pre:' 'MD5 check failed' -e9e18b8add5d997189fb16e67d1467b2 page02.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page02.pre'`" - test 252 -eq "$shar_count" || - $echo 'page02.pre:' 'original size' '252,' 'current size' "$shar_count!" - fi -fi -# ============= page03.pre ============== -if test -f 'page03.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page03.pre' '(file already exists)' -else - $echo 'x -' extracting 'page03.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page03.pre' && -<P>In <A HREF="client_acceptor.h">client_acceptor.h</A>, we've extended -our object just a bit. The primary reason is to allow us to select -the previous single-threaded implementation or our new thread-per-connection -implementation. Client_Acceptor itself doesn't use this information -but makes it available to the Client_Handler objects it creates. -If we wanted a single-strategy implementation, we would have made no changes -to the Tutorial 5 version of this file. -X -<P> -<HR WIDTH="100%"> -SHAR_EOF - $shar_touch -am 0118202399 'page03.pre' && - chmod 0664 'page03.pre' || - $echo 'restore of' 'page03.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page03.pre:' 'MD5 check failed' -40dd465ac9815a2c35375ccdbad0c98b page03.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page03.pre'`" - test 507 -eq "$shar_count" || - $echo 'page03.pre:' 'original size' '507,' 'current size' "$shar_count!" - fi -fi -# ============= page04.pre ============== -if test -f 'page04.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page04.pre' '(file already exists)' -else - $echo 'x -' extracting 'page04.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page04.pre' && -<P><A HREF="client_handler.h">client_handler.h</A> -shows a few more changes than the previous sources. The important -change is the addition of a svc() method where our connection thread will -exist. -X -<P> -<HR WIDTH="100%"> -SHAR_EOF - $shar_touch -am 0118202399 'page04.pre' && - chmod 0664 'page04.pre' || - $echo 'restore of' 'page04.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page04.pre:' 'MD5 check failed' -8256aff03563fbc281403fc5bb970e69 page04.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pre'`" - test 227 -eq "$shar_count" || - $echo 'page04.pre:' 'original size' '227,' 'current size' "$shar_count!" - fi -fi -# ============= page05.pre ============== -if test -f 'page05.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page05.pre' '(file already exists)' -else - $echo 'x -' extracting 'page05.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page05.pre' && -<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(). -X -<P> -<HR WIDTH="100%"> -SHAR_EOF - $shar_touch -am 0118202399 'page05.pre' && - chmod 0664 'page05.pre' || - $echo 'restore of' 'page05.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page05.pre:' 'MD5 check failed' -08278c0477a109e107b680424bf70a9d page05.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page05.pre'`" - test 231 -eq "$shar_count" || - $echo 'page05.pre:' 'original size' '231,' 'current size' "$shar_count!" - fi -fi -# ============= page06.pre ============== -if test -f 'page06.pre' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page06.pre' '(file already exists)' -else - $echo 'x -' extracting 'page06.pre' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page06.pre' && -<P>That's it for Tutorial 6. With very little effort we've managed -to extend the previous single-threaded server to an implementation which -allows runtime selection of single or multi-threaded operation. In -Tutorial 7 we'll extend that again to allow a thread-pool choice in addition -to the current two. -X -<P>For reference, here's the file list again: -<UL> -<LI> -<A HREF="Makefile">Makefile</A></LI> -X -<LI> -<A HREF="client_acceptor.h">client_acceptor.h</A></LI> -X -<LI> -<A HREF="client_handler.cpp">client_handler.cpp</A></LI> -X -<LI> -<A HREF="client_handler.h">client_handler.h</A></LI> -X -<LI> -<A HREF="server.cpp">server.cpp</A></LI> -X -<LI> -<A HREF="fix.Makefile">fix.Makefile</A></LI> -</UL> -SHAR_EOF - $shar_touch -am 0118202399 'page06.pre' && - chmod 0664 'page06.pre' || - $echo 'restore of' 'page06.pre' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page06.pre:' 'MD5 check failed' -0adca372a5154acf673cc373d2acaf5a page06.pre -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page06.pre'`" - test 697 -eq "$shar_count" || - $echo 'page06.pre:' 'original size' '697,' 'current size' "$shar_count!" - fi -fi -# ============= page02.pst ============== -if test -f 'page02.pst' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page02.pst' '(file already exists)' -else - $echo 'x -' extracting 'page02.pst' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page02.pst' && -<HR WIDTH="100%"> -X -<P>Let's move along and see what happend to the Client_Acceptor. -X -<P> -SHAR_EOF - $shar_touch -am 0118202399 'page02.pst' && - chmod 0664 'page02.pst' || - $echo 'restore of' 'page02.pst' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page02.pst:' 'MD5 check failed' -0188a5ff7cacc123676e420ac5432207 page02.pst -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page02.pst'`" - test 89 -eq "$shar_count" || - $echo 'page02.pst:' 'original size' '89,' 'current size' "$shar_count!" - fi -fi -# ============= page03.pst ============== -if test -f 'page03.pst' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page03.pst' '(file already exists)' -else - $echo 'x -' extracting 'page03.pst' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page03.pst' && -<HR WIDTH="100%"> -X -<P>Ok, so far we haven't done much to change our concurrency strategy. -Let's move on to the Client_Handler and see if it has changed any. -X -<P> -SHAR_EOF - $shar_touch -am 0118202399 'page03.pst' && - chmod 0664 'page03.pst' || - $echo 'restore of' 'page03.pst' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page03.pst:' 'MD5 check failed' -7743577254d06f5848b5e50f3b6c3014 page03.pst -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page03.pst'`" - test 168 -eq "$shar_count" || - $echo 'page03.pst:' 'original size' '168,' 'current size' "$shar_count!" - fi -fi -# ============= page04.pst ============== -if test -f 'page04.pst' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page04.pst' '(file already exists)' -else - $echo 'x -' extracting 'page04.pst' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page04.pst' && -<HR WIDTH="100%"> -X -<P>So... we've added a svc() method and alluded to changes in open(). -Let's move on to the object definition and see what all the fuss is about. -X -<P> -SHAR_EOF - $shar_touch -am 0118202399 'page04.pst' && - chmod 0664 'page04.pst' || - $echo 'restore of' 'page04.pst' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page04.pst:' 'MD5 check failed' -dfe0897cc3f000b69c16c87dd1596281 page04.pst -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pst'`" - test 175 -eq "$shar_count" || - $echo 'page04.pst:' 'original size' '175,' 'current size' "$shar_count!" - fi -fi -# ============= page05.pst ============== -if test -f 'page05.pst' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'page05.pst' '(file already exists)' -else - $echo 'x -' extracting 'page05.pst' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'page05.pst' && -<HR WIDTH="100%"> -<P> -X Did you notice the <i>THR_DETACHED</i> flag on the call to -X <i>activate()</i>? Threads, like any system resource, are a -X limited resource. Unless we intend to <i>join()</i> or -X <i>wait()</i> for the new thread later, we want use THR_DETACHED -X so that we don't cause a leak. In fact, in most cases, you'll -X want to specify THR_DETACHED because it's just easier. -<p> -X Another handy flag for use with <i>activate()</i> is -X <i>THR_NEW_LWP</i>. That's short for <i>Light Weight -X Process</i>. If you've got a multiprocessor, this flag will -X allocate a new schedulable process and decrease the odds of your -X threads all fighting for the same process. Of course, if you -X have a uni-processor, it will neither help nor hurt. Since I -X developed these on a uni-processor, I've been a bit inconsistent -X in the use of <i>THR_NEW_LWP</i>. -X -<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. -X -<P>The next page is the last for this tutorial. Head on over there -& we'll round up the file list one last time. -X -<P> -SHAR_EOF - $shar_touch -am 0921222699 'page05.pst' && - chmod 0664 'page05.pst' || - $echo 'restore of' 'page05.pst' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'page05.pst:' 'MD5 check failed' -fab2e03eb2f115f89a58d70be291e87e page05.pst -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page05.pst'`" - test 1420 -eq "$shar_count" || - $echo 'page05.pst:' 'original size' '1420,' 'current size' "$shar_count!" - fi -fi -rm -fr _sh04795 -exit 0 diff --git a/docs/tutorials/006/page01.html b/docs/tutorials/006/page01.html deleted file mode 100644 index 3ff14911f04..00000000000 --- a/docs/tutorials/006/page01.html +++ /dev/null @@ -1,62 +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>In this tutorial, we're going to extend Tutorial 5 to create a thread-per-connection -server. This implementation will create a new thread for each client -which connects to us. The ACE_Reactor is still used but only for -accepting new connections. The Client_Handler objects won't be registered -with the reactor. Instead, they'll be responsible for monitoring -their peer() directly. -<P> -Abstract:<sup>*</sup> -<ul> -Here, we build a thread-per-connection simple server. This is the next -step from a simple single-threaded server towards a multithreaded -server. -<p> -We make use of the Strategy Pattern in this example. -The ACE_Acceptor inherits form the ACE_Acceptor_Base class which -facilitates us to implement various different concurrency strategies -depending on whether the server is single-threaded or the server creates -a new thread per connection. This also allows us to extend the -capabilities of the server in the future by implementing a different -strategy. -<p> -This information is passed on to the Client_Handler -(remember ACE_Acceptor < Client_Handler, ACE_SOCK_ACCEPTOR > ?). -The Client_Handler is an ACE_Svc_Handler as the Svc_Handler is a -derivative of the Event_Handler and is associated with -ACE_Sock_Stream. It is also derived form the ACE_Task class which allows -us to have a thread per connection. -<p> -We incorporate the data processing in the svc() method, which will be -called per thread for the thread-per-connection server. -<p> -Note that here all the Client_Handler objects aren't registered with the -reactor. The Reactor is only used to accept client connections. Once a -thread has been deicated per connection, the Client Handler object -reponsible for that client connection now takes up the job of the -reactor and handles future events. -<p> -Thus a simple, thread-per-connection server has been built which doesnt -delve too deeply into mutli-threading issues. -</ul> -<font size=-1>* Abstract by Kirthika as always</font> -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page02.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/006/page02.html b/docs/tutorials/006/page02.html deleted file mode 100644 index e56c466767d..00000000000 --- a/docs/tutorials/006/page02.html +++ /dev/null @@ -1,145 +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>Again, we begin with <A HREF="server.cpp">server.cpp.</A> If you -look closely you will see that the only difference between this and the -Tutorial 5 implementation is a single comment. - -<P> -<HR WIDTH="100%"><FONT FACE="Arial,Helvetica"></FONT> -<PRE> -<font color=red>// $Id$</font> - -<font color=red>/* We try to keep main() very simple. One of the ways we do that is - to push much of the complicated stuff into worker objects. In this - case, we only need to include the acceptor header in our main - source file. We let it worry about the "<font color=green>real work</font>". */</font> - -<font color=blue>#include</font> "<font color=green>client_acceptor.h</font>" - -<font color=red>/* As before, we create a simple signal handler that will set our - finished flag. There are, of course, more elegant ways to handle - program shutdown requests but that isn't really our focus right - now, so we'll just do the easiest thing. */</font> - -static sig_atomic_t finished = 0; -extern "<font color=green>C</font>" void handler (int) -{ - finished = 1; -} - -<font color=red>/* A server has to listen for clients at a known TCP/IP port. The - default ACE port is 10002 (at least on my system) and that's good - enough for what we want to do here. Obviously, a more robust - application would take a command line parameter or read from a - configuration file or do some other clever thing. Just like the - signal handler above, though, that's what we want to focus on, so - we're taking the easy way out. */</font> - -static const u_short PORT = ACE_DEFAULT_SERVER_PORT; - -<font color=red>/* Finally, we get to main. Some C++ compilers will complain loudly - if your function signature doesn't match the prototype. Even - though we're not going to use the parameters, we still have to - specify them. */</font> - -int -main (int argc, char *argv[]) -{ - <font color=red>/* In our earlier servers, we used a global pointer to get to the - reactor. I've never really liked that idea, so I've moved it into - main() this time. When we get to the Client_Handler object you'll - see how we manage to get a pointer back to this reactor. */</font> - ACE_Reactor reactor; - - <font color=red>/* The acceptor will take care of letting clients connect to us. It - will also arrange for a Client_Handler to be created for each new - client. Since we're only going to listen at one TCP/IP port, we - only need one acceptor. If we wanted, though, we could create - several of these and listen at several ports. (That's what we - would do if we wanted to rewrite inetd for instance.) */</font> - Client_Acceptor peer_acceptor; - - <font color=red>/* Create an ACE_INET_Addr that represents our endpoint of a - connection. We then open our acceptor object with that Addr. - Doing so tells the acceptor where to listen for connections. - Servers generally listen at "<font color=green>well known</font>" addresses. If not, there - must be some mechanism by which the client is informed of the - server's address. - - Note how ACE_ERROR_RETURN is used if we fail to open the acceptor. - This technique is used over and over again in our tutorials. */</font> - if (peer_acceptor.open (ACE_INET_Addr (PORT), - &reactor) == -1) - ACE_ERROR_RETURN ((LM_ERROR, - "<font color=green>%p\n</font>", - "<font color=green>open</font>"), - -1); - - <font color=red>/* As with Tutorial 5, we know that we're now registered with our - reactor so we don't have to mess with that step. */</font> - - <font color=red>/* Install our signal handler. You can actually register signal - handlers with the reactor. You might do that when the signal - handler is responsible for performing "<font color=green>real</font>" work. Our simple - flag-setter doesn't justify deriving from ACE_Event_Handler and - providing a callback function though. */</font> - ACE_Sig_Action sa ((ACE_SignalHandler) handler, SIGINT); - - <font color=red>/* Like ACE_ERROR_RETURN, the ACE_DEBUG macro gets used quite a bit. - It's a handy way to generate uniform debug output from your - program. */</font> - ACE_DEBUG ((LM_DEBUG, - "<font color=green>(%P|%t) starting up server daemon\n</font>")); - - <font color=red>/* This will loop "<font color=green>forever</font>" invoking the handle_events() method of - our reactor. handle_events() watches for activity on any - registered handlers and invokes their appropriate callbacks when - necessary. Callback-driven programming is a big thing in ACE, you - should get used to it. If the signal handler catches something, - the finished flag will be set and we'll exit. Conveniently - enough, handle_events() is also interrupted by signals and will - exit back to the while() loop. (If you want your event loop to - not be interrupted by signals, checkout the <i>restart</i> flag on - the open() method of ACE_Reactor if you're interested.) */</font> - while (!finished) - reactor.handle_events (); - - ACE_DEBUG ((LM_DEBUG, - "<font color=green>(%P|%t) shutting down server daemon\n</font>")); - - return 0; -} - -<font color=blue>#if !defined</font>(<font color=purple>ACE_HAS_GNU_REPO</font>) -<font color=blue>#if defined</font> (<font color=purple>ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION</font>) -template class ACE_Acceptor <Client_Handler, ACE_SOCK_ACCEPTOR>; -template class ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>; -<font color=blue>#elif defined</font> (<font color=purple>ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA</font>) -<font color=blue>#pragma</font> <font color=purple>instantiate</font> ACE_Acceptor <Client_Handler, ACE_SOCK_ACCEPTOR> -<font color=blue>#pragma</font> <font color=purple>instantiate</font> ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> -<font color=blue>#endif</font> <font color=red>/* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */</font> -<font color=blue>#endif</font> <font color=red>/* ACE_HAS_GNU_REPO */</font> -</PRE> -<HR WIDTH="100%"> - -<P>Let's move along and see what happend to the Client_Acceptor. - -<P> -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page03.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/006/page03.html b/docs/tutorials/006/page03.html deleted file mode 100644 index 1c202720321..00000000000 --- a/docs/tutorials/006/page03.html +++ /dev/null @@ -1,112 +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>In <A HREF="client_acceptor.h">client_acceptor.h</A>, we've extended -our object just a bit. The primary reason is to allow us to select -the previous single-threaded implementation or our new thread-per-connection -implementation. Client_Acceptor itself doesn't use this information -but makes it available to the Client_Handler objects it creates. -If we wanted a single-strategy implementation, we would have made no changes -to the Tutorial 5 version of this file. - -<P> -<HR WIDTH="100%"> -<PRE> -<font color=red>// $Id$</font> - -<font color=blue>#ifndef</font> <font color=purple>CLIENT_ACCEPTOR_H</font> -<font color=blue>#define</font> <font color=purple>CLIENT_ACCEPTOR_H</font> - -<font color=red>/* The ACE_Acceptor<> template lives in the ace/Acceptor.h header - file. You'll find a very consistent naming convention between the - ACE objects and the headers where they can be found. In general, - the ACE object ACE_Foobar will be found in ace/Foobar.h. */</font> - -<font color=blue>#include</font> "<A HREF="../../../ace/Acceptor.h">ace/Acceptor.h</A>" - -<font color=blue>#if !defined</font> (<font color=purple>ACE_LACKS_PRAGMA_ONCE</font>) -<font color=blue># pragma</font> <font color=purple>once</font> -<font color=blue>#endif</font> <font color=red>/* ACE_LACKS_PRAGMA_ONCE */</font> - -<font color=red>/* Since we want to work with sockets, we'll need a SOCK_Acceptor to - allow the clients to connect to us. */</font> -<font color=blue>#include</font> "<A HREF="../../../ace/SOCK_Acceptor.h">ace/SOCK_Acceptor.h</A>" - -<font color=red>/* The Client_Handler object we develop will be used to handle clients - once they're connected. The ACE_Acceptor<> template's first - parameter requires such an object. In some cases, you can get by - with just a forward declaration on the class, in others you have to - have the whole thing. */</font> -<font color=blue>#include</font> "<font color=green>client_handler.h</font>" - -<font color=red>/* Parameterize the ACE_Acceptor<> such that it will listen for socket - connection attempts and create Client_Handler objects when they - happen. In Tutorial 001, we wrote the basic acceptor logic on our - own before we realized that ACE_Acceptor<> was available. You'll - get spoiled using the ACE templates because they take away a lot of - the tedious details! */</font> -typedef ACE_Acceptor <Client_Handler, ACE_SOCK_ACCEPTOR> Client_Acceptor_Base; - -<font color=red>/* Here, we use the parameterized ACE_Acceptor<> as a baseclass for - our customized Client_Acceptor object. I've done this so that we - can provide it with our choice of concurrency strategies when the - object is created. Each Client_Handler it creates will use this - information to determine how to act. If we were going to create a - system that was always thread-per-connection, we would not have - bothered to extend Client_Acceptor. */</font> -class Client_Acceptor : public Client_Acceptor_Base -{ -public: - <font color=red>/* - This is always a good idea. If nothing else, it makes your code more - orthogonal no matter what baseclasses your objects have. - */</font> - typedef Client_Acceptor_Base inherited; - - <font color=red>/* - Construct the object with the concurrency strategy. Since this tutorial - is focused on thread-per-connection, we make that the default. We could - have chosen to omitt the default and populate it in main() instead. - */</font> - Client_Acceptor (int thread_per_connection = 1) - : thread_per_connection_ (thread_per_connection) - { - } - - <font color=red>/* Return the value of our strategy flag. This is used by the - Client_Handler to decide how to act. If 'true' then the handler - will behave in a thread-per-connection manner. */</font> - int thread_per_connection (void) - { - return this->thread_per_connection_; - } - -protected: - int thread_per_connection_; -}; - -<font color=blue>#endif</font> <font color=red>/* CLIENT_ACCEPTOR_H */</font> -</PRE> -<HR WIDTH="100%"> - -<P>Ok, so far we haven't done much to change our concurrency strategy. -Let's move on to the Client_Handler and see if it has changed any. - -<P> -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page04.html">Continue This Tutorial</A>]</CENTER> diff --git a/docs/tutorials/006/page04.html b/docs/tutorials/006/page04.html deleted file mode 100644 index a43304af2e4..00000000000 --- a/docs/tutorials/006/page04.html +++ /dev/null @@ -1,153 +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.h">client_handler.h</A> -shows a few more changes than the previous sources. The important -change is the addition of a svc() method where our connection thread will -exist. - -<P> -<HR WIDTH="100%"> -<PRE> -<font color=red>// $Id$</font> - -<font color=blue>#ifndef</font> <font color=purple>CLIENT_HANDLER_H</font> -<font color=blue>#define</font> <font color=purple>CLIENT_HANDLER_H</font> - -<font color=red>/* Our client handler must exist somewhere in the ACE_Event_Handler - object hierarchy. This is a requirement of the ACE_Reactor because - it maintains ACE_Event_Handler pointers for each registered event - handler. You could derive our Client_Handler directly from - ACE_Event_Handler but you still have to have an ACE_SOCK_Stream for - the actually connection. With a direct derivative of - ACE_Event_Handler, you'll have to contain and maintain an - ACE_SOCK_Stream instance yourself. With ACE_Svc_Handler (which is - a derivative of ACE_Event_Handler) some of those details are - handled for you. - - */</font> - -<font color=blue>#include</font> "<A HREF="../../../ace/Svc_Handler.h">ace/Svc_Handler.h</A>" - -<font color=blue>#if !defined</font> (<font color=purple>ACE_LACKS_PRAGMA_ONCE</font>) -<font color=blue># pragma</font> <font color=purple>once</font> -<font color=blue>#endif</font> <font color=red>/* ACE_LACKS_PRAGMA_ONCE */</font> - -<font color=blue>#include</font> "<A HREF="../../../ace/SOCK_Stream.h">ace/SOCK_Stream.h</A>" - -<font color=red>/* Another feature of ACE_Svc_Handler is it's ability to present the - ACE_Task<> interface as well. That's what the ACE_NULL_SYNCH - parameter below is all about. If our Client_Acceptor has chosen - thread-per-connection then our open() method will activate us into - a thread. At that point, our svc() method will execute. We still - don't take advantage of the things ACE_NULL_SYNCH exists for but - stick around for Tutorial 7 and pay special attention to the - Thread_Pool object there for an explanation. */</font> -class Client_Handler : public ACE_Svc_Handler <ACE_SOCK_STREAM, ACE_NULL_SYNCH> -{ -public: - typedef ACE_Svc_Handler <ACE_SOCK_STREAM, ACE_NULL_SYNCH> inherited; - - <font color=red>// Constructor...</font> - Client_Handler (void); - - <font color=red>/* The destroy() method is our preferred method of destruction. We - could have overloaded the delete operator but that is neither easy - nor intuitive (at least to me). Instead, we provide a new method - of destruction and we make our destructor protected so that only - ourselves, our derivatives and our friends can delete us. It's a - nice compromise. */</font> - void destroy (void); - - <font color=red>/* Most ACE objects have an open() method. That's how you make them - ready to do work. ACE_Event_Handler has a virtual open() method - which allows us to create this overrride. ACE_Acceptor<> will - invoke this method after creating a new Client_Handler when a - client connects. Notice that the parameter to open() is a void*. - It just so happens that the pointer points to the acceptor which - created us. You would like for the parameter to be an - ACE_Acceptor<>* but since ACE_Event_Handler is generic, that would - tie it too closely to the ACE_Acceptor<> set of objects. In our - definition of open() you'll see how we get around that. */</font> - int open (void *acceptor); - - <font color=red>/* When an ACE_Task<> object falls out of the svc() method, the - framework will call the close() method. That's where we want to - cleanup ourselves if we're running in either thread-per-connection - or thread-pool mode. */</font> - int close (u_long flags = 0); - - <font color=red>/* When there is activity on a registered handler, the - handle_input() method of the handler will be invoked. If that - method returns an error code (eg -- -1) then the reactor will - invoke handle_close() to allow the object to clean itself - up. Since an event handler can be registered for more than one - type of callback, the callback mask is provided to inform - handle_close() exactly which method failed. That way, you don't - have to maintain state information between your handle_* method - calls. The <handle> parameter is explained below... As a - side-effect, the reactor will also invoke remove_handler() for the - object on the mask that caused the -1 return. This means that we - don't have to do that ourselves! */</font> - virtual int handle_close (ACE_HANDLE handle = ACE_INVALID_HANDLE, - ACE_Reactor_Mask mask = <font color=#008888>ACE_Event_Handler::ALL_EVENTS_MASK</font>); - -protected: - - <font color=red>/* If the Client_Acceptor which created us has chosen a - thread-per-connection strategy then our open() method will - activate us into a dedicate thread. The svc() method will then - execute in that thread performing some of the functions we used to - leave up to the reactor. */</font> - int svc (void); - - <font color=red>/* When we register with the reactor, we're going to tell it that we - want to be notified of READ events. When the reactor sees that - there is read activity for us, our handle_input() will be - invoked. The _handleg provided is the handle (file descriptor in - Unix) of the actual connection causing the activity. Since we're - derived from ACE_Svc_Handler<> and it maintains it's own peer - (ACE_SOCK_Stream) object, this is redundant for us. However, if - we had been derived directly from ACE_Event_Handler, we may have - chosen not to contain the peer. In that case, the <handle> would - be important to us for reading the client's data. */</font> - int handle_input (ACE_HANDLE handle); - - <font color=red>/* This has nothing at all to do with ACE. I've added this here as - a worker function which I will call from handle_input(). As - promised in Tutorial 5 I will use this now to make it easier to - switch between our two possible concurrency strategies. */</font> - int process (char *rdbuf, int rdbuf_len); - - <font color=red>/* We don't really do anything in our destructor but we've declared - it to be protected to prevent casual deletion of this object. As - I said above, I really would prefer that everyone goes through the - destroy() method to get rid of us. */</font> - ~Client_Handler (void); -}; - -<font color=blue>#endif</font> <font color=red>/* CLIENT_HANDLER_H */</font> -</PRE> -<HR WIDTH="100%"> - -<P>So... we've added a svc() method and alluded to changes in open(). -Let's move on to the object definition and see what all the fuss is about. - -<P> -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page05.html">Continue This Tutorial</A>]</CENTER> 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. 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<> 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[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! 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> diff --git a/docs/tutorials/006/page06.html b/docs/tutorials/006/page06.html deleted file mode 100644 index 522318b436c..00000000000 --- a/docs/tutorials/006/page06.html +++ /dev/null @@ -1,46 +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>That's it for Tutorial 6. With very little effort we've managed -to extend the previous single-threaded server to an implementation which -allows runtime selection of single or multi-threaded operation. In -Tutorial 7 we'll extend that again to allow a thread-pool choice in addition -to the current two. - -<P>For reference, here's the file list again: -<UL> -<LI> -<A HREF="Makefile">Makefile</A></LI> - -<LI> -<A HREF="client_acceptor.h">client_acceptor.h</A></LI> - -<LI> -<A HREF="client_handler.cpp">client_handler.cpp</A></LI> - -<LI> -<A HREF="client_handler.h">client_handler.h</A></LI> - -<LI> -<A HREF="server.cpp">server.cpp</A></LI> - -<LI> -<A HREF="fix.Makefile">fix.Makefile</A></LI> -</UL> -<P><HR WIDTH="100%"> -<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] </CENTER> diff --git a/docs/tutorials/006/server.cpp b/docs/tutorials/006/server.cpp deleted file mode 100644 index fd3a18d9d28..00000000000 --- a/docs/tutorials/006/server.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// $Id$ - -/* We try to keep main() very simple. One of the ways we do that is - to push much of the complicated stuff into worker objects. In this - case, we only need to include the acceptor header in our main - source file. We let it worry about the "real work". */ - -#include "client_acceptor.h" - -/* As before, we create a simple signal handler that will set our - finished flag. There are, of course, more elegant ways to handle - program shutdown requests but that isn't really our focus right - now, so we'll just do the easiest thing. */ - -static sig_atomic_t finished = 0; -extern "C" void handler (int) -{ - finished = 1; -} - -/* A server has to listen for clients at a known TCP/IP port. The - default ACE port is 10002 (at least on my system) and that's good - enough for what we want to do here. Obviously, a more robust - application would take a command line parameter or read from a - configuration file or do some other clever thing. Just like the - signal handler above, though, that's what we want to focus on, so - we're taking the easy way out. */ - -static const u_short PORT = ACE_DEFAULT_SERVER_PORT; - -/* Finally, we get to main. Some C++ compilers will complain loudly - if your function signature doesn't match the prototype. Even - though we're not going to use the parameters, we still have to - specify them. */ - -int -main (int argc, char *argv[]) -{ - /* In our earlier servers, we used a global pointer to get to the - reactor. I've never really liked that idea, so I've moved it into - main() this time. When we get to the Client_Handler object you'll - see how we manage to get a pointer back to this reactor. */ - ACE_Reactor reactor; - - /* The acceptor will take care of letting clients connect to us. It - will also arrange for a Client_Handler to be created for each new - client. Since we're only going to listen at one TCP/IP port, we - only need one acceptor. If we wanted, though, we could create - several of these and listen at several ports. (That's what we - would do if we wanted to rewrite inetd for instance.) */ - Client_Acceptor peer_acceptor; - - /* Create an ACE_INET_Addr that represents our endpoint of a - connection. We then open our acceptor object with that Addr. - Doing so tells the acceptor where to listen for connections. - Servers generally listen at "well known" addresses. If not, there - must be some mechanism by which the client is informed of the - server's address. - - Note how ACE_ERROR_RETURN is used if we fail to open the acceptor. - This technique is used over and over again in our tutorials. */ - if (peer_acceptor.open (ACE_INET_Addr (PORT), - &reactor) == -1) - ACE_ERROR_RETURN ((LM_ERROR, - "%p\n", - "open"), - -1); - - /* As with Tutorial 5, we know that we're now registered with our - reactor so we don't have to mess with that step. */ - - /* Install our signal handler. You can actually register signal - handlers with the reactor. You might do that when the signal - handler is responsible for performing "real" work. Our simple - flag-setter doesn't justify deriving from ACE_Event_Handler and - providing a callback function though. */ - ACE_Sig_Action sa ((ACE_SignalHandler) handler, SIGINT); - - /* Like ACE_ERROR_RETURN, the ACE_DEBUG macro gets used quite a bit. - It's a handy way to generate uniform debug output from your - program. */ - ACE_DEBUG ((LM_DEBUG, - "(%P|%t) starting up server daemon\n")); - - /* This will loop "forever" invoking the handle_events() method of - our reactor. handle_events() watches for activity on any - registered handlers and invokes their appropriate callbacks when - necessary. Callback-driven programming is a big thing in ACE, you - should get used to it. If the signal handler catches something, - the finished flag will be set and we'll exit. Conveniently - enough, handle_events() is also interrupted by signals and will - exit back to the while() loop. (If you want your event loop to - not be interrupted by signals, checkout the <i>restart</i> flag on - the open() method of ACE_Reactor if you're interested.) */ - while (!finished) - reactor.handle_events (); - - ACE_DEBUG ((LM_DEBUG, - "(%P|%t) shutting down server daemon\n")); - - return 0; -} - -#if !defined(ACE_HAS_GNU_REPO) -#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) -template class ACE_Acceptor <Client_Handler, ACE_SOCK_ACCEPTOR>; -template class ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>; -#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) -#pragma instantiate ACE_Acceptor <Client_Handler, ACE_SOCK_ACCEPTOR> -#pragma instantiate ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> -#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ -#endif /* ACE_HAS_GNU_REPO */ |