summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjcej <jcej@ae88bc3d-4319-0410-8dbf-d08b4c9d3795>1998-10-19 22:32:23 +0000
committerjcej <jcej@ae88bc3d-4319-0410-8dbf-d08b4c9d3795>1998-10-19 22:32:23 +0000
commitfbacb099c89a0c51a37d632322e1f8952937eda8 (patch)
treecb5aeb09b9a776f177f167d20dbe4319abaaa1e4
parent319b82eb48224f27cb81f52dd88610acd595c93e (diff)
downloadATCD-fbacb099c89a0c51a37d632322e1f8952937eda8.tar.gz
*** empty log message ***
-rw-r--r--ChangeLog-98b7
-rw-r--r--docs/tutorials/015/Handler.cpp135
-rw-r--r--docs/tutorials/015/Handler.h28
-rw-r--r--docs/tutorials/015/Makefile5
-rw-r--r--docs/tutorials/015/Protocol_Stream.cpp47
-rw-r--r--docs/tutorials/015/Protocol_Stream.h8
-rw-r--r--docs/tutorials/015/Protocol_Task.cpp55
-rw-r--r--docs/tutorials/015/Protocol_Task.h39
-rw-r--r--docs/tutorials/015/Recv.cpp20
-rw-r--r--docs/tutorials/015/Recv.h27
-rw-r--r--docs/tutorials/015/Server.cpp33
-rw-r--r--docs/tutorials/015/Server.h19
-rw-r--r--docs/tutorials/015/Xmit.cpp56
-rw-r--r--docs/tutorials/015/Xmit.h19
-rw-r--r--docs/tutorials/015/combine.shar539
-rw-r--r--docs/tutorials/015/page01.html7
-rw-r--r--docs/tutorials/015/page05.html11
-rw-r--r--docs/tutorials/015/page06.html29
-rw-r--r--docs/tutorials/015/page07.html96
-rw-r--r--docs/tutorials/015/page08.html89
-rw-r--r--docs/tutorials/015/page09.html225
-rw-r--r--docs/tutorials/015/page10.html121
-rw-r--r--docs/tutorials/015/page11.html195
-rw-r--r--docs/tutorials/015/page12.html94
-rw-r--r--docs/tutorials/015/page13.html170
-rw-r--r--docs/tutorials/015/page14.html83
-rw-r--r--docs/tutorials/015/page15.html115
-rw-r--r--docs/tutorials/015/page16.html85
-rw-r--r--docs/tutorials/015/page17.html106
-rw-r--r--docs/tutorials/015/server.cpp9
-rw-r--r--docs/tutorials/015/stream.gifbin0 -> 4079 bytes
-rwxr-xr-xdocs/tutorials/combine87
32 files changed, 2368 insertions, 191 deletions
diff --git a/ChangeLog-98b b/ChangeLog-98b
index fc82ffe00ac..8f50aa021bd 100644
--- a/ChangeLog-98b
+++ b/ChangeLog-98b
@@ -1,3 +1,10 @@
+Mon Oct 19 17:00:52 EDT 1998 James CE Johnson <jcej@chiroptera.tragus.org>
+
+ * docs/tutorials/015:
+ Just about everything in this directory has been touched. I'm
+ in the middle of documenting it and need a checkpoint so nothing
+ gets lost! ETA for completion is later tonight or sometime tomorrow.
+
Mon Oct 19 11:47:20 1998 David L. Levine <levine@cs.wustl.edu>
* ace/OS.cpp (readPPCTimeBase): wrapped each line in "".
diff --git a/docs/tutorials/015/Handler.cpp b/docs/tutorials/015/Handler.cpp
index 81e1a85a8e7..ab141c7e8c2 100644
--- a/docs/tutorials/015/Handler.cpp
+++ b/docs/tutorials/015/Handler.cpp
@@ -4,48 +4,33 @@
#include "Handler.h"
#include "Protocol_Task.h"
+/* The Protocol_Stream gives us the option to insert a Protocol_Task
+ to process data received by the stream. We'll get into the details
+ more when we talk about the stream in detail. For now it's enough
+ to know that Handler_Task::recv() will be invoked by the stream
+ after data from the client has been received and processed (eg --
+ decrypted, uncompressed, and whatever else the protocol requires.)
+*/
class Handler_Task : public Protocol_Task
{
public:
+ // Typical...
typedef Protocol_Task inherited;
-
+
+ // Simple...
Handler_Task(void);
~Handler_Task(void);
protected:
-
+
+ // recv() is invoked after received data has been fully
+ // processed by the protocol rules. Data processing typically
+ // done in handle_input() can then be done here.
int recv(ACE_Message_Block * message,
ACE_Time_Value *timeout = 0);
};
-Handler_Task::Handler_Task(void)
- : inherited(0)
-{
- ;
-}
-
-Handler_Task::~Handler_Task(void)
-{
- ;
-}
-
-int Handler_Task::recv(ACE_Message_Block * message,
- ACE_Time_Value *timeout )
-{
- ACE_DEBUG ((LM_INFO, "(%P|%t) Handler_Task::handle_input() got (%s)\n", message->rd_ptr() ));
-
- ACE_Message_Block * response = new ACE_Message_Block( 128 );
-
- ACE_OS::sprintf( response->wr_ptr(), "You Said: (%s)", message->rd_ptr() );
- response->wr_ptr( strlen(response->wr_ptr())+1 );
-
- message->release();
-
- // Turn the message around and send it back down the Stream.
- return this->reply( response, timeout );
-}
-
Handler::Handler(void)
{
;
@@ -56,21 +41,53 @@ Handler::~Handler(void)
;
}
+/* The Acceptor will open() us once the peer() connection is
+ established. There are a couple of things we have to do here
+ before we're ready to receive data from the client.
+*/
int Handler::open (void *)
{
ACE_INET_Addr addr;
+ // Make sure that we can get the peer's address. If we can't
+ // then there may be a network error or something else that
+ // will prevent communicating with the client. This is
+ // something you'll want to do in every event handler you create.
if (this->peer ().get_remote_addr (addr) == -1)
ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t) can't get remote addr\n"), -1);
+ // Announce the client
+ ACE_DEBUG ((LM_DEBUG, "(%P|%t) connected with %s\n", addr.get_host_name() ));
+
+ // Here's the first new twist to the old event handler.
+ // Before we can use the Protocol_Stream to communicate with
+ // the peer, we must open() it. We provide the stream with
+ // the peer() so that it will have a valid socket on which to
+ // read client requests and send our responses. We also
+ // provide a Handler_Task instance that will ultimately be
+ // responsible for processing any client data we receive.
+ int rval = stream().open( this->peer(), new Handler_Task() );
+
+ // Of course, we have to account for the chance that the
+ // stream's open() may fail.
+ if( rval == -1 )
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t) can't open the protocol stream.\n"), -1);
+ }
+
+ // Now that we know the client is valid and that the stream is
+ // ready for business we can register with the gloabl reactor
+ // instance. Here again is an opportunity for improvement if
+ // we expect to have mulitple Server object instances.
if (ACE_Reactor::instance()->register_handler (this, ACE_Event_Handler::READ_MASK) == -1)
ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t) can't register with reactor\n"), -1);
- ACE_DEBUG ((LM_DEBUG, "(%P|%t) connected with %s\n", addr.get_host_name() ));
-
- return stream().open( this->peer(), new Handler_Task() );
+ return rval;
}
+/* This is a fairly typical destroy() method that can be shared by
+ both close() and handle_close().
+*/
void Handler::destroy (void)
{
ACE_Reactor::instance()->remove_handler(this,ACE_Event_Handler::READ_MASK|ACE_Event_Handler::DONT_CALL);
@@ -80,6 +97,10 @@ void Handler::destroy (void)
delete this;
}
+/* In this simple application we just forward the close() and
+ handle_close() requests right on to the destroy() method.
+*/
+
int Handler::close (u_long)
{
this->destroy ();
@@ -92,6 +113,10 @@ int Handler::handle_close(ACE_HANDLE, ACE_Reactor_Mask _mask)
return 0;
}
+/* Unlike a "traditional" handle_input() ours is very simple. Because
+ of the use of the protocol stream, we delegate the read function to
+ the stream's get() and rely on our Handler_Task to do the real work.
+*/
int Handler::handle_input (ACE_HANDLE)
{
ACE_DEBUG ((LM_DEBUG, "(%P|%t) Activity from client\n" ));
@@ -106,3 +131,49 @@ int Handler::handle_input (ACE_HANDLE)
return 0;
}
+/* A Protocol_Task is derived from ACE_Task and has the option of
+ running in one or more threads. I've chosen here to construct the
+ baseclass with no threads but it should work just fine with one or
+ more if you need. Unless you're sharing the Handler_Task with
+ several peers, however, you're probably just wasting a thread to
+ activate it. On the other hand, if your reactor is running in a
+ single thread (as in this example) then you can easily implement
+ thread-per-connectin concurrency by giving the baseclass one thread.
+*/
+Handler_Task::Handler_Task(void)
+ : inherited(0)
+{
+ ;
+}
+
+Handler_Task::~Handler_Task(void)
+{
+ ;
+}
+
+/* When installed into the protocol stream, the Handler_Task's recv()
+ method will be called when data is ready for processing.
+ */
+int Handler_Task::recv(ACE_Message_Block * message,
+ ACE_Time_Value *timeout )
+{
+ // Announce the request we got from the client
+ ACE_DEBUG ((LM_INFO, "(%P|%t) Handler_Task::recv() got (%s)\n", message->rd_ptr() ));
+
+ // Create a response message to send to the client
+ ACE_Message_Block * response = new ACE_Message_Block( 128 );
+
+ // Nothing very original about this I'm afraid...
+ ACE_OS::sprintf( response->wr_ptr(), "You Said: (%s)", message->rd_ptr() );
+ response->wr_ptr( strlen(response->wr_ptr())+1 );
+
+ // Release the original message block now that we're through
+ // "processing" it.
+ message->release();
+
+ // Turn the message around and send it back down the Stream.
+ // In other words, we invoke the put() method on the
+ // Protocol_Stream without having to have a direct reference
+ // to the stream object.
+ return this->reply( response, timeout );
+}
diff --git a/docs/tutorials/015/Handler.h b/docs/tutorials/015/Handler.h
index 82c4f7855e1..7e3123f7cf2 100644
--- a/docs/tutorials/015/Handler.h
+++ b/docs/tutorials/015/Handler.h
@@ -8,24 +8,48 @@
#include "ace/SOCK_Stream.h"
#include "Protocol_Stream.h"
+/* Just your basic event handler. We use ACE_Svc_Handler<> as a
+ baseclass so that it can maintain the peer() and other details for
+ us. We're not going to activate() this object, so we can get away
+ with the NULL synch choice.
+*/
class Handler : public ACE_Svc_Handler < ACE_SOCK_STREAM, ACE_NULL_SYNCH >
{
-
public:
Handler(void);
~Handler(void);
-
+
+ // Called by the acceptor when we're created in response to a
+ // client connection.
int open (void *);
+
+ // Called when it's time for us to be deleted. We take care
+ // of removing ourselves from the reactor and shutting down
+ // the peer() connectin.
void destroy (void);
+
+ // Called when it's time for us to go away. There are subtle
+ // differences between destroy() and close() so don't try to
+ // use either for all cases.
int close (u_long);
protected:
+ // Respond to peer() activity.
int handle_input (ACE_HANDLE);
+
+ // This will be called when handle_input() returns a failure
+ // code. That's our signal that it's time to begin the
+ // shutdown process.
int handle_close(ACE_HANDLE, ACE_Reactor_Mask _mask);
private:
+
+ // Like the Client, we have to abide by the protocol
+ // requirements. We use a local Protocol_Stream object to
+ // take care of those details. For us, I/O then just becomes
+ // a matter of interacting with the stream.
Protocol_Stream stream_;
Protocol_Stream & stream(void)
diff --git a/docs/tutorials/015/Makefile b/docs/tutorials/015/Makefile
index d701c5eed6c..d2de9a3b0bf 100644
--- a/docs/tutorials/015/Makefile
+++ b/docs/tutorials/015/Makefile
@@ -20,12 +20,11 @@ Depend : #
HTML : #
[ -f hdr ] || $(MAKE) UNSHAR
perl ../combine *.pre
- rm -f *.bdy
SHAR : #
[ ! -f combine.shar ] || exit 1
- shar -T hdr links *.pre *.pst > combine.shar
- rm -f hdr links *.pre *.pst
+ shar -T hdr bodies *.pre *.pst > combine.shar
+ rm -f hdr bodies *.pre *.pst
UNSHAR : #
sh combine.shar
diff --git a/docs/tutorials/015/Protocol_Stream.cpp b/docs/tutorials/015/Protocol_Stream.cpp
index 044ea791e26..058ec300b2d 100644
--- a/docs/tutorials/015/Protocol_Stream.cpp
+++ b/docs/tutorials/015/Protocol_Stream.cpp
@@ -10,12 +10,21 @@
#include "Compressor.h"
#include "Crypt.h"
+#include "ace/Stream_Modules.h"
+
+/* You can choose at compile time to include/exclude the protocol
+ pieces.
+*/
#define ENABLE_COMPRESSION
#define ENABLE_ENCRYPTION
+// The usual typedefs to make things easier to type.
typedef ACE_Module<ACE_MT_SYNCH> Module;
typedef ACE_Thru_Task<ACE_MT_SYNCH> Thru_Task;
+/* Do-nothing constructor and destructor
+ */
+
Protocol_Stream::Protocol_Stream( void )
{
;
@@ -26,16 +35,24 @@ Protocol_Stream::~Protocol_Stream( void )
;
}
+/* Even opening the stream is rather simple. The important thing to
+ rememer is that the modules you push onto the stream first will be
+ at the tail (eg -- most downstream) end of things when you're
+ done.
+ */
int Protocol_Stream::open( ACE_SOCK_Stream & _peer, Protocol_Task * _reader )
{
// Initialize our peer() to read/write the socket we're given
peer_.set_handle( _peer.get_handle() );
// Construct (and remember) the Recv object so that we can
- // read from the peer()
+ // read from the peer().
recv_ = new Recv( peer() );
- // Add the transmit and receive tasks to the head of the stream
+ // Add the transmit and receive tasks to the head of the
+ // stream. As we add more modules these will get pushed
+ // downstream and end up nearest the tail by the time we're
+ // done.
if( stream().push( new Module( "Xmit/Recv", new Xmit( peer() ), recv_ ) ) == -1 )
{
ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "stream().push( xmit/recv )"), -1);
@@ -54,6 +71,8 @@ int Protocol_Stream::open( ACE_SOCK_Stream & _peer, Protocol_Task * _reader )
// from the peer() will be sent through here last. Server
// applications will typically use this task to do the actual
// processing of data.
+ // Note the use of Thru_Task. Since a module must always have
+ // a pair of tasks we use this on the writter side as a no-op.
if( _reader )
{
if( stream().push( new Module( "Reader", new Thru_Task(), _reader ) ) == -1 )
@@ -65,6 +84,9 @@ int Protocol_Stream::open( ACE_SOCK_Stream & _peer, Protocol_Task * _reader )
return(0);
}
+/* Add the necessary protocol objects to the stream. The way we're
+ pushing things on we will encrypt the data before compressing it.
+*/
int Protocol_Stream::open(void)
{
#if defined(ENABLE_COMPRESSION)
@@ -83,6 +105,7 @@ int Protocol_Stream::open(void)
return( 0 );
}
+// Closing the Protocol_Stream is as simple as closing the ACE_Stream.
int Protocol_Stream::close(void)
{
return stream().close();
@@ -94,21 +117,32 @@ int Protocol_Stream::put(ACE_Message_Block * & _message, ACE_Time_Value * _timeo
return stream().put(_message,_timeout);
}
-/* Tell the Recv module to read some data (eg -- get() from the peer
- and pass it upstream.
+/* Tell the Recv module to read some data from the peer and pass it
+ upstream. Servers will typically use this method in a
+ handle_input() method to tell the stream to get a client's request.
*/
int Protocol_Stream::get(void)
{
+ // If there is no Recv module, we're in big trouble!
if( ! recv_ )
{
ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t) No Recv object!\n"), -1);
}
-
+
+ // This tells the Recv module to go to it's peer() and read
+ // some data. Once read, that data will be pushed upstream.
+ // If there is a reader object then it will have a chance to
+ // process the data. If not, the received data will be
+ // available in the message queue of the stream head's reader
+ // object (eg -- stream().head()->reader()->msg_queue()) and
+ // can be read with our other get() method below.
if( recv_->get() == -1 )
{
ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t) Cannot queue read request\n"), -1);
}
+ // For flexibility I've added an error() method to tell us if
+ // something bad has happened to the Recv object.
if( recv_->error() )
{
ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t) Recv object error!\n"), -1);
@@ -119,6 +153,9 @@ int Protocol_Stream::get(void)
/* Take a message block off of the stream head reader's message
queue. If the queue is empty, use get() to read from the peer.
+ This is most often used by client applications. Servers will
+ generaly insert a reader that will prevent the data from getting
+ all the way upstream to the head.
*/
int Protocol_Stream::get(ACE_Message_Block * & _response, ACE_Time_Value * _timeout )
{
diff --git a/docs/tutorials/015/Protocol_Stream.h b/docs/tutorials/015/Protocol_Stream.h
index 57b31ec27c5..384e7240e4f 100644
--- a/docs/tutorials/015/Protocol_Stream.h
+++ b/docs/tutorials/015/Protocol_Stream.h
@@ -7,12 +7,17 @@
#include "ace/SOCK_Stream.h"
#include "ace/Stream.h"
+// Shorthand for the stream.
typedef ACE_Stream<ACE_MT_SYNCH> Stream;
+// Forward references to cut down on the number of #includes
class ACE_Message_Block;
class Recv;
class Protocol_Task;
+/* The Protocol_Stream provides a tidy interface to an ACE_Stream
+ setup to process a data block through a series of protocol stages.
+*/
class Protocol_Stream
{
public:
@@ -54,7 +59,10 @@ public:
}
private:
+ // Our peer connection
ACE_SOCK_Stream peer_;
+
+ // The stream managing the various protocol tasks
Stream stream_;
// A task which is capable of receiving data on a socket.
diff --git a/docs/tutorials/015/Protocol_Task.cpp b/docs/tutorials/015/Protocol_Task.cpp
index e82569ec62d..3cfe7495539 100644
--- a/docs/tutorials/015/Protocol_Task.cpp
+++ b/docs/tutorials/015/Protocol_Task.cpp
@@ -3,6 +3,7 @@
#include "Protocol_Task.h"
+// Construct the object and remember the thread count.
Protocol_Task::Protocol_Task( int _thr_count )
: desired_thr_count_(_thr_count)
{
@@ -12,6 +13,7 @@ Protocol_Task::~Protocol_Task(void)
{
}
+// Activate the object if necessary.
int Protocol_Task::open(void *arg)
{
ACE_UNUSED_ARG(arg);
@@ -24,6 +26,11 @@ int Protocol_Task::open(void *arg)
return(0);
}
+/* When we're being closed by the ACE_Stream and we've got threads to
+ worry about then we drop a hangup message onto the message queue so
+ that svc() will go away. Except for the call to is_active(), this
+ is lifted directly from Tutorial 14.
+*/
int Protocol_Task::close(u_long flags)
{
if (flags == 1 && is_active() )
@@ -44,6 +51,10 @@ int Protocol_Task::close(u_long flags)
return 0;
}
+/* The put() method has to make a decision. If we've got threads then
+ put the unit of work onto the message queue for svc() to deal
+ with. If not then process() it directly.
+*/
int Protocol_Task::put(ACE_Message_Block *message,ACE_Time_Value *timeout)
{
if( is_active() )
@@ -54,43 +65,58 @@ int Protocol_Task::put(ACE_Message_Block *message,ACE_Time_Value *timeout)
return this->process(message,timeout);
}
+/* svc() is about what you would expect. This is again lifted
+ directly from Tutorial 14 but with a call to process() for handling
+ the logic instead of doing the work right here.
+ */
int Protocol_Task::svc(void)
{
-
ACE_Message_Block * message;
- while (1) {
-
+ while (1)
+ {
+ // Get a message
if ( this->getq(message, 0) == -1) {
ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "Protocol_Task::svc() getq"), -1);
}
ACE_DEBUG ((LM_DEBUG, "(%P|%t) Protocol_Task::svc() got message\n"));
-
+
+ // Check for hangup
if (message->msg_type() == ACE_Message_Block::MB_HANGUP) {
ACE_DEBUG ((LM_DEBUG, "(%P|%t) Protocol_Task::svc() -- HANGUP block received\n"));
+ // Hangup our thread-pool peers (if any)
if (this->putq(message->duplicate()) == -1) {
ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "Protocol_Task::svc() putq"), -1);
}
-
+
+ // Leave svc()
break;
}
+ // Do some work on the data.
if( this->process(message->duplicate(),0) == -1 )
{
break;
}
-
+
+ // Give up the message block before we go get another.
message->release();
}
+ // Give up the message block that caused us to exit the
+ // while(1) loop.
message->release();
return(0);
}
+/* There's nothing really magic about process(). We just decide if
+ we're moving data upstream or downstream and invoke the appropriate
+ virtual function to handle it.
+*/
int Protocol_Task::process(ACE_Message_Block * message, ACE_Time_Value *timeout)
{
if( this->is_writer() )
@@ -101,3 +127,20 @@ int Protocol_Task::process(ACE_Message_Block * message, ACE_Time_Value *timeout)
return this->recv(message,timeout);
}
+/* We must insist that derivatives provide a meaningful overload for
+ these methods. It's fairly common for ACE object methods to return
+ an error when an overload is expected but the method cannot be
+ safely made pure virtual.
+ */
+
+int Protocol_Task::send(ACE_Message_Block *message,
+ ACE_Time_Value *timeout)
+{
+ return -1;
+}
+
+int Protocol_Task::recv(ACE_Message_Block * message,
+ ACE_Time_Value *timeout)
+{
+ return -1;
+}
diff --git a/docs/tutorials/015/Protocol_Task.h b/docs/tutorials/015/Protocol_Task.h
index 66025fca1fe..eeec1d71588 100644
--- a/docs/tutorials/015/Protocol_Task.h
+++ b/docs/tutorials/015/Protocol_Task.h
@@ -6,43 +6,62 @@
#include "ace/Task.h"
+/* A typical ACE_Task<> derivative that adds a few things appropriate
+ to protocol stacks.
+*/
class Protocol_Task : public ACE_Task<ACE_MT_SYNCH>
{
public:
typedef ACE_Task<ACE_MT_SYNCH> inherited;
-
+
+ // A choice of concurrency strategies is offered by the
+ // constructor. In most cases it makes sense to set this to
+ // zero and let things proceed serially. You might have a
+ // need, however, for some of your tasks to have their own thread.
Protocol_Task( int _thr_count );
+
~Protocol_Task(void);
+ // open() is invoked when the task is inserted into the stream.
virtual int open(void *arg);
+
+ // close() is invoked when the stream is closed (flags will be
+ // set to '1') and when the svc() method exits (flags will be
+ // '0').
virtual int close(u_long flags);
+ // As data travels through the stream, the put() method of
+ // each task is invoked to keep the data moving along.
virtual int put(ACE_Message_Block *message,
ACE_Time_Value *timeout);
+ // If you choose to activate the task then this method will be
+ // doing all of the work.
virtual int svc(void);
protected:
+ // Called by put() or svc() as necessary to process a block of
+ // data.
int process(ACE_Message_Block * message, ACE_Time_Value *timeout);
-
+
+ // Just let us know if we're active or not.
int is_active(void)
{
return this->thr_count() != 0;
}
+ // Tasks on the writter (downstream) side of the stream
+ // are called upon to send() data that will ultimately go to
+ // the peer.
virtual int send(ACE_Message_Block *message,
- ACE_Time_Value *timeout)
- {
- return -1;
- }
+ ACE_Time_Value *timeout);
+ // Tasks on the reader (upstream) side will be receiving data
+ // that came from the peer.
virtual int recv(ACE_Message_Block * message,
- ACE_Time_Value *timeout)
- {
- return -1;
- }
+ ACE_Time_Value *timeout);
private:
int desired_thr_count_;
diff --git a/docs/tutorials/015/Recv.cpp b/docs/tutorials/015/Recv.cpp
index 7a3add97509..a8f284a76ef 100644
--- a/docs/tutorials/015/Recv.cpp
+++ b/docs/tutorials/015/Recv.cpp
@@ -4,17 +4,33 @@
#include "Recv.h"
#include "ace/SOCK_Stream.h"
+/* Construct the object with the peer reference and other appropriate
+ initializations.
+*/
Recv::Recv( ACE_SOCK_Stream & _peer )
: inherited(0), peer_(_peer), error_(0)
{
+ // Create the tickler that get() will use to trigger recv()
+ // through the baseclass. Since we're single-threaded this is
+ // probably overkill but it makes multi-threading easier if we
+ // choose to do that.
tickler_ = new ACE_Message_Block(1);
}
+/* Be sure we manage the lifetime of the tickler to prevent a memory
+ leak.
+*/
Recv::~Recv(void)
{
tickler_->release();
}
+/* By putting the tickler to ourselves we cause things to happen in
+ the baseclass that will invoke recv(). If we know we're single
+ threaded we could directly call recv() and be done with it but then
+ we'd have to do something else if we're multi-threaded. Just let
+ the baseclass worry about those things!
+*/
int Recv::get(void)
{
return this->put( tickler_, 0 );
@@ -31,7 +47,9 @@ int Recv::recv(ACE_Message_Block * message, ACE_Time_Value *timeout)
int b = 0;
/* Read from the socket one byte at a time until we see then
- end-of-string NULL character.
+ end-of-string NULL character. Since the OS layers (at leas
+ in Unix) will provide some buffering this isn't as bad as
+ it may seem at first.
*/
do
{
diff --git a/docs/tutorials/015/Recv.h b/docs/tutorials/015/Recv.h
index c155cc83221..4514f816aa9 100644
--- a/docs/tutorials/015/Recv.h
+++ b/docs/tutorials/015/Recv.h
@@ -8,17 +8,26 @@
class ACE_SOCK_Stream;
+/* Get some data from the peer and send it upstream for
+ de-protocol-ization.
+*/
class Recv : public Protocol_Task
{
public:
typedef Protocol_Task inherited;
-
+
+ // Give it someone to talk to...
Recv( ACE_SOCK_Stream & _peer );
+
~Recv(void);
+ // Trigger a read from the socket
int get(void);
-
+
+ // In some cases it might be easier to check the "state" of the
+ // Recv object than to rely on return codes filtering back to
+ // you.
int error(void)
{
return this->error_;
@@ -31,12 +40,22 @@ protected:
return this->peer_;
}
- int recv(ACE_Message_Block * message,
- ACE_Time_Value *timeout = 0);
+ // The baseclass will trigger this when our get() method is
+ // called. A message block of the appropriate size is created,
+ // filled and passed up the stream.
+ int recv(ACE_Message_Block * message,
+ ACE_Time_Value *timeout = 0);
private:
+ // Our endpoint
ACE_SOCK_Stream & peer_;
+
+ // get() uses a bogus message block to cause the baseclass to
+ // invoke recv(). To avoid memory thrashing, we create that
+ // bogus message once and reuse it for the life of Recv.
ACE_Message_Block * tickler_;
+
+ // Our error flag (duh)
int error_;
};
diff --git a/docs/tutorials/015/Server.cpp b/docs/tutorials/015/Server.cpp
index 347f5c5ca3f..8809d92dabe 100644
--- a/docs/tutorials/015/Server.cpp
+++ b/docs/tutorials/015/Server.cpp
@@ -3,10 +3,14 @@
#include "Server.h"
-static const u_short PORT = ACE_DEFAULT_SERVER_PORT;
-
+/* We have to allocate space for our static finished_ flag. We also
+ initialize it to 'false' so that we don't exit immediately.
+*/
sig_atomic_t Server::finished_ = 0;
+/* The simple constructor and destructor don't do anything but give us
+ a place to expand in the future if we want.
+*/
Server::Server(void)
{
;
@@ -17,21 +21,39 @@ Server::~Server(void)
;
}
+/* Opening the server is as simple as opening the acceptor with the
+ default ACE_Reactor instance. If we want to allow multiple
+ instances of Server objects then we should have an ACE_Reactor
+ member variable that we can register with.
+*/
int Server::open(void)
{
- if (acceptor_.open (ACE_INET_Addr (PORT), ACE_Reactor::instance()) == -1)
+ if (acceptor_.open (ACE_INET_Addr (ACE_DEFAULT_SERVER_PORT), ACE_Reactor::instance()) == -1)
ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "open"), -1);
return(0);
}
+/* Running the server just means that we execute the basic event
+ loop for the reactor. Again, if we had a private reactor then we
+ could have multiple server's in their run() method.
+*/
int Server::run(void)
{
ACE_DEBUG ((LM_DEBUG, "(%P|%t) starting up server daemon\n"));
+ ACE_Time_Value timeout(2);
+
+ // Here's the basic event loop. I have a 2-second timeout on
+ // the handle_events() so that we don't have to wait too long
+ // when we set the finished_ flag.
while (!finished_)
- ACE_Reactor::instance()->handle_events ();
+ {
+ ACE_Reactor::instance()->handle_events (&timeout);
+ }
+ // Close the acceptor when we're done. This may be handled by
+ // the framework but it's good practice to be explicit about things.
acceptor_.close();
ACE_DEBUG ((LM_DEBUG, "(%P|%t) shutting down server daemon\n"));
@@ -39,6 +61,9 @@ int Server::run(void)
return 0;
}
+/* The close() method simply sets the finished_ flag so that run()
+ will leave the event loop and exit.
+*/
int Server::close(void)
{
finished_ = 1;
diff --git a/docs/tutorials/015/Server.h b/docs/tutorials/015/Server.h
index c2e75ff71dc..0e6b9f9c9cf 100644
--- a/docs/tutorials/015/Server.h
+++ b/docs/tutorials/015/Server.h
@@ -8,22 +8,39 @@
#include "ace/SOCK_Acceptor.h"
#include "Handler.h"
+/* Anytime I have templates I try to remember to create a typedef for
+ the parameterized object. It makes for much less typing later!
+*/
typedef ACE_Acceptor < Handler, ACE_SOCK_ACCEPTOR > Acceptor;
-
class Server
{
public:
+ // Our simple constructor takes no parameters. To make the
+ // server a bit more useful, you may want to pass in the
+ // TCP/IP port to be used by the acceptor.
Server(void);
~Server(void);
+ // Open the server for business
int open(void);
+
+ // Close all server instances by setting the finished_ flag.
+ // Actually, the way this class is written, you can only have
+ // one instance.
static int close(void);
+ // Run the server's main loop. The use of the gloabl
+ // ACE_Reactor by this method is what limits us to one Server
+ // instance.
int run(void);
private:
+ // This will accept client connection requests and instantiate
+ // a Handler object for each new connection.
Acceptor acceptor_;
+
+ // Our shutdown flag
static sig_atomic_t finished_;
};
diff --git a/docs/tutorials/015/Xmit.cpp b/docs/tutorials/015/Xmit.cpp
index 2cb0bfa1eee..fb80ab8aa34 100644
--- a/docs/tutorials/015/Xmit.cpp
+++ b/docs/tutorials/015/Xmit.cpp
@@ -4,6 +4,13 @@
#include "Xmit.h"
#include "ace/SOCK_Stream.h"
+/* Construct the object with the peer connection and choose not to
+ activate ourselves into a dedicated thread. You might get some
+ performance gain by activating but if you really want a
+ multi-threaded apprroach you should handle that as a separate
+ issue. Attempting to force threading at this level will likely
+ cause more trouble than you want to deal with.
+*/
Xmit::Xmit( ACE_SOCK_Stream & _peer )
: inherited(0), peer_(_peer)
{
@@ -13,36 +20,49 @@ Xmit::~Xmit(void)
{
}
+/* Check to see if we're being closed by the stream (flags != 0) or if
+ we're responding to the exit of our svc() method.
+*/
int Xmit::close(u_long flags)
{
+ // Take care of the baseclass closure.
+ int rval = inherited::close(flags);
+
+ // Only if we're being called at the stream shutdown do we close
+ // the peer connection. If, for some reason, we were activated
+ // into one or more threads we wouldn't want to close the pipe
+ // before all threads had a chance to flush their data.
if( flags )
{
- return( inherited::close(flags) );
+ peer().close();
}
- return( peer().close() );
+ return( rval );
}
+/* Our overload of send() will take care of sending the data to the
+ peer.
+*/
int Xmit::send(ACE_Message_Block *message, ACE_Time_Value *timeout)
{
int rval;
ACE_DEBUG ((LM_INFO, "(%P|%t) Xmit::send() sending (%s)(%d)\n", message->rd_ptr(), message->length() ));
- /* Since we're going to be sending data that may have been
- compressed and encrypted it's probably important for the
- receiver to get an entire "block" instead of having a
- partial read.
+ /* Since we're going to be sending data that may have been
+ compressed and encrypted it's probably important for the
+ receiver to get an entire "block" instead of having a
+ partial read.
- For that reason, we'll send the length of the message block
- (in clear-text) to the peer so that it can then recv_n()
- the entire block contents in one read operation.
- */
+ For that reason, we'll send the length of the message block
+ (in clear-text) to the peer so that it can then recv_n()
+ the entire block contents in one read operation.
+ */
char msize[32];
sprintf(msize,"%d",message->length());
- // Be sure we send the end-of-string NULL so that Recv will
- // know when to stop assembling the length.
+ // Be sure we send the end-of-string NULL so that Recv will
+ // know when to stop assembling the length.
rval = this->peer().send_n( msize, strlen(msize)+1, 0, timeout );
if( rval == -1 )
@@ -50,14 +70,14 @@ int Xmit::send(ACE_Message_Block *message, ACE_Time_Value *timeout)
ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "Xmit::send() Failed to send message size."), -1);
}
- /* Now we send the actual data. If you're worried about
- network efficiency then you may choose to create one buffer
- containing msize and the message data and send it all at
- once.
- */
+ /* Now we send the actual data. If you're worried about
+ network efficiency then you may choose to create one buffer
+ containing msize and the message data and send it all at
+ once.
+ */
rval = this->peer().send_n( message->rd_ptr(), message->length(), 0, timeout );
- // Release the message block since we're done with it.
+ // Release the message block since we're done with it.
message->release();
return( rval );
diff --git a/docs/tutorials/015/Xmit.h b/docs/tutorials/015/Xmit.h
index 0a658a0d032..7d8df91e0ef 100644
--- a/docs/tutorials/015/Xmit.h
+++ b/docs/tutorials/015/Xmit.h
@@ -6,17 +6,27 @@
#include "Protocol_Task.h"
+// Forward reference reduces #include dependencies
class ACE_SOCK_Stream;
+/* A class suitable for sending data to a peer from within an
+ ACE_Stream.
+ */
class Xmit : public Protocol_Task
{
public:
typedef Protocol_Task inherited;
-
+
+ // We must be given a valid peer when constructed. Without that
+ // we don't know who to send data to.
Xmit( ACE_SOCK_Stream & _peer );
+
~Xmit(void);
+ // As you know, close() will be called in a couple of ways by the
+ // ACE framework. We use that opportunity to terminate the
+ // connection to the peer.
int close(u_long flags);
protected:
@@ -26,10 +36,13 @@ protected:
return this->peer_;
}
- int send(ACE_Message_Block *message,
- ACE_Time_Value *timeout);
+ // Send the data to the peer. By now it will have been
+ // completely protocol-ized by other tasks in the stream.
+ int send(ACE_Message_Block *message,
+ ACE_Time_Value *timeout);
private:
+ // A representation of the peer we're talking to.
ACE_SOCK_Stream & peer_;
};
diff --git a/docs/tutorials/015/combine.shar b/docs/tutorials/015/combine.shar
index b22aef439e9..564843d6251 100644
--- a/docs/tutorials/015/combine.shar
+++ b/docs/tutorials/015/combine.shar
@@ -3,23 +3,35 @@
# 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 1998-10-18 17:18 EDT by <jcej@chiroptera.tragus.org>.
-# Source directory was `/home/jcej/tmp/ACE_wrappers/docs/tutorials/015'.
+# Made on 1998-10-19 16:39 EDT by <jcej@caldera.lads.com>.
+# Source directory was `/scsiA/home/jcej/projects/ACE_wrappers/docs/tutorials/015'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
-# 414 -rw-r--r-- hdr
-# 758 -rw-r--r-- links
-# 931 -rw-rw-r-- page01.pre
+# 414 -rw-rw-r-- hdr
+# 411 -rw-rw-r-- bodies
+# 1183 -rw-rw-r-- page01.pre
# 194 -rw-rw-r-- page02.pre
# 318 -rw-rw-r-- page03.pre
# 178 -rw-rw-r-- page04.pre
-# 216 -rw-rw-r-- page05.pre
-# 5 -rw-rw-r-- page06.pre
+# 304 -rw-rw-r-- page05.pre
+# 418 -rw-rw-r-- page06.pre
+# 321 -rw-rw-r-- page07.pre
+# 501 -rw-rw-r-- page08.pre
+# 640 -rw-rw-r-- page09.pre
+# 978 -rw-rw-r-- page10.pre
+# 334 -rw-rw-r-- page11.pre
+# 334 -rw-rw-r-- page12.pre
+# 326 -rw-rw-r-- page13.pre
+# 770 -rw-rw-r-- page14.pre
+# 660 -rw-rw-r-- page15.pre
+# 213 -rw-rw-r-- page16.pre
+# 129 -rw-rw-r-- page17.pre
# 348 -rw-rw-r-- page04.pst
+# 609 -rw-rw-r-- page09.pst
#
save_IFS="${IFS}"
IFS="${IFS}:"
@@ -66,7 +78,7 @@ else
fi
rm -f 1231235999 $$.touch
#
-if mkdir _sh22464; then
+if mkdir _sh23762; then
$echo 'x -' 'creating lock directory'
else
$echo 'failed to create lock directory'
@@ -93,8 +105,8 @@ X
<P>
<HR WIDTH="100%">
SHAR_EOF
- $shar_touch -am 1018170998 'hdr' &&
- chmod 0644 'hdr' ||
+ $shar_touch -am 1019163798 '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
@@ -108,60 +120,62 @@ SHAR_EOF
$echo 'hdr:' 'original size' '414,' 'current size' "$shar_count!"
fi
fi
-# ============= links ==============
-if test -f 'links' && test "$first_param" != -c; then
- $echo 'x -' SKIPPING 'links' '(file already exists)'
+# ============= bodies ==============
+if test -f 'bodies' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'bodies' '(file already exists)'
else
- $echo 'x -' extracting 'links' '(text)'
- sed 's/^X//' << 'SHAR_EOF' > 'links' &&
+ $echo 'x -' extracting 'bodies' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'bodies' &&
+#
# The client application specific files
#
-client.cpp page02.bdy P
-Client.h page03.bdy P
-Client.cpp page04.bdy P
+PAGE=2
+client.cpp
+Client.h
+Client.cpp
#
# The server application specific files
#
-server.cpp page05.bdy P
-Server.h page06.bdy P
-Server.cpp page07.bdy P
-Handler.h page08.bdy P
-Handler.cpp page09.bdy P
+server.cpp
+Server.h
+Server.cpp
+Handler.h
+Handler.cpp
#
# The basic protocol stream
#
-Protocol_Stream.cpp page10.bdy P
-Protocol_Stream.h page11.bdy P
-Protocol_Task.h page12.bdy P
-Protocol_Task.cpp page13.bdy P
+Protocol_Stream.h
+Protocol_Stream.cpp
+Protocol_Task.h
+Protocol_Task.cpp
#
# Send/Receive objects
#
-XXmit.h page14.bdy P
-XXmit.cpp page15.bdy P
-Recv.h page16.bdy P
-Recv.cpp page17.bdy P
+XXmit.h
+XXmit.cpp
+Recv.h
+Recv.cpp
#
# Protocol objects
#
-Compressor.h page18.bdy P
-Compressor.cpp page19.bdy P
-Crypt.h page20.bdy P
-Crypt.cpp page21.bdy P
-SHAR_EOF
- $shar_touch -am 1018170998 'links' &&
- chmod 0644 'links' ||
- $echo 'restore of' 'links' 'failed'
+Compressor.h
+Compressor.cpp
+Crypt.h
+Crypt.cpp
+SHAR_EOF
+ $shar_touch -am 1019163798 '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 'links:' 'MD5 check failed'
-aba8616d83b1f9d83ec955796f0fb36b links
+ || $echo 'bodies:' 'MD5 check failed'
+e12e3d0a32a2930d5518e407a0a6b6bb bodies
SHAR_EOF
else
- shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'links'`"
- test 758 -eq "$shar_count" ||
- $echo 'links:' 'original size' '758,' 'current size' "$shar_count!"
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'bodies'`"
+ test 411 -eq "$shar_count" ||
+ $echo 'bodies:' 'original size' '411,' 'current size' "$shar_count!"
fi
fi
# ============= page01.pre ==============
@@ -185,25 +199,30 @@ X compression, applying checksums or any number of other actions.
<P>
In this tutorial a Protocol_Stream object is created to encrypt and
X compress* data being sent between peers. Both client and server
-X applications are presented as well.
+X applications are presented as well. I present the application
+level code first and then go into the details of the protocol stream
+and it's helper objects. If the stream stuff in the application logic
+is confusing then just read on by and come back to it after the later
+discussions.
+X
<P>
<font size=-1>* Ok, I didn't really implement encryption and
X compression objects. I'll leave that as a thought
X exercise!</font>
SHAR_EOF
- $shar_touch -am 1018170998 'page01.pre' &&
+ $shar_touch -am 1019163798 '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'
-44ab699ec8b707039dccecbb42cb8403 page01.pre
+2a7a0fad3e88fcaa6b70cedbf873192f page01.pre
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page01.pre'`"
- test 931 -eq "$shar_count" ||
- $echo 'page01.pre:' 'original size' '931,' 'current size' "$shar_count!"
+ test 1183 -eq "$shar_count" ||
+ $echo 'page01.pre:' 'original size' '1183,' 'current size' "$shar_count!"
fi
fi
# ============= page02.pre ==============
@@ -218,7 +237,7 @@ X delegate the tricky stuff to another object.
X
<HR>
SHAR_EOF
- $shar_touch -am 1018170998 'page02.pre' &&
+ $shar_touch -am 1019163798 'page02.pre' &&
chmod 0664 'page02.pre' ||
$echo 'restore of' 'page02.pre' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
@@ -246,7 +265,7 @@ X Note the Protocol_Stream member that will take care of
X converting and sending/receiving the data.
<HR>
SHAR_EOF
- $shar_touch -am 1018170998 'page03.pre' &&
+ $shar_touch -am 1019163798 'page03.pre' &&
chmod 0664 'page03.pre' ||
$echo 'restore of' 'page03.pre' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
@@ -272,7 +291,7 @@ X really does any work. The other methods simply delegate their
X function to the Protocol_Stream.
<HR>
SHAR_EOF
- $shar_touch -am 1018170998 'page04.pre' &&
+ $shar_touch -am 1019163798 'page04.pre' &&
chmod 0664 'page04.pre' ||
$echo 'restore of' 'page04.pre' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
@@ -296,22 +315,24 @@ else
Like the client, we want to keep the main() part of our server code as
X simple as possible. This is done by putting most of the work
X into the Handler object that will deal with client connections.
+XFrom the looks of the code below, I think we've been successful in our
+simplification.
<HR>
X
SHAR_EOF
- $shar_touch -am 1018170998 'page05.pre' &&
+ $shar_touch -am 1019163798 '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'
-1e5b69f5006598935d3b003ded00fbb2 page05.pre
+8833b0d7f90a4d13f68b8cdb9147c29a page05.pre
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page05.pre'`"
- test 216 -eq "$shar_count" ||
- $echo 'page05.pre:' 'original size' '216,' 'current size' "$shar_count!"
+ test 304 -eq "$shar_count" ||
+ $echo 'page05.pre:' 'original size' '304,' 'current size' "$shar_count!"
fi
fi
# ============= page06.pre ==============
@@ -320,21 +341,376 @@ if test -f 'page06.pre' && test "$first_param" != -c; then
else
$echo 'x -' extracting 'page06.pre' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'page06.pre' &&
+The Server object exists in order simplify the
+main() application level. To that end, it hides the details of
+creating an acceptor and managing the reactor.
+<P>
+The static close() method available for a signal handler as you saw on
+the previous page. Of course the assumption here is that there would
+only be one Server instance but since you can't provide a TCP/IP port,
+that's probably a valid assumption!
<HR>
SHAR_EOF
- $shar_touch -am 1018170998 'page06.pre' &&
+ $shar_touch -am 1019163798 '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'
-a9e92ad99b49fbdc87e172d8434a24c5 page06.pre
+06a0abefdf4e704b42147f433bd27e4d page06.pre
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page06.pre'`"
- test 5 -eq "$shar_count" ||
- $echo 'page06.pre:' 'original size' '5,' 'current size' "$shar_count!"
+ test 418 -eq "$shar_count" ||
+ $echo 'page06.pre:' 'original size' '418,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page07.pre ==============
+if test -f 'page07.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page07.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page07.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page07.pre' &&
+And now the implementation of Server. This is actually just the
+main() code from a previous tutorial broken into appropriate method
+calls. It may seem silly to do this rather than keeping the stuff in
+main() but you'll find that you have less trouble enhancing an
+application when you take this sort of approach.
+<HR>
+X
+SHAR_EOF
+ $shar_touch -am 1019163798 'page07.pre' &&
+ chmod 0664 'page07.pre' ||
+ $echo 'restore of' 'page07.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 'page07.pre:' 'MD5 check failed'
+7dfb75884939c3c05ee1e1000956e9f4 page07.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page07.pre'`"
+ test 321 -eq "$shar_count" ||
+ $echo 'page07.pre:' 'original size' '321,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page08.pre ==============
+if test -f 'page08.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page08.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page08.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page08.pre' &&
+The Handler object is our event handler. You can use either
+ACE_Event_Handler or ACE_Svc_Handler<> for the baseclass. I generally
+prefer the latter since it takes care of some housekeeping that I
+would otherwise be responsible for.
+<P>
+The class declaration is taken almost exactly from a previous
+tutorial. A good design will have a simple handler object that will
+collect data from the peer and pass it along to another object for
+processing. Again, keep it simple and delegate authority.
+<HR>
+SHAR_EOF
+ $shar_touch -am 1019163798 'page08.pre' &&
+ chmod 0664 'page08.pre' ||
+ $echo 'restore of' 'page08.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 'page08.pre:' 'MD5 check failed'
+471889eb736a025fc46719549bca160a page08.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page08.pre'`"
+ test 501 -eq "$shar_count" ||
+ $echo 'page08.pre:' 'original size' '501,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page09.pre ==============
+if test -f 'page09.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page09.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page09.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page09.pre' &&
+Like any other event handler, the handle_input() method will be
+responsible for getting data from the peer() and doing something with
+it. In this case, we have a Protocol_Stream to deal with. We'll use
+the stream for the actual I/O but we are ultimately responsible for
+processing the data from the peer. To do that, we've created a
+Handler_Task that fits within the Protocol_Stream framework to process
+data that has been received. Handler::handle_input() will tell the stream that
+it's time to read data and that data will eventually show up at
+Handler_Task::recv() where we'll process it as required by our
+application logic.
+<HR>
+SHAR_EOF
+ $shar_touch -am 1019163798 'page09.pre' &&
+ chmod 0664 'page09.pre' ||
+ $echo 'restore of' 'page09.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 'page09.pre:' 'MD5 check failed'
+da5c4b11d356dc9caf3a8e3b6b2527a6 page09.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page09.pre'`"
+ test 640 -eq "$shar_count" ||
+ $echo 'page09.pre:' 'original size' '640,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page10.pre ==============
+if test -f 'page10.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page10.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page10.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page10.pre' &&
+And so finally we come to the Protocol_Stream. That, after all, is
+the focus of the entire tutorial but it took us half of the day to get
+here!
+<P>
+The Protocol_Stream uses an ACE_Stream to move an ACE_Message_Block
+through a series of tasks. Each task in the stream is responsible for
+performing some operation on the data in the message block. That is
+the nature of a protocol stream (or "stack" if you prefer). In this
+stream, the data is compressed and encrypted* on its way between
+peers. We also allow users of the stream to install a reader task to
+handle data that percolates up from the peer. As you saw a page or
+two ago, this is most useful for a server.
+X
+<P>
+<font size=-1>*Again, I just pretend to do these things. It would
+take another day or two to go through any sort of reasonable
+encryption or compression!</font>
+<P>
+Before we get into the code, here's a picture that's shows what's
+going on here.
+<P><center><img src="stream.gif"></center></p>
+<HR>
+SHAR_EOF
+ $shar_touch -am 1019163798 'page10.pre' &&
+ chmod 0664 'page10.pre' ||
+ $echo 'restore of' 'page10.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 'page10.pre:' 'MD5 check failed'
+a74423390fb5217104c6d89d7f202e8b page10.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page10.pre'`"
+ test 978 -eq "$shar_count" ||
+ $echo 'page10.pre:' 'original size' '978,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page11.pre ==============
+if test -f 'page11.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page11.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page11.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page11.pre' &&
+And now the implementation of the Protocol_Stream. There are more
+lines of code here than we've seen so far but it still isn't
+complicated. The basic idea is to construct the ACE_Stream with our
+set of protocol objects that will manipulate the data. Our primary
+concern in this file is to get everything in the correct order!
+<HR>
+SHAR_EOF
+ $shar_touch -am 1019163798 'page11.pre' &&
+ chmod 0664 'page11.pre' ||
+ $echo 'restore of' 'page11.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 'page11.pre:' 'MD5 check failed'
+b0e968102fb417b12710e99465f4e387 page11.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page11.pre'`"
+ test 334 -eq "$shar_count" ||
+ $echo 'page11.pre:' 'original size' '334,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page12.pre ==============
+if test -f 'page12.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page12.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page12.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page12.pre' &&
+And now the implementation of the Protocol_Stream. There are more
+lines of code here than we've seen so far but it still isn't
+complicated. The basic idea is to construct the ACE_Stream with our
+set of protocol objects that will manipulate the data. Our primary
+concern in this file is to get everything in the correct order!
+<HR>
+SHAR_EOF
+ $shar_touch -am 1019163798 'page12.pre' &&
+ chmod 0664 'page12.pre' ||
+ $echo 'restore of' 'page12.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 'page12.pre:' 'MD5 check failed'
+b0e968102fb417b12710e99465f4e387 page12.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page12.pre'`"
+ test 334 -eq "$shar_count" ||
+ $echo 'page12.pre:' 'original size' '334,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page13.pre ==============
+if test -f 'page13.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page13.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page13.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page13.pre' &&
+The Protocol_Task implementation takes care of the open(), close(),
+put() and svc() methods so that derivatives can concentrate on the
+send() and recv() methods. After a while you find that most
+ACE_Task<> derivatives look very similar in the four basic methods and
+only need one or two additional to do any real work.
+<HR>
+SHAR_EOF
+ $shar_touch -am 1019163798 'page13.pre' &&
+ chmod 0664 'page13.pre' ||
+ $echo 'restore of' 'page13.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 'page13.pre:' 'MD5 check failed'
+8b74a6d79d158222928097a9bb1335db page13.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page13.pre'`"
+ test 326 -eq "$shar_count" ||
+ $echo 'page13.pre:' 'original size' '326,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page14.pre ==============
+if test -f 'page14.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page14.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page14.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page14.pre' &&
+The implementation of Xmit isn't too complicated. If we choose to
+combine it with the Recv task we simply lift the recv() method from
+that object and drop it into this one.
+<P>
+Note that close() must decide if it's being called when the stream is
+shutdown or when it's svc() method exits. Since we tell the baseclass
+not to use any threads it's a safe bet that flags will always be
+non-zero. Still, it's good practice to plan for the future by
+checking the value.
+<P>
+Note also that when we send the data we prefix it with the data size.
+This let's our sibling Recv ensure that an entire block is received
+together. This can be very important for compression and encryption
+processes which typically work better with blocks of data instead of
+streams of data.
+<HR>
+SHAR_EOF
+ $shar_touch -am 1019163798 'page14.pre' &&
+ chmod 0664 'page14.pre' ||
+ $echo 'restore of' 'page14.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 'page14.pre:' 'MD5 check failed'
+0d79137eaedd73b820037fcafe6e16b6 page14.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page14.pre'`"
+ test 770 -eq "$shar_count" ||
+ $echo 'page14.pre:' 'original size' '770,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page15.pre ==============
+if test -f 'page15.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page15.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page15.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page15.pre' &&
+Recv is the sibling to Xmit. Again, they could be combined into a
+single object if you want.
+<P>
+An ACE_Stream is designed to handle downstream traffic very
+well. You put() data into it and it flows along towards the tail.
+However, there doesn't seem to be a way to put data in such that it
+will travel upstream. To get around that, I've added a get() method
+to Recv that will trigger a read on the socket. Recv will then put
+the data to the next upstream module and we're on our way. As noted
+earlier, that data will eventually show up either in the <i>reader</i>
+(if installed on the stream open()) or the stream head reader task's
+message queue.
+<HR>
+SHAR_EOF
+ $shar_touch -am 1019163798 'page15.pre' &&
+ chmod 0664 'page15.pre' ||
+ $echo 'restore of' 'page15.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 'page15.pre:' 'MD5 check failed'
+2d89b3c894cfcfdfef47ae506913cdad page15.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page15.pre'`"
+ test 660 -eq "$shar_count" ||
+ $echo 'page15.pre:' 'original size' '660,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page16.pre ==============
+if test -f 'page16.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page16.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page16.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page16.pre' &&
+The Recv implementation is nearly as simple as Xmit. There's
+opportunity for error when we get the message size and we have to
+manage the lifetime of the tickler but other than that it's pretty
+basic stuff.
+<HR>
+SHAR_EOF
+ $shar_touch -am 1019163798 'page16.pre' &&
+ chmod 0664 'page16.pre' ||
+ $echo 'restore of' 'page16.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 'page16.pre:' 'MD5 check failed'
+7db337f2c6ec74d75560534dec550b0e page16.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page16.pre'`"
+ test 213 -eq "$shar_count" ||
+ $echo 'page16.pre:' 'original size' '213,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page17.pre ==============
+if test -f 'page17.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page17.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page17.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page17.pre' &&
+This and the next three pages present the protocol objects that
+provide compression and encryption. If you were hoping to
+<HR>
+SHAR_EOF
+ $shar_touch -am 1019163798 'page17.pre' &&
+ chmod 0664 'page17.pre' ||
+ $echo 'restore of' 'page17.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 'page17.pre:' 'MD5 check failed'
+d8553fb71b067ee360aca09883a6c775 page17.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page17.pre'`"
+ test 129 -eq "$shar_count" ||
+ $echo 'page17.pre:' 'original size' '129,' 'current size' "$shar_count!"
fi
fi
# ============= page04.pst ==============
@@ -357,7 +733,7 @@ For a quick look back:
<P>
Now we'll move on and examine the server counter-part of our client.
SHAR_EOF
- $shar_touch -am 1018170998 'page04.pst' &&
+ $shar_touch -am 1019163798 'page04.pst' &&
chmod 0664 'page04.pst' ||
$echo 'restore of' 'page04.pst' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
@@ -372,5 +748,44 @@ SHAR_EOF
$echo 'page04.pst:' 'original size' '348,' 'current size' "$shar_count!"
fi
fi
-rm -fr _sh22464
+# ============= page09.pst ==============
+if test -f 'page09.pst' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page09.pst' '(file already exists)'
+else
+ $echo 'x -' extracting 'page09.pst' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page09.pst' &&
+<HR>
+<P>
+That's it for the server-specific code. I think I've been fairly
+successful in keeping it simple and to the point. There are a couple
+of places where the as-yet-undescribed Protocol_Stream pops up and may
+cause confusion. We're going to discuss that mystery now but before
+we do here's the list of server files if you want to review:
+X
+<UL>
+<LI><A HREF="server.cpp">server.cpp</A>
+<LI><A HREF="Server.h">Server.h</A>
+<LI><A HREF="Server.cpp">Server.cpp</A>
+<LI><A HREF="Handler.h">Handler.h</A>
+<LI><A HREF="Handler.cpp">Handler.cpp</A>
+<LI><A HREF="Makefile.server">Server Makefile</A>
+</UL>
+<P>
+SHAR_EOF
+ $shar_touch -am 1019163798 'page09.pst' &&
+ chmod 0664 'page09.pst' ||
+ $echo 'restore of' 'page09.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 'page09.pst:' 'MD5 check failed'
+a391cfe4b4fcd0002c418310c5b254c9 page09.pst
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page09.pst'`"
+ test 609 -eq "$shar_count" ||
+ $echo 'page09.pst:' 'original size' '609,' 'current size' "$shar_count!"
+ fi
+fi
+rm -fr _sh23762
exit 0
diff --git a/docs/tutorials/015/page01.html b/docs/tutorials/015/page01.html
index 8eee4b6cae7..9f12daac318 100644
--- a/docs/tutorials/015/page01.html
+++ b/docs/tutorials/015/page01.html
@@ -27,7 +27,12 @@ In a more robust system, one might want to process the data before
<P>
In this tutorial a Protocol_Stream object is created to encrypt and
compress* data being sent between peers. Both client and server
- applications are presented as well.
+ applications are presented as well. I present the application
+level code first and then go into the details of the protocol stream
+and it's helper objects. If the stream stuff in the application logic
+is confusing then just read on by and come back to it after the later
+discussions.
+
<P>
<font size=-1>* Ok, I didn't really implement encryption and
compression objects. I'll leave that as a thought
diff --git a/docs/tutorials/015/page05.html b/docs/tutorials/015/page05.html
index d32561782e2..ceb76843974 100644
--- a/docs/tutorials/015/page05.html
+++ b/docs/tutorials/015/page05.html
@@ -15,6 +15,8 @@
Like the client, we want to keep the main() part of our server code as
simple as possible. This is done by putting most of the work
into the Handler object that will deal with client connections.
+From the looks of the code below, I think we've been successful in our
+simplification.
<HR>
<PRE>
@@ -23,6 +25,7 @@ Like the client, we want to keep the main() part of our server code as
<font color=blue>#include</font> "<font color=green>Server.h</font>"
+<font color=red>// A signal handler that will close the server object</font>
extern "<font color=green>C</font>" void handler (int)
{
<font color=#008888>Server::close</font>();
@@ -30,15 +33,20 @@ extern "<font color=green>C</font>" void handler (int)
int main (int, char **)
{
+ <font color=red>// The server object that abstracts away all of difficult parts.</font>
Server server;
+ <font color=red>// Attempt to open the server. Like all good ACE-based</font>
+ <font color=red>// objects, we'll get -1 on failure.</font>
if( server.open() == -1 )
{
ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>server.open()</font>"), -1);
}
+ <font color=red>// Install a signal handler for ^C so that we can exit gracefully</font>
ACE_Sig_Action sa ((ACE_SignalHandler) handler, SIGINT);
+ <font color=red>// Run the server's main loop until we're interrupted</font>
if( server.run() == -1 )
{
ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>server.run()</font>"), -1);
@@ -47,6 +55,9 @@ int main (int, char **)
return 0;
}
+<font color=red>/* These explicit instantiations were taken from an earlier tutorial.
+ Your compiler may require others as well.
+*/</font>
<font color=blue>#if defined</font> (<font color=purple>ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION</font>)
template class ACE_Acceptor &lt;Handler, ACE_SOCK_ACCEPTOR>;
template class ACE_Svc_Handler&lt;ACE_SOCK_STREAM, ACE_NULL_SYNCH>;
diff --git a/docs/tutorials/015/page06.html b/docs/tutorials/015/page06.html
index 1ae669507c0..d7fa3f994db 100644
--- a/docs/tutorials/015/page06.html
+++ b/docs/tutorials/015/page06.html
@@ -12,6 +12,14 @@
<P>
<HR WIDTH="100%">
+The Server object exists in order simplify the
+main() application level. To that end, it hides the details of
+creating an acceptor and managing the reactor.
+<P>
+The static close() method available for a signal handler as you saw on
+the previous page. Of course the assumption here is that there would
+only be one Server instance but since you can't provide a TCP/IP port,
+that's probably a valid assumption!
<HR>
<PRE>
@@ -24,26 +32,43 @@
<font color=blue>#include</font> "<font color=green>ace/SOCK_Acceptor.h</font>"
<font color=blue>#include</font> "<font color=green>Handler.h</font>"
+<font color=red>/* Anytime I have templates I try to remember to create a typedef for
+ the parameterized object. It makes for much less typing later!
+*/</font>
typedef ACE_Acceptor &lt; Handler, ACE_SOCK_ACCEPTOR > Acceptor;
-
class Server
{
public:
+ <font color=red>// Our simple constructor takes no parameters. To make the</font>
+ <font color=red>// server a bit more useful, you may want to pass in the</font>
+ <font color=red>// TCP/IP port to be used by the acceptor.</font>
Server(void);
~Server(void);
+ <font color=red>// Open the server for business</font>
int open(void);
+
+ <font color=red>// Close all server instances by setting the finished_ flag.</font>
+ <font color=red>// Actually, the way this class is written, you can only have</font>
+ <font color=red>// one instance.</font>
static int close(void);
+ <font color=red>// Run the server's main loop. The use of the gloabl</font>
+ <font color=red>// ACE_Reactor by this method is what limits us to one Server</font>
+ <font color=red>// instance.</font>
int run(void);
private:
+ <font color=red>// This will accept client connection requests and instantiate </font>
+ <font color=red>// a Handler object for each new connection.</font>
Acceptor acceptor_;
+
+ <font color=red>// Our shutdown flag</font>
static sig_atomic_t finished_;
};
<font color=blue>#endif</font> <font color=red>// SERVER_H</font>
</PRE>
<P><HR WIDTH="100%">
-<CENTER>[<A HREF="..">Tutorial Index</A>] </CENTER>
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page07.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page07.html b/docs/tutorials/015/page07.html
new file mode 100644
index 00000000000..676376acea8
--- /dev/null
+++ b/docs/tutorials/015/page07.html
@@ -0,0 +1,96 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 015</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 015</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Building a protocol stream</FONT></B></CENTER>
+
+<P>
+<HR WIDTH="100%">
+And now the implementation of Server. This is actually just the
+main() code from a previous tutorial broken into appropriate method
+calls. It may seem silly to do this rather than keeping the stuff in
+main() but you'll find that you have less trouble enhancing an
+application when you take this sort of approach.
+<HR>
+
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#include</font> "<font color=green>Server.h</font>"
+
+<font color=red>/* We have to allocate space for our static finished_ flag. We also
+ initialize it to 'false' so that we don't exit immediately.
+*/</font>
+sig_atomic_t <font color=#008888>Server::finished_</font> = 0;
+
+<font color=red>/* The simple constructor and destructor don't do anything but give us
+ a place to expand in the future if we want.
+*/</font>
+<font color=#008888>Server::Server</font>(void)
+{
+ ;
+}
+
+<font color=#008888>Server::~Server</font>(void)
+{
+ ;
+}
+
+<font color=red>/* Opening the server is as simple as opening the acceptor with the
+ default ACE_Reactor instance. If we want to allow multiple
+ instances of Server objects then we should have an ACE_Reactor
+ member variable that we can register with.
+*/</font>
+int <font color=#008888>Server::open</font>(void)
+{
+ if (acceptor_.open (ACE_INET_Addr (ACE_DEFAULT_SERVER_PORT), <font color=#008888>ACE_Reactor::instance</font>()) == -1)
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>open</font>"), -1);
+
+ return(0);
+}
+
+<font color=red>/* Running the server just means that we execute the basic event
+ loop for the reactor. Again, if we had a private reactor then we
+ could have multiple server's in their run() method.
+*/</font>
+int <font color=#008888>Server::run</font>(void)
+{
+ ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) starting up server daemon\n</font>"));
+
+ ACE_Time_Value timeout(2);
+
+ <font color=red>// Here's the basic event loop. I have a 2-second timeout on</font>
+ <font color=red>// the handle_events() so that we don't have to wait too long</font>
+ <font color=red>// when we set the finished_ flag.</font>
+ while (!finished_)
+ {
+ <font color=#008888>ACE_Reactor::instance</font>()->handle_events (&timeout);
+ }
+
+ <font color=red>// Close the acceptor when we're done. This may be handled by </font>
+ <font color=red>// the framework but it's good practice to be explicit about things.</font>
+ acceptor_.close();
+
+ ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) shutting down server daemon\n</font>"));
+
+ return 0;
+}
+
+<font color=red>/* The close() method simply sets the finished_ flag so that run()
+ will leave the event loop and exit.
+*/</font>
+int <font color=#008888>Server::close</font>(void)
+{
+ finished_ = 1;
+ return(0);
+}
+</PRE>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page08.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page08.html b/docs/tutorials/015/page08.html
new file mode 100644
index 00000000000..e63f1b7c6bd
--- /dev/null
+++ b/docs/tutorials/015/page08.html
@@ -0,0 +1,89 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 015</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 015</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Building a protocol stream</FONT></B></CENTER>
+
+<P>
+<HR WIDTH="100%">
+The Handler object is our event handler. You can use either
+ACE_Event_Handler or ACE_Svc_Handler<> for the baseclass. I generally
+prefer the latter since it takes care of some housekeeping that I
+would otherwise be responsible for.
+<P>
+The class declaration is taken almost exactly from a previous
+tutorial. A good design will have a simple handler object that will
+collect data from the peer and pass it along to another object for
+processing. Again, keep it simple and delegate authority.
+<HR>
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#ifndef</font> <font color=purple>HANDLER_H</font>
+<font color=blue>#define</font> <font color=purple>HANDLER_H</font>
+
+<font color=blue>#include</font> "<font color=green>ace/Svc_Handler.h</font>"
+<font color=blue>#include</font> "<font color=green>ace/SOCK_Stream.h</font>"
+<font color=blue>#include</font> "<font color=green>Protocol_Stream.h</font>"
+
+<font color=red>/* Just your basic event handler. We use ACE_Svc_Handler&lt;> as a
+ baseclass so that it can maintain the peer() and other details for
+ us. We're not going to activate() this object, so we can get away
+ with the NULL synch choice.
+*/</font>
+class Handler : public ACE_Svc_Handler &lt; ACE_SOCK_STREAM, ACE_NULL_SYNCH >
+{
+public:
+
+ Handler(void);
+ ~Handler(void);
+
+ <font color=red>// Called by the acceptor when we're created in response to a</font>
+ <font color=red>// client connection.</font>
+ int open (void *);
+
+ <font color=red>// Called when it's time for us to be deleted. We take care</font>
+ <font color=red>// of removing ourselves from the reactor and shutting down</font>
+ <font color=red>// the peer() connectin.</font>
+ void destroy (void);
+
+ <font color=red>// Called when it's time for us to go away. There are subtle</font>
+ <font color=red>// differences between destroy() and close() so don't try to</font>
+ <font color=red>// use either for all cases.</font>
+ int close (u_long);
+
+protected:
+
+ <font color=red>// Respond to peer() activity.</font>
+ int handle_input (ACE_HANDLE);
+
+ <font color=red>// This will be called when handle_input() returns a failure</font>
+ <font color=red>// code. That's our signal that it's time to begin the</font>
+ <font color=red>// shutdown process.</font>
+ int handle_close(ACE_HANDLE, ACE_Reactor_Mask _mask);
+
+private:
+
+ <font color=red>// Like the Client, we have to abide by the protocol</font>
+ <font color=red>// requirements. We use a local Protocol_Stream object to</font>
+ <font color=red>// take care of those details. For us, I/O then just becomes</font>
+ <font color=red>// a matter of interacting with the stream.</font>
+ Protocol_Stream stream_;
+
+ Protocol_Stream & stream(void)
+ {
+ return this->stream_;
+ }
+};
+
+<font color=blue>#endif</font> <font color=red>// HANDLER_H</font>
+</PRE>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page09.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page09.html b/docs/tutorials/015/page09.html
new file mode 100644
index 00000000000..6a842298010
--- /dev/null
+++ b/docs/tutorials/015/page09.html
@@ -0,0 +1,225 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 015</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 015</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Building a protocol stream</FONT></B></CENTER>
+
+<P>
+<HR WIDTH="100%">
+Like any other event handler, the handle_input() method will be
+responsible for getting data from the peer() and doing something with
+it. In this case, we have a Protocol_Stream to deal with. We'll use
+the stream for the actual I/O but we are ultimately responsible for
+processing the data from the peer. To do that, we've created a
+Handler_Task that fits within the Protocol_Stream framework to process
+data that has been received. Handler::handle_input() will tell the stream that
+it's time to read data and that data will eventually show up at
+Handler_Task::recv() where we'll process it as required by our
+application logic.
+<HR>
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#include</font> "<font color=green>Handler.h</font>"
+<font color=blue>#include</font> "<font color=green>Protocol_Task.h</font>"
+
+<font color=red>/* The Protocol_Stream gives us the option to insert a Protocol_Task
+ to process data received by the stream. We'll get into the details
+ more when we talk about the stream in detail. For now it's enough
+ to know that <font color=#008888>Handler_Task::recv</font>() will be invoked by the stream
+ after data from the client has been received and processed (eg --
+ decrypted, uncompressed, and whatever else the protocol requires.)
+*/</font>
+class Handler_Task : public Protocol_Task
+{
+public:
+
+ <font color=red>// Typical...</font>
+ typedef Protocol_Task inherited;
+
+ <font color=red>// Simple...</font>
+ Handler_Task(void);
+ ~Handler_Task(void);
+
+protected:
+
+ <font color=red>// recv() is invoked after received data has been fully</font>
+ <font color=red>// processed by the protocol rules. Data processing typically </font>
+ <font color=red>// done in handle_input() can then be done here.</font>
+ int recv(ACE_Message_Block * message,
+ ACE_Time_Value *timeout = 0);
+};
+
+<font color=#008888>Handler::Handler</font>(void)
+{
+ ;
+}
+
+<font color=#008888>Handler::~Handler</font>(void)
+{
+ ;
+}
+
+<font color=red>/* The Acceptor will open() us once the peer() connection is
+ established. There are a couple of things we have to do here
+ before we're ready to receive data from the client.
+*/</font>
+int <font color=#008888>Handler::open</font> (void *)
+{
+ ACE_INET_Addr addr;
+
+ <font color=red>// Make sure that we can get the peer's address. If we can't</font>
+ <font color=red>// then there may be a network error or something else that</font>
+ <font color=red>// will prevent communicating with the client. This is</font>
+ <font color=red>// something you'll want to do in every event handler you create.</font>
+ if (this->peer ().get_remote_addr (addr) == -1)
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>(%P|%t) can't get remote addr\n</font>"), -1);
+
+ <font color=red>// Announce the client</font>
+ ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) connected with %s\n</font>", addr.get_host_name() ));
+
+ <font color=red>// Here's the first new twist to the old event handler.</font>
+ <font color=red>// Before we can use the Protocol_Stream to communicate with</font>
+ <font color=red>// the peer, we must open() it. We provide the stream with</font>
+ <font color=red>// the peer() so that it will have a valid socket on which to</font>
+ <font color=red>// read client requests and send our responses. We also</font>
+ <font color=red>// provide a Handler_Task instance that will ultimately be</font>
+ <font color=red>// responsible for processing any client data we receive.</font>
+ int rval = stream().open( this->peer(), new Handler_Task() );
+
+ <font color=red>// Of course, we have to account for the chance that the</font>
+ <font color=red>// stream's open() may fail.</font>
+ if( rval == -1 )
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>(%P|%t) can't open the protocol stream.\n</font>"), -1);
+ }
+
+ <font color=red>// Now that we know the client is valid and that the stream is </font>
+ <font color=red>// ready for business we can register with the gloabl reactor</font>
+ <font color=red>// instance. Here again is an opportunity for improvement if</font>
+ <font color=red>// we expect to have mulitple Server object instances.</font>
+ if (<font color=#008888>ACE_Reactor::instance</font>()->register_handler (this, ACE_Event_Handler::READ_MASK) == -1)
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>(%P|%t) can't register with reactor\n</font>"), -1);
+
+ return rval;
+}
+
+<font color=red>/* This is a fairly typical destroy() method that can be shared by
+ both close() and handle_close().
+*/</font>
+void <font color=#008888>Handler::destroy</font> (void)
+{
+ <font color=#008888>ACE_Reactor::instance</font>()->remove_handler(this,ACE_Event_Handler::READ_MASK|ACE_Event_Handler::DONT_CALL);
+
+ this->peer ().close ();
+
+ delete this;
+}
+
+<font color=red>/* In this simple application we just forward the close() and
+ handle_close() requests right on to the destroy() method.
+*/</font>
+
+int <font color=#008888>Handler::close</font> (u_long)
+{
+ this->destroy ();
+ return 0;
+}
+
+int <font color=#008888>Handler::handle_close</font>(ACE_HANDLE, ACE_Reactor_Mask _mask)
+{
+ this->destroy();
+ return 0;
+}
+
+<font color=red>/* Unlike a "<font color=green>traditional</font>" handle_input() ours is very simple. Because
+ of the use of the protocol stream, we delegate the read function to
+ the stream's get() and rely on our Handler_Task to do the real work.
+*/</font>
+int <font color=#008888>Handler::handle_input</font> (ACE_HANDLE)
+{
+ ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) Activity from client\n</font>" ));
+
+ <font color=red>// This will cause a blocking read from the peer(). The data</font>
+ <font color=red>// will then be pushed through the protocol stream.</font>
+ if( stream().get( ) == -1 )
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>(%P|%t) can't get data from protocol stream\n</font>"), -1);
+ }
+
+ return 0;
+}
+
+<font color=red>/* A Protocol_Task is derived from ACE_Task and has the option of
+ running in one or more threads. I've chosen here to construct the
+ baseclass with no threads but it should work just fine with one or
+ more if you need. Unless you're sharing the Handler_Task with
+ several peers, however, you're probably just wasting a thread to
+ activate it. On the other hand, if your reactor is running in a
+ single thread (as in this example) then you can easily implement
+ thread-per-connectin concurrency by giving the baseclass one thread.
+*/</font>
+<font color=#008888>Handler_Task::Handler_Task</font>(void)
+ : inherited(0)
+{
+ ;
+}
+
+<font color=#008888>Handler_Task::~Handler_Task</font>(void)
+{
+ ;
+}
+
+<font color=red>/* When installed into the protocol stream, the Handler_Task's recv()
+ method will be called when data is ready for processing.
+ */</font>
+int <font color=#008888>Handler_Task::recv</font>(ACE_Message_Block * message,
+ ACE_Time_Value *timeout )
+{
+ <font color=red>// Announce the request we got from the client</font>
+ ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t) <font color=#008888>Handler_Task::recv</font>() got (%s)\n</font>", message->rd_ptr() ));
+
+ <font color=red>// Create a response message to send to the client</font>
+ ACE_Message_Block * response = new ACE_Message_Block( 128 );
+
+ <font color=red>// Nothing very original about this I'm afraid...</font>
+ <font color=#008888>ACE_OS::sprintf</font>( response->wr_ptr(), "<font color=green>You Said: (%s)</font>", message->rd_ptr() );
+ response->wr_ptr( strlen(response->wr_ptr())+1 );
+
+ <font color=red>// Release the original message block now that we're through</font>
+ <font color=red>// "<font color=green>processing</font>" it.</font>
+ message->release();
+
+ <font color=red>// Turn the message around and send it back down the Stream.</font>
+ <font color=red>// In other words, we invoke the put() method on the</font>
+ <font color=red>// Protocol_Stream without having to have a direct reference</font>
+ <font color=red>// to the stream object.</font>
+ return this->reply( response, timeout );
+}
+</PRE>
+<HR>
+<P>
+That's it for the server-specific code. I think I've been fairly
+successful in keeping it simple and to the point. There are a couple
+of places where the as-yet-undescribed Protocol_Stream pops up and may
+cause confusion. We're going to discuss that mystery now but before
+we do here's the list of server files if you want to review:
+
+<UL>
+<LI><A HREF="server.cpp">server.cpp</A>
+<LI><A HREF="Server.h">Server.h</A>
+<LI><A HREF="Server.cpp">Server.cpp</A>
+<LI><A HREF="Handler.h">Handler.h</A>
+<LI><A HREF="Handler.cpp">Handler.cpp</A>
+<LI><A HREF="Makefile.server">Server Makefile</A>
+</UL>
+<P>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page10.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page10.html b/docs/tutorials/015/page10.html
new file mode 100644
index 00000000000..abd33bcb829
--- /dev/null
+++ b/docs/tutorials/015/page10.html
@@ -0,0 +1,121 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 015</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 015</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Building a protocol stream</FONT></B></CENTER>
+
+<P>
+<HR WIDTH="100%">
+And so finally we come to the Protocol_Stream. That, after all, is
+the focus of the entire tutorial but it took us half of the day to get
+here!
+<P>
+The Protocol_Stream uses an ACE_Stream to move an ACE_Message_Block
+through a series of tasks. Each task in the stream is responsible for
+performing some operation on the data in the message block. That is
+the nature of a protocol stream (or "stack" if you prefer). In this
+stream, the data is compressed and encrypted* on its way between
+peers. We also allow users of the stream to install a reader task to
+handle data that percolates up from the peer. As you saw a page or
+two ago, this is most useful for a server.
+
+<P>
+<font size=-1>*Again, I just pretend to do these things. It would
+take another day or two to go through any sort of reasonable
+encryption or compression!</font>
+<P>
+Before we get into the code, here's a picture that's shows what's
+going on here.
+<P><center><img src="stream.gif"></center></p>
+<HR>
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#ifndef</font> <font color=purple>PROTOCOL_STREAM_H</font>
+<font color=blue>#define</font> <font color=purple>PROTOCOL_STREAM_H</font>
+
+<font color=blue>#include</font> "<font color=green>ace/SOCK_Stream.h</font>"
+<font color=blue>#include</font> "<font color=green>ace/Stream.h</font>"
+
+<font color=red>// Shorthand for the stream.</font>
+typedef ACE_Stream&lt;ACE_MT_SYNCH> Stream;
+
+<font color=red>// Forward references to cut down on the number of <font color=blue>#include</font>s</font>
+class ACE_Message_Block;
+class Recv;
+class Protocol_Task;
+
+<font color=red>/* The Protocol_Stream provides a tidy interface to an ACE_Stream
+ setup to process a data block through a series of protocol stages.
+*/</font>
+class Protocol_Stream
+{
+public:
+ Protocol_Stream(void);
+ ~Protocol_Stream(void);
+
+ <font color=red>// Provide the stream with an ACE_SOCK_Stream on which it can</font>
+ <font color=red>// communicate. If _reader is non-null, it will be added as</font>
+ <font color=red>// the reader task just below the stream head so that it can</font>
+ <font color=red>// process data read from the peer.</font>
+ int open( ACE_SOCK_Stream & _peer, Protocol_Task * _reader = 0 );
+
+ <font color=red>// Close the stream. All of the tasks & modules will also be closed.</font>
+ int close(void);
+
+ <font color=red>// putting data onto the stream will pass it through all</font>
+ <font color=red>// protocol levels and send it to the peer.</font>
+ int put( ACE_Message_Block * & _message, ACE_Time_Value *
+ _timeout = 0 );
+
+ <font color=red>// get will cause the Recv task (at the tail of the stream) to</font>
+ <font color=red>// read some data from the peer and pass it upstream. The</font>
+ <font color=red>// message block is then taken from the stream reader task's</font>
+ <font color=red>// message queue.</font>
+ int get( ACE_Message_Block * & _response, ACE_Time_Value *
+ _timeout = 0 );
+
+ <font color=red>// Tell the Recv task to read some data and send it upstream.</font>
+ <font color=red>// The data will pass through the protocol tasks and be queued </font>
+ <font color=red>// into the stream head reader task's message queue. If</font>
+ <font color=red>// you've installed a _reader in open() then that task's</font>
+ <font color=red>// recv() method will see the message and may consume it</font>
+ <font color=red>// instead of passing it to the stream head for queueing.</font>
+ int get(void);
+
+ ACE_SOCK_Stream & peer(void)
+ {
+ return this->peer_;
+ }
+
+private:
+ <font color=red>// Our peer connection</font>
+ ACE_SOCK_Stream peer_;
+
+ <font color=red>// The stream managing the various protocol tasks</font>
+ Stream stream_;
+
+ <font color=red>// A task which is capable of receiving data on a socket.</font>
+ <font color=red>// Note that this is only useful by client-side applications.</font>
+ Recv * recv_;
+
+ Stream & stream(void)
+ {
+ return this->stream_;
+ }
+
+ <font color=red>// Install the protocol tasks into the stream.</font>
+ int open(void);
+};
+
+<font color=blue>#endif</font> <font color=red>// PROTOCOL_STREAM_H</font>
+</PRE>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page11.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page11.html b/docs/tutorials/015/page11.html
new file mode 100644
index 00000000000..0ae929e8f7d
--- /dev/null
+++ b/docs/tutorials/015/page11.html
@@ -0,0 +1,195 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 015</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 015</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Building a protocol stream</FONT></B></CENTER>
+
+<P>
+<HR WIDTH="100%">
+And now the implementation of the Protocol_Stream. There are more
+lines of code here than we've seen so far but it still isn't
+complicated. The basic idea is to construct the ACE_Stream with our
+set of protocol objects that will manipulate the data. Our primary
+concern in this file is to get everything in the correct order!
+<HR>
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#include</font> "<font color=green>Protocol_Stream.h</font>"
+<font color=blue>#include</font> "<font color=green>Protocol_Task.h</font>"
+
+<font color=blue>#include</font> "<font color=green>Xmit.h</font>"
+<font color=blue>#include</font> "<font color=green>Recv.h</font>"
+
+<font color=blue>#include</font> "<font color=green>Compressor.h</font>"
+<font color=blue>#include</font> "<font color=green>Crypt.h</font>"
+
+<font color=blue>#include</font> "<font color=green>ace/Stream_Modules.h</font>"
+
+<font color=red>/* You can choose at compile time to include/exclude the protocol
+ pieces.
+*/</font>
+<font color=blue>#define</font> <font color=purple>ENABLE_COMPRESSION</font>
+<font color=blue>#define</font> <font color=purple>ENABLE_ENCRYPTION</font>
+
+<font color=red>// The usual typedefs to make things easier to type.</font>
+typedef ACE_Module&lt;ACE_MT_SYNCH> Module;
+typedef ACE_Thru_Task&lt;ACE_MT_SYNCH> Thru_Task;
+
+<font color=red>/* Do-nothing constructor and destructor
+ */</font>
+
+<font color=#008888>Protocol_Stream::Protocol_Stream</font>( void )
+{
+ ;
+}
+
+<font color=#008888>Protocol_Stream::~Protocol_Stream</font>( void )
+{
+ ;
+}
+
+<font color=red>/* Even opening the stream is rather simple. The important thing to
+ rememer is that the modules you push onto the stream first will be
+ at the tail (eg -- most downstream) end of things when you're
+ done.
+ */</font>
+int <font color=#008888>Protocol_Stream::open</font>( ACE_SOCK_Stream & _peer, Protocol_Task * _reader )
+{
+ <font color=red>// Initialize our peer() to read/write the socket we're given</font>
+ peer_.set_handle( _peer.get_handle() );
+
+ <font color=red>// Construct (and remember) the Recv object so that we can</font>
+ <font color=red>// read from the peer().</font>
+ recv_ = new Recv( peer() );
+
+ <font color=red>// Add the transmit and receive tasks to the head of the</font>
+ <font color=red>// stream. As we add more modules these will get pushed</font>
+ <font color=red>// downstream and end up nearest the tail by the time we're</font>
+ <font color=red>// done.</font>
+ if( stream().push( new Module( "<font color=green>Xmit/Recv</font>", new Xmit( peer() ), recv_ ) ) == -1 )
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>stream().push( xmit/recv )</font>"), -1);
+ }
+
+ <font color=red>// Add any other protocol tasks to the stream. Each one is</font>
+ <font color=red>// added at the head. The net result is that Xmit/Recv are at </font>
+ <font color=red>// the tail.</font>
+ if( this->open() == -1 )
+ {
+ return(-1);
+ }
+
+ <font color=red>// If a reader task was provided then push that in as the</font>
+ <font color=red>// upstream side of the next-to-head module. Any data read</font>
+ <font color=red>// from the peer() will be sent through here last. Server</font>
+ <font color=red>// applications will typically use this task to do the actual</font>
+ <font color=red>// processing of data.</font>
+ <font color=red>// Note the use of Thru_Task. Since a module must always have </font>
+ <font color=red>// a pair of tasks we use this on the writter side as a no-op.</font>
+ if( _reader )
+ {
+ if( stream().push( new Module( "<font color=green>Reader</font>", new Thru_Task(), _reader ) ) == -1 )
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>stream().push( reader )</font>"), -1);
+ }
+ }
+
+ return(0);
+}
+
+<font color=red>/* Add the necessary protocol objects to the stream. The way we're
+ pushing things on we will encrypt the data before compressing it.
+*/</font>
+int <font color=#008888>Protocol_Stream::open</font>(void)
+{
+<font color=blue>#if defined</font>(<font color=purple>ENABLE_COMPRESSION</font>)
+ if( stream().push( new Module( "<font color=green>compress</font>", new Compressor(), new Compressor() ) ) == -1 )
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>stream().push( comprssor )</font>"), -1);
+ }
+<font color=blue>#endif</font> <font color=red>// ENABLE_COMPRESSION</font>
+
+<font color=blue>#if defined</font>(<font color=purple>ENABLE_ENCRYPTION</font>)
+ if( stream().push( new Module( "<font color=green>crypt</font>", new Crypt(), new Crypt() ) ) == -1 )
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>stream().push( crypt )</font>"), -1);
+ }
+<font color=blue>#endif</font> <font color=red>// ENABLE_ENCRYPTION</font>
+ return( 0 );
+}
+
+<font color=red>// Closing the Protocol_Stream is as simple as closing the ACE_Stream.</font>
+int <font color=#008888>Protocol_Stream::close</font>(void)
+{
+ return stream().close();
+}
+
+<font color=red>// Simply pass the data directly to the ACE_Stream.</font>
+int <font color=#008888>Protocol_Stream::put</font>(ACE_Message_Block * & _message, ACE_Time_Value * _timeout )
+{
+ return stream().put(_message,_timeout);
+}
+
+<font color=red>/* Tell the Recv module to read some data from the peer and pass it
+ upstream. Servers will typically use this method in a
+ handle_input() method to tell the stream to get a client's request.
+*/</font>
+int <font color=#008888>Protocol_Stream::get</font>(void)
+{
+ <font color=red>// If there is no Recv module, we're in big trouble!</font>
+ if( ! recv_ )
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>(%P|%t) No Recv object!\n</font>"), -1);
+ }
+
+ <font color=red>// This tells the Recv module to go to it's peer() and read</font>
+ <font color=red>// some data. Once read, that data will be pushed upstream.</font>
+ <font color=red>// If there is a reader object then it will have a chance to</font>
+ <font color=red>// process the data. If not, the received data will be</font>
+ <font color=red>// available in the message queue of the stream head's reader</font>
+ <font color=red>// object (eg -- stream().head()->reader()->msg_queue()) and</font>
+ <font color=red>// can be read with our other get() method below.</font>
+ if( recv_->get() == -1 )
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>(%P|%t) Cannot queue read request\n</font>"), -1);
+ }
+
+ <font color=red>// For flexibility I've added an error() method to tell us if</font>
+ <font color=red>// something bad has happened to the Recv object.</font>
+ if( recv_->error() )
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>(%P|%t) Recv object error!\n</font>"), -1);
+ }
+
+ return(0);
+}
+
+<font color=red>/* Take a message block off of the stream head reader's message
+ queue. If the queue is empty, use get() to read from the peer.
+ This is most often used by client applications. Servers will
+ generaly insert a reader that will prevent the data from getting
+ all the way upstream to the head.
+*/</font>
+int <font color=#008888>Protocol_Stream::get</font>(ACE_Message_Block * & _response, ACE_Time_Value * _timeout )
+{
+ if( stream().head()->reader()->msg_queue()->is_empty() )
+ {
+ if( this->get() == -1 )
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>(%P|%t) Cannot get data into the stream.\n</font>"), -1);
+ }
+ }
+
+ return stream().head()->reader()->getq(_response,_timeout);
+}
+</PRE>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page12.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page12.html b/docs/tutorials/015/page12.html
new file mode 100644
index 00000000000..b125a7e3f57
--- /dev/null
+++ b/docs/tutorials/015/page12.html
@@ -0,0 +1,94 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 015</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 015</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Building a protocol stream</FONT></B></CENTER>
+
+<P>
+<HR WIDTH="100%">
+And now the implementation of the Protocol_Stream. There are more
+lines of code here than we've seen so far but it still isn't
+complicated. The basic idea is to construct the ACE_Stream with our
+set of protocol objects that will manipulate the data. Our primary
+concern in this file is to get everything in the correct order!
+<HR>
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#ifndef</font> <font color=purple>PROTOCOL_TASK_H</font>
+<font color=blue>#define</font> <font color=purple>PROTOCOL_TASK_H</font>
+
+<font color=blue>#include</font> "<font color=green>ace/Task.h</font>"
+
+<font color=red>/* A typical ACE_Task&lt;> derivative that adds a few things appropriate
+ to protocol stacks.
+*/</font>
+class Protocol_Task : public ACE_Task&lt;ACE_MT_SYNCH>
+{
+public:
+
+ typedef ACE_Task&lt;ACE_MT_SYNCH> inherited;
+
+ <font color=red>// A choice of concurrency strategies is offered by the</font>
+ <font color=red>// constructor. In most cases it makes sense to set this to</font>
+ <font color=red>// zero and let things proceed serially. You might have a</font>
+ <font color=red>// need, however, for some of your tasks to have their own thread.</font>
+ Protocol_Task( int _thr_count );
+
+ ~Protocol_Task(void);
+
+ <font color=red>// open() is invoked when the task is inserted into the stream.</font>
+ virtual int open(void *arg);
+
+ <font color=red>// close() is invoked when the stream is closed (flags will be </font>
+ <font color=red>// set to '1') and when the svc() method exits (flags will be</font>
+ <font color=red>// '0').</font>
+ virtual int close(u_long flags);
+
+ <font color=red>// As data travels through the stream, the put() method of</font>
+ <font color=red>// each task is invoked to keep the data moving along.</font>
+ virtual int put(ACE_Message_Block *message,
+ ACE_Time_Value *timeout);
+
+ <font color=red>// If you choose to activate the task then this method will be </font>
+ <font color=red>// doing all of the work.</font>
+ virtual int svc(void);
+
+protected:
+
+ <font color=red>// Called by put() or svc() as necessary to process a block of </font>
+ <font color=red>// data.</font>
+ int process(ACE_Message_Block * message, ACE_Time_Value *timeout);
+
+ <font color=red>// Just let us know if we're active or not.</font>
+ int is_active(void)
+ {
+ return this->thr_count() != 0;
+ }
+
+ <font color=red>// Tasks on the writter (downstream) side of the stream</font>
+ <font color=red>// are called upon to send() data that will ultimately go to</font>
+ <font color=red>// the peer.</font>
+ virtual int send(ACE_Message_Block *message,
+ ACE_Time_Value *timeout);
+
+ <font color=red>// Tasks on the reader (upstream) side will be receiving data</font>
+ <font color=red>// that came from the peer.</font>
+ virtual int recv(ACE_Message_Block * message,
+ ACE_Time_Value *timeout);
+
+private:
+ int desired_thr_count_;
+};
+
+<font color=blue>#endif</font> <font color=red>// PROTOCOL_TASK_H</font>
+</PRE>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page13.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page13.html b/docs/tutorials/015/page13.html
new file mode 100644
index 00000000000..da6fa97e0e5
--- /dev/null
+++ b/docs/tutorials/015/page13.html
@@ -0,0 +1,170 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 015</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 015</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Building a protocol stream</FONT></B></CENTER>
+
+<P>
+<HR WIDTH="100%">
+The Protocol_Task implementation takes care of the open(), close(),
+put() and svc() methods so that derivatives can concentrate on the
+send() and recv() methods. After a while you find that most
+ACE_Task<> derivatives look very similar in the four basic methods and
+only need one or two additional to do any real work.
+<HR>
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#include</font> "<font color=green>Protocol_Task.h</font>"
+
+<font color=red>// Construct the object and remember the thread count.</font>
+<font color=#008888>Protocol_Task::Protocol_Task</font>( int _thr_count )
+ : desired_thr_count_(_thr_count)
+{
+}
+
+<font color=#008888>Protocol_Task::~Protocol_Task</font>(void)
+{
+}
+
+<font color=red>// Activate the object if necessary.</font>
+int <font color=#008888>Protocol_Task::open</font>(void *arg)
+{
+ ACE_UNUSED_ARG(arg);
+
+ if( desired_thr_count_ )
+ {
+ return this->activate(THR_NEW_LWP, desired_thr_count_);
+ }
+
+ return(0);
+}
+
+<font color=red>/* When we're being closed by the ACE_Stream and we've got threads to
+ worry about then we drop a hangup message onto the message queue so
+ that svc() will go away. Except for the call to is_active(), this
+ is lifted directly from Tutorial 14.
+*/</font>
+int <font color=#008888>Protocol_Task::close</font>(u_long flags)
+{
+ if (flags == 1 && is_active() )
+ {
+ ACE_Message_Block *hangupBlock = new ACE_Message_Block();
+
+ hangupBlock->msg_type(<font color=#008888>ACE_Message_Block::MB_HANGUP</font>);
+
+ if (this->putq(hangupBlock->duplicate()) == -1) {
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green><font color=#008888>Task::close</font>() putq</font>"), -1);
+ }
+
+ hangupBlock->release();
+
+ return this->wait();
+ }
+
+ return 0;
+}
+
+<font color=red>/* The put() method has to make a decision. If we've got threads then
+ put the unit of work onto the message queue for svc() to deal
+ with. If not then process() it directly.
+*/</font>
+int <font color=#008888>Protocol_Task::put</font>(ACE_Message_Block *message,ACE_Time_Value *timeout)
+{
+ if( is_active() )
+ {
+ return this->putq(message,timeout);
+ }
+
+ return this->process(message,timeout);
+}
+
+<font color=red>/* svc() is about what you would expect. This is again lifted
+ directly from Tutorial 14 but with a call to process() for handling
+ the logic instead of doing the work right here.
+ */</font>
+int <font color=#008888>Protocol_Task::svc</font>(void)
+{
+ ACE_Message_Block * message;
+
+ while (1)
+ {
+ <font color=red>// Get a message</font>
+ if ( this->getq(message, 0) == -1) {
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green><font color=#008888>Protocol_Task::svc</font>() getq</font>"), -1);
+ }
+
+ ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) <font color=#008888>Protocol_Task::svc</font>() got message\n</font>"));
+
+ <font color=red>// Check for hangup</font>
+ if (message->msg_type() == <font color=#008888>ACE_Message_Block::MB_HANGUP</font>) {
+
+ ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) <font color=#008888>Protocol_Task::svc</font>() -- HANGUP block received\n</font>"));
+
+ <font color=red>// Hangup our thread-pool peers (if any)</font>
+ if (this->putq(message->duplicate()) == -1) {
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green><font color=#008888>Protocol_Task::svc</font>() putq</font>"), -1);
+ }
+
+ <font color=red>// Leave svc()</font>
+ break;
+ }
+
+ <font color=red>// Do some work on the data.</font>
+ if( this->process(message->duplicate(),0) == -1 )
+ {
+ break;
+ }
+
+ <font color=red>// Give up the message block before we go get another.</font>
+ message->release();
+ }
+
+ <font color=red>// Give up the message block that caused us to exit the</font>
+ <font color=red>// while(1) loop.</font>
+ message->release();
+
+ return(0);
+}
+
+<font color=red>/* There's nothing really magic about process(). We just decide if
+ we're moving data upstream or downstream and invoke the appropriate
+ virtual function to handle it.
+*/</font>
+int <font color=#008888>Protocol_Task::process</font>(ACE_Message_Block * message, ACE_Time_Value *timeout)
+{
+ if( this->is_writer() )
+ {
+ return this->send(message,timeout);
+ }
+
+ return this->recv(message,timeout);
+}
+
+<font color=red>/* We must insist that derivatives provide a meaningful overload for
+ these methods. It's fairly common for ACE object methods to return
+ an error when an overload is expected but the method cannot be
+ safely made pure virtual.
+ */</font>
+
+int <font color=#008888>Protocol_Task::send</font>(ACE_Message_Block *message,
+ ACE_Time_Value *timeout)
+{
+ return -1;
+}
+
+int <font color=#008888>Protocol_Task::recv</font>(ACE_Message_Block * message,
+ ACE_Time_Value *timeout)
+{
+ return -1;
+}
+</PRE>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page14.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page14.html b/docs/tutorials/015/page14.html
new file mode 100644
index 00000000000..2478f40606e
--- /dev/null
+++ b/docs/tutorials/015/page14.html
@@ -0,0 +1,83 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 015</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 015</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Building a protocol stream</FONT></B></CENTER>
+
+<P>
+<HR WIDTH="100%">
+The implementation of Xmit isn't too complicated. If we choose to
+combine it with the Recv task we simply lift the recv() method from
+that object and drop it into this one.
+<P>
+Note that close() must decide if it's being called when the stream is
+shutdown or when it's svc() method exits. Since we tell the baseclass
+not to use any threads it's a safe bet that flags will always be
+non-zero. Still, it's good practice to plan for the future by
+checking the value.
+<P>
+Note also that when we send the data we prefix it with the data size.
+This let's our sibling Recv ensure that an entire block is received
+together. This can be very important for compression and encryption
+processes which typically work better with blocks of data instead of
+streams of data.
+<HR>
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#ifndef</font> <font color=purple>XMIT_H</font>
+<font color=blue>#define</font> <font color=purple>XMIT_h</font>
+
+<font color=blue>#include</font> "<font color=green>Protocol_Task.h</font>"
+
+<font color=red>// Forward reference reduces <font color=blue>#include</font> dependencies</font>
+class ACE_SOCK_Stream;
+
+<font color=red>/* A class suitable for sending data to a peer from within an
+ ACE_Stream.
+ */</font>
+class Xmit : public Protocol_Task
+{
+public:
+
+ typedef Protocol_Task inherited;
+
+ <font color=red>// We must be given a valid peer when constructed. Without that</font>
+ <font color=red>// we don't know who to send data to.</font>
+ Xmit( ACE_SOCK_Stream & _peer );
+
+ ~Xmit(void);
+
+ <font color=red>// As you know, close() will be called in a couple of ways by the </font>
+ <font color=red>// ACE framework. We use that opportunity to terminate the</font>
+ <font color=red>// connection to the peer.</font>
+ int close(u_long flags);
+
+protected:
+
+ ACE_SOCK_Stream & peer(void)
+ {
+ return this->peer_;
+ }
+
+ <font color=red>// Send the data to the peer. By now it will have been</font>
+ <font color=red>// completely protocol-ized by other tasks in the stream.</font>
+ int send(ACE_Message_Block *message,
+ ACE_Time_Value *timeout);
+
+private:
+ <font color=red>// A representation of the peer we're talking to.</font>
+ ACE_SOCK_Stream & peer_;
+};
+
+<font color=blue>#endif</font> <font color=red>// XMIT_H</font>
+</PRE>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page15.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page15.html b/docs/tutorials/015/page15.html
new file mode 100644
index 00000000000..cae5a12f871
--- /dev/null
+++ b/docs/tutorials/015/page15.html
@@ -0,0 +1,115 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 015</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 015</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Building a protocol stream</FONT></B></CENTER>
+
+<P>
+<HR WIDTH="100%">
+Recv is the sibling to Xmit. Again, they could be combined into a
+single object if you want.
+<P>
+An ACE_Stream is designed to handle downstream traffic very
+well. You put() data into it and it flows along towards the tail.
+However, there doesn't seem to be a way to put data in such that it
+will travel upstream. To get around that, I've added a get() method
+to Recv that will trigger a read on the socket. Recv will then put
+the data to the next upstream module and we're on our way. As noted
+earlier, that data will eventually show up either in the <i>reader</i>
+(if installed on the stream open()) or the stream head reader task's
+message queue.
+<HR>
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#include</font> "<font color=green>Xmit.h</font>"
+<font color=blue>#include</font> "<font color=green>ace/SOCK_Stream.h</font>"
+
+<font color=red>/* Construct the object with the peer connection and choose not to
+ activate ourselves into a dedicated thread. You might get some
+ performance gain by activating but if you really want a
+ multi-threaded apprroach you should handle that as a separate
+ issue. Attempting to force threading at this level will likely
+ cause more trouble than you want to deal with.
+*/</font>
+<font color=#008888>Xmit::Xmit</font>( ACE_SOCK_Stream & _peer )
+ : inherited(0), peer_(_peer)
+{
+}
+
+<font color=#008888>Xmit::~Xmit</font>(void)
+{
+}
+
+<font color=red>/* Check to see if we're being closed by the stream (flags != 0) or if
+ we're responding to the exit of our svc() method.
+*/</font>
+int <font color=#008888>Xmit::close</font>(u_long flags)
+{
+ <font color=red>// Take care of the baseclass closure.</font>
+ int rval = <font color=#008888>inherited::close</font>(flags);
+
+ <font color=red>// Only if we're being called at the stream shutdown do we close</font>
+ <font color=red>// the peer connection. If, for some reason, we were activated</font>
+ <font color=red>// into one or more threads we wouldn't want to close the pipe</font>
+ <font color=red>// before all threads had a chance to flush their data.</font>
+ if( flags )
+ {
+ peer().close();
+ }
+
+ return( rval );
+}
+
+<font color=red>/* Our overload of send() will take care of sending the data to the
+ peer.
+*/</font>
+int <font color=#008888>Xmit::send</font>(ACE_Message_Block *message, ACE_Time_Value *timeout)
+{
+ int rval;
+
+ ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t) <font color=#008888>Xmit::send</font>() sending (%s)(%d)\n</font>", message->rd_ptr(), message->length() ));
+
+ <font color=red>/* Since we're going to be sending data that may have been
+ compressed and encrypted it's probably important for the
+ receiver to get an entire "<font color=green>block</font>" instead of having a
+ partial read.
+
+ For that reason, we'll send the length of the message block
+ (in clear-text) to the peer so that it can then recv_n()
+ the entire block contents in one read operation.
+ */</font>
+ char msize[32];
+ sprintf(msize,"<font color=green>%d</font>",message->length());
+
+ <font color=red>// Be sure we send the end-of-string NULL so that Recv will</font>
+ <font color=red>// know when to stop assembling the length.</font>
+ rval = this->peer().send_n( msize, strlen(msize)+1, 0, timeout );
+
+ if( rval == -1 )
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green><font color=#008888>Xmit::send</font>() Failed to send message size.</font>"), -1);
+ }
+
+ <font color=red>/* Now we send the actual data. If you're worried about
+ network efficiency then you may choose to create one buffer
+ containing msize and the message data and send it all at
+ once.
+ */</font>
+ rval = this->peer().send_n( message->rd_ptr(), message->length(), 0, timeout );
+
+ <font color=red>// Release the message block since we're done with it.</font>
+ message->release();
+
+ return( rval );
+}
+</PRE>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page16.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page16.html b/docs/tutorials/015/page16.html
new file mode 100644
index 00000000000..75ca714d7dd
--- /dev/null
+++ b/docs/tutorials/015/page16.html
@@ -0,0 +1,85 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 015</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 015</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Building a protocol stream</FONT></B></CENTER>
+
+<P>
+<HR WIDTH="100%">
+The Recv implementation is nearly as simple as Xmit. There's
+opportunity for error when we get the message size and we have to
+manage the lifetime of the tickler but other than that it's pretty
+basic stuff.
+<HR>
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#ifndef</font> <font color=purple>RECV_H</font>
+<font color=blue>#define</font> <font color=purple>RECV_h</font>
+
+<font color=blue>#include</font> "<font color=green>Protocol_Task.h</font>"
+
+class ACE_SOCK_Stream;
+
+<font color=red>/* Get some data from the peer and send it upstream for
+ de-protocol-ization.
+*/</font>
+class Recv : public Protocol_Task
+{
+public:
+
+ typedef Protocol_Task inherited;
+
+ <font color=red>// Give it someone to talk to...</font>
+ Recv( ACE_SOCK_Stream & _peer );
+
+ ~Recv(void);
+
+ <font color=red>// Trigger a read from the socket</font>
+ int get(void);
+
+ <font color=red>// In some cases it might be easier to check the "<font color=green>state</font>" of the</font>
+ <font color=red>// Recv object than to rely on return codes filtering back to</font>
+ <font color=red>// you.</font>
+ int error(void)
+ {
+ return this->error_;
+ }
+
+protected:
+
+ ACE_SOCK_Stream & peer(void)
+ {
+ return this->peer_;
+ }
+
+ <font color=red>// The baseclass will trigger this when our get() method is</font>
+ <font color=red>// called. A message block of the appropriate size is created,</font>
+ <font color=red>// filled and passed up the stream.</font>
+ int recv(ACE_Message_Block * message,
+ ACE_Time_Value *timeout = 0);
+
+private:
+ <font color=red>// Our endpoint</font>
+ ACE_SOCK_Stream & peer_;
+
+ <font color=red>// get() uses a bogus message block to cause the baseclass to</font>
+ <font color=red>// invoke recv(). To avoid memory thrashing, we create that</font>
+ <font color=red>// bogus message once and reuse it for the life of Recv.</font>
+ ACE_Message_Block * tickler_;
+
+ <font color=red>// Our error flag (duh)</font>
+ int error_;
+};
+
+<font color=blue>#endif</font> <font color=red>// RECV_H</font>
+</PRE>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page17.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page17.html b/docs/tutorials/015/page17.html
new file mode 100644
index 00000000000..f563bfabd9a
--- /dev/null
+++ b/docs/tutorials/015/page17.html
@@ -0,0 +1,106 @@
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="Author" CONTENT="James CE Johnson">
+ <TITLE>ACE Tutorial 015</TITLE>
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">
+
+<CENTER><B><FONT SIZE=+2>ACE Tutorial 015</FONT></B></CENTER>
+
+<CENTER><B><FONT SIZE=+2>Building a protocol stream</FONT></B></CENTER>
+
+<P>
+<HR WIDTH="100%">
+This and the next three pages present the protocol objects that
+provide compression and encryption. If you were hoping to
+<HR>
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#include</font> "<font color=green>Recv.h</font>"
+<font color=blue>#include</font> "<font color=green>ace/SOCK_Stream.h</font>"
+
+<font color=red>/* Construct the object with the peer reference and other appropriate
+ initializations.
+*/</font>
+<font color=#008888>Recv::Recv</font>( ACE_SOCK_Stream & _peer )
+ : inherited(0), peer_(_peer), error_(0)
+{
+ <font color=red>// Create the tickler that get() will use to trigger recv()</font>
+ <font color=red>// through the baseclass. Since we're single-threaded this is</font>
+ <font color=red>// probably overkill but it makes multi-threading easier if we</font>
+ <font color=red>// choose to do that.</font>
+ tickler_ = new ACE_Message_Block(1);
+}
+
+<font color=red>/* Be sure we manage the lifetime of the tickler to prevent a memory
+ leak.
+*/</font>
+<font color=#008888>Recv::~Recv</font>(void)
+{
+ tickler_->release();
+}
+
+<font color=red>/* By putting the tickler to ourselves we cause things to happen in
+ the baseclass that will invoke recv(). If we know we're single
+ threaded we could directly call recv() and be done with it but then
+ we'd have to do something else if we're multi-threaded. Just let
+ the baseclass worry about those things!
+*/</font>
+int <font color=#008888>Recv::get</font>(void)
+{
+ return this->put( tickler_, 0 );
+}
+
+int <font color=#008888>Recv::recv</font>(ACE_Message_Block * message, ACE_Time_Value *timeout)
+{
+ int rval;
+
+ <font color=red>/* Xmit will send us the message length in clear-text. I
+ assume that will be less than 32-bytes!
+ */</font>
+ char msize[32];
+ int b = 0;
+
+ <font color=red>/* Read from the socket one byte at a time until we see then
+ end-of-string NULL character. Since the OS layers (at leas
+ in Unix) will provide some buffering this isn't as bad as
+ it may seem at first.
+ */</font>
+ do
+ {
+ rval = this->peer().recv( &msize[b], 1, timeout );
+ if( rval == -1 )
+ {
+ error_ = 1;
+ ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green><font color=#008888>Recv::recv</font>() Failed to get message size.</font>"), -1);
+ }
+ }
+ while( msize[b++] != 0 );
+
+ int size = atoi(msize);
+
+ <font color=red>// Make a block big enough to contain the data we'll read</font>
+ message = new ACE_Message_Block( size );
+
+ <font color=red>// Read the actual message data into our new message block</font>
+ rval = this->peer().recv_n( message->wr_ptr(), size, 0, timeout );
+
+ <font color=red>// If we got the data correctly then send it on upstream.</font>
+ if( rval > 0 )
+ {
+ message->wr_ptr( rval );
+ return( this->put_next( message ) );
+ }
+
+ <font color=red>// Something bad happend on the recv_n(). Set an error flag</font>
+ <font color=red>// and return error.</font>
+ error_ = 1;
+
+ return( -1 );
+}
+</PRE>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] </CENTER>
diff --git a/docs/tutorials/015/server.cpp b/docs/tutorials/015/server.cpp
index ac2c2e9e0a1..4ea275f6d83 100644
--- a/docs/tutorials/015/server.cpp
+++ b/docs/tutorials/015/server.cpp
@@ -3,6 +3,7 @@
#include "Server.h"
+// A signal handler that will close the server object
extern "C" void handler (int)
{
Server::close();
@@ -10,15 +11,20 @@ extern "C" void handler (int)
int main (int, char **)
{
+ // The server object that abstracts away all of difficult parts.
Server server;
+ // Attempt to open the server. Like all good ACE-based
+ // objects, we'll get -1 on failure.
if( server.open() == -1 )
{
ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "server.open()"), -1);
}
+ // Install a signal handler for ^C so that we can exit gracefully
ACE_Sig_Action sa ((ACE_SignalHandler) handler, SIGINT);
+ // Run the server's main loop until we're interrupted
if( server.run() == -1 )
{
ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "server.run()"), -1);
@@ -27,6 +33,9 @@ int main (int, char **)
return 0;
}
+/* These explicit instantiations were taken from an earlier tutorial.
+ Your compiler may require others as well.
+*/
#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION)
template class ACE_Acceptor <Handler, ACE_SOCK_ACCEPTOR>;
template class ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>;
diff --git a/docs/tutorials/015/stream.gif b/docs/tutorials/015/stream.gif
new file mode 100644
index 00000000000..daae2420b74
--- /dev/null
+++ b/docs/tutorials/015/stream.gif
Binary files differ
diff --git a/docs/tutorials/combine b/docs/tutorials/combine
index 5852837fbfb..bf2372816f2 100755
--- a/docs/tutorials/combine
+++ b/docs/tutorials/combine
@@ -18,13 +18,6 @@
#
# Input files:
#
-# links
-# A list of links to be created before combining. The format of this
-# file is:
-# source-file destination-file format-code
-# A hard-link will be created from source to dest. The format-code 'P'
-# will tell the combiner to wrap the destination in <PRE></PRE> if it
-# is later used as a bdy file.
#
# hdr
# If no *.hdr exists for the current page, this is used. You will typically
@@ -42,6 +35,9 @@
# links between source-code and a bdy filename. The bdy files are
# examined and "fontified" (think emacs).
#
+# bodies
+# The list of files to use for the body of the HTML pages generated
+#
# *.pst
# Post files. This content follows the bdy content.
#
@@ -53,14 +49,25 @@ use File::Copy;
%format = ();
-open(LINKS,"<links") || die;
-while( (($src,$dst,$fmt) = split(/\s+/,<LINKS>)) ) {
+$PAGE=0;
+
+open(LINKS,"<bodies") || die;
+while( ($file = <LINKS>) ) {
+
+ chomp($file);
- next if( $src =~ /^#/ || $src eq '' );
+ next if( $file =~ /^#/ || $file eq '' );
+
+
+ if( $file =~ /^.*=/ ) {
+ ($var,$value) = split(/=/,$file);
+ if( $var =~ /PAGE/ ) {
+ $PAGE = $value + 0;
+ }
+ next;
+ }
- unlink($dst);
- link($src,$dst) || die "Failed to link ($src) to ($dst): $!\n";
- $format{"$dst"} = "$fmt";
+ $body{$PAGE++} = "$file";
}
close(LINKS);
@@ -85,28 +92,14 @@ foreach $file (@ARGV) {
}
# .bdy is the body of the page
if( -f "$base.bdy" ) {
- if( $format{"$base\.bdy"} eq 'P' ) {
- print FILE "<PRE>\n" ;
- open(INPUT,"<$base.bdy") || die "Cannot open $base.bdy for read\n";
- # Do some substitutes on each line to try and get the output to
- # look like it does in fontified emacs.
- while( <INPUT> )
- {
- s/</\&lt;/g;
- s,\#(e?l?if !?defined|pragma|ifn?def|define)(\W*)([\w\.]+),<font color=blue>\#$1</font>$2<font color=purple>$3</font>,;
- s,\#(include|endif),<font color=blue>$&</font>,;
- s,"([^"]+)","<font color=green>$1</font>",g;
- s,//.*$,<font color=red>$&</font>,;
- s,/\*,<font color=red>$&,;
- s,\*/,$&</font>,;
- s,\w+::\~?\w+,<font color=\#008888>$&</font>,;
- print FILE $_;
- }
- print FILE "</PRE>\n";
- }
- else {
- copy("$base.bdy",\*FILE);
- }
+ &addFile("$base.bdy");
+ }
+
+ ($num = $base) =~ s/[A-z]//g;
+ $num += 0;
+
+ if( -f "$body{$num}" ) {
+ &addFile("$body{$num}");
}
# .pst summarizes what was seen
if( -f "$base.pst" ) {
@@ -117,9 +110,8 @@ foreach $file (@ARGV) {
copy("$base.ftr",\*FILE);
}
else {
- ($num = $base) =~ s/[A-z]//g;
- $base =~ s/[0-9]//g;
++$num;
+ $base =~ s/[0-9]//g;
$next = sprintf("$base%02.2d",$num);
print FILE "<P><HR WIDTH=\"100%\">\n";
print FILE "<CENTER>[<A HREF=\"..\">Tutorial Index</A>] ";
@@ -131,3 +123,24 @@ foreach $file (@ARGV) {
close(FILE);
}
+sub addFile {
+ local($file) = @_;
+
+ print FILE "<PRE>\n" ;
+ open(INPUT,"<$file") || die "Cannot open $file for read\n";
+ # Do some substitutes on each line to try and get the output to
+ # look like it does in fontified emacs.
+ while( <INPUT> )
+ {
+ s/</\&lt;/g;
+ s,\#(e?l?if !?defined|pragma|ifn?def|define)(\W*)([\w\.]+),<font color=blue>\#$1</font>$2<font color=purple>$3</font>,;
+ s,\#(include|endif),<font color=blue>$&</font>,;
+ s,"([^"]+)","<font color=green>$1</font>",g;
+ s,//.*$,<font color=red>$&</font>,;
+ s,/\*,<font color=red>$&,;
+ s,\*/,$&</font>,;
+ s,\w+::\~?\w+,<font color=\#008888>$&</font>,;
+ print FILE $_;
+ }
+ print FILE "</PRE>\n";
+}