summaryrefslogtreecommitdiff
path: root/docs/tutorials
diff options
context:
space:
mode:
Diffstat (limited to 'docs/tutorials')
-rw-r--r--docs/tutorials/015/Compressor.cpp38
-rw-r--r--docs/tutorials/015/Compressor.h23
-rw-r--r--docs/tutorials/015/Crypt.cpp18
-rw-r--r--docs/tutorials/015/Crypt.h13
-rw-r--r--docs/tutorials/015/combine.shar333
-rw-r--r--docs/tutorials/015/page04.html1
-rw-r--r--docs/tutorials/015/page09.html2
-rw-r--r--docs/tutorials/015/page14.html22
-rw-r--r--docs/tutorials/015/page15.html25
-rw-r--r--docs/tutorials/015/page16.html16
-rw-r--r--docs/tutorials/015/page17.html8
-rw-r--r--docs/tutorials/015/page18.html68
-rw-r--r--docs/tutorials/015/page19.html119
-rw-r--r--docs/tutorials/015/page20.html61
-rw-r--r--docs/tutorials/015/page21.html99
-rw-r--r--docs/tutorials/015/page22.html82
-rw-r--r--docs/tutorials/index.html13
17 files changed, 835 insertions, 106 deletions
diff --git a/docs/tutorials/015/Compressor.cpp b/docs/tutorials/015/Compressor.cpp
index 06efab06947..c2f796bd68e 100644
--- a/docs/tutorials/015/Compressor.cpp
+++ b/docs/tutorials/015/Compressor.cpp
@@ -4,22 +4,38 @@
#include "Compressor.h"
#include "ace/SOCK_Stream.h"
+/* Construct our baseclass with the proper thread count. I really
+ should remove this option...
+ */
Compressor::Compressor( int _thr_count )
: inherited(_thr_count)
{
+ ;
}
Compressor::~Compressor(void)
{
+ ;
}
+/* This is where you insert your compression code. Most compressors
+ want to work on a block of data instead of a byte-stream.
+ Fortunately the message block has a block that can be compressed.
+ Take a look at libz for a quick way to add compression to your
+ apps
+ */
int Compressor::send(ACE_Message_Block *message, ACE_Time_Value *timeout)
{
ACE_DEBUG ((LM_INFO, "(%P|%t) Compressor::send() compressing (%s)\n", message->rd_ptr() ));
+ // Create a block to hold the compressed data. I belive libz
+ // recommends a buffer about 10-20% larger than the source.
+ // Other libraries/algorithms may have their own quirks.
ACE_Message_Block * compressed = new ACE_Message_Block( message->size() );
- // Perform a bogus compression algorithm
+ // Perform a bogus compression algorithm. 'CD' just tells me
+ // that this is compressed data and when we "decompress" we'll
+ // look for this signature to validate the data received.
ACE_OS::sprintf( compressed->wr_ptr(), "CD:%s", message->rd_ptr() );
compressed->wr_ptr( strlen(compressed->wr_ptr())+1 );
@@ -32,12 +48,27 @@ int Compressor::send(ACE_Message_Block *message, ACE_Time_Value *timeout)
return( 0 );
}
+/* And here's the decompression side. We've written Xmit/Recv so that
+ we're guaranteed to get an entire block of compressed data. If
+ we'd used recv() in the Recv object then we might have gotten a
+ partial block and that may not decompress very nicely.
+ */
int Compressor::recv(ACE_Message_Block *message, ACE_Time_Value *timeout)
{
ACE_DEBUG ((LM_INFO, "(%P|%t) Compress::recv() decompressing (%s)\n", message->rd_ptr() ));
+ // Room for the decompressed data. In the real world you
+ // would probably want to send the original (uncompressed)
+ // data size in the message. You can predict the maximum
+ // possible decompression size but it's cheap and easy just to
+ // send that along. Look again at how I do exacly that
+ // between Xmit and Recv.
ACE_Message_Block * decompressed = new ACE_Message_Block( message->size() );
+ // Check for our signature. Even when you use a real
+ // compression algorithm you may want to include your own
+ // signature so that you can verify the block. It pays to be
+ // paranoid!
if( ACE_OS::strncmp( message->rd_ptr(), "CD:", 3 ) )
{
ACE_DEBUG ((LM_INFO, "(%P|%t) Improperly encompressed data.\n" ));
@@ -45,9 +76,12 @@ int Compressor::recv(ACE_Message_Block *message, ACE_Time_Value *timeout)
return(-1);
}
+ // Skip past the signature before going any further.
message->rd_ptr( 3 );
- // Perform a bogus decompression algorithm
+ // Perform a bogus decompression algorithm. This is where you
+ // would feed to libz or your favorite decompressor. (It's
+ // costly but you could invoke popen() on gzip!)
ACE_OS::sprintf( decompressed->wr_ptr(), "%s", message->rd_ptr() );
decompressed->wr_ptr( strlen(decompressed->wr_ptr())+1 );
diff --git a/docs/tutorials/015/Compressor.h b/docs/tutorials/015/Compressor.h
index f1310432841..bc0257b16d1 100644
--- a/docs/tutorials/015/Compressor.h
+++ b/docs/tutorials/015/Compressor.h
@@ -6,27 +6,38 @@
#include "Protocol_Task.h"
-class ACE_SOCK_Stream;
-
+/* A reallly dumb compression object. (It actually adds 3 bytes to
+ every message block.)
+*/
class Compressor : public Protocol_Task
{
public:
typedef Protocol_Task inherited;
-
+
+ // I've given you the option of creating this task derivative
+ // with a number of threads. In retro-spect that really isn't
+ // a good idea. Most client/server systems rely on requests
+ // and responses happening in a predicatable order. Introduce
+ // a thread pool and message queue and that ordering goes
+ // right out the window. In other words: Don't ever use the
+ // constructor parameter!
Compressor( int _thr_count = 0 );
+
~Compressor(void);
protected:
+ // This is called when the compressor is on the downstream
+ // side. We'll take the message, compress it and move it
+ // along to the next module.
int send(ACE_Message_Block *message,
ACE_Time_Value *timeout);
+ // This one is called on the upstream side. No surprise: we
+ // decompress the data and send it on up the stream.
int recv(ACE_Message_Block *message,
ACE_Time_Value *timeout);
-
-private:
- mode_t mode_;
};
#endif // COMPRESSOR_H
diff --git a/docs/tutorials/015/Crypt.cpp b/docs/tutorials/015/Crypt.cpp
index 26827ce44b0..1e20b8f5b42 100644
--- a/docs/tutorials/015/Crypt.cpp
+++ b/docs/tutorials/015/Crypt.cpp
@@ -4,6 +4,8 @@
#include "Crypt.h"
#include "ace/SOCK_Stream.h"
+/* The expected constructor...
+ */
Crypt::Crypt( int _thr_count )
: inherited(_thr_count)
{
@@ -13,13 +15,19 @@ Crypt::~Crypt(void)
{
}
+/* To send the data we'll apply a signature and encryption.
+ */
int Crypt::send(ACE_Message_Block *message, ACE_Time_Value *timeout)
{
ACE_DEBUG ((LM_INFO, "(%P|%t) Crypt::send() encrypting (%s)\n", message->rd_ptr() ));
+ // I suspect that some encryptors might change the data size.
+ // It probably isn't safe to create a same-size destination buffer.
ACE_Message_Block * encrypted = new ACE_Message_Block( message->size() );
- // Perform a bogus encryption algorithm
+ // Perform a bogus encryption algorithm and add our safety
+ // signature. Adding the original data size is also probably
+ // a good idea that I haven't encorporated here.
ACE_OS::sprintf( encrypted->wr_ptr(), "ED:%s", message->rd_ptr() );
encrypted->wr_ptr( strlen(encrypted->wr_ptr())+1 );
@@ -32,12 +40,18 @@ int Crypt::send(ACE_Message_Block *message, ACE_Time_Value *timeout)
return( 0 );
}
+/* The upstream movement requires that we decrypt what the peer has
+ given us.
+*/
int Crypt::recv(ACE_Message_Block *message, ACE_Time_Value *timeout)
{
ACE_DEBUG ((LM_INFO, "(%P|%t) Crypt::recv() decrypting (%s)\n", message->rd_ptr() ));
+ // Create a destination for the decrypted data. The same
+ // block size caveat exists of course.
ACE_Message_Block * decrypted = new ACE_Message_Block( message->size() );
+ // Check the signature as expected.
if( ACE_OS::strncmp( message->rd_ptr(), "ED:", 3 ) )
{
ACE_DEBUG ((LM_INFO, "(%P|%t) Improperly encrypted data.\n" ));
@@ -45,6 +59,8 @@ int Crypt::recv(ACE_Message_Block *message, ACE_Time_Value *timeout)
return(-1);
}
+ // Don't forget to skip past the signature before decrypting
+ // or things will be quite exciting!
message->rd_ptr( 3 );
// Perform a bogus decryption algorithm
diff --git a/docs/tutorials/015/Crypt.h b/docs/tutorials/015/Crypt.h
index b34dda4c12e..7a38e1bdb52 100644
--- a/docs/tutorials/015/Crypt.h
+++ b/docs/tutorials/015/Crypt.h
@@ -6,25 +6,30 @@
#include "Protocol_Task.h"
-class ACE_SOCK_Stream;
-
+/* An interface (adaptor) between your favorite encryption method and
+ an ACE_Stream.
+*/
class Crypt : public Protocol_Task
{
public:
typedef Protocol_Task inherited;
-
+
+ // Again we have the option of multiple threads and again I
+ // regret tempting folks to use it.
Crypt( int _thr_count = 0 );
+
~Crypt(void);
protected:
+ // Moving downstream will encrypt the data
int send(ACE_Message_Block *message,
ACE_Time_Value *timeout);
+ // And moving upstream will decrypt it.
int recv(ACE_Message_Block *message,
ACE_Time_Value *timeout);
-private:
};
#endif // CRYPT_H
diff --git a/docs/tutorials/015/combine.shar b/docs/tutorials/015/combine.shar
index 564843d6251..2faa3e703da 100644
--- a/docs/tutorials/015/combine.shar
+++ b/docs/tutorials/015/combine.shar
@@ -3,8 +3,8 @@
# 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-19 16:39 EDT by <jcej@caldera.lads.com>.
-# Source directory was `/scsiA/home/jcej/projects/ACE_wrappers/docs/tutorials/015'.
+# Made on 1998-10-19 20:42 EDT by <jcej@chiroptera.tragus.org>.
+# Source directory was `/var/home/jcej/projects/ACE_wrappers/docs/tutorials/015'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
@@ -26,11 +26,16 @@
# 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
+# 540 -rw-rw-r-- page14.pre
+# 770 -rw-rw-r-- page15.pre
+# 660 -rw-rw-r-- page16.pre
+# 213 -rw-rw-r-- page17.pre
+# 372 -rw-rw-r-- page18.pre
+# 281 -rw-rw-r-- page19.pre
+# 356 -rw-rw-r-- page20.pre
+# 151 -rw-rw-r-- page21.pre
+# 2551 -rw-rw-r-- page22.pre
+# 398 -rw-rw-r-- page04.pst
# 609 -rw-rw-r-- page09.pst
#
save_IFS="${IFS}"
@@ -78,7 +83,7 @@ else
fi
rm -f 1231235999 $$.touch
#
-if mkdir _sh23762; then
+if mkdir _sh02329; then
$echo 'x -' 'creating lock directory'
else
$echo 'failed to create lock directory'
@@ -594,36 +599,30 @@ if test -f 'page14.pre' && test "$first_param" != -c; then
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.
+The Xmit object knows how to send data to the peer. It sits at the
+tail of the stream and gets everything that flows down from the head.
+In keeping with the spirit of things, this object does only one thing
+and doesn't concern itself with anyone else' details.
<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.
+The only thing you might want to do is combine it with Recv. Why?
+As you'll realize in a page or two, the Xmit and Recv objects must
+interact if you're going to ensure a safe transit. By having a single
+object it's easier to coordinate and maintain the interaction.
<HR>
SHAR_EOF
- $shar_touch -am 1019163798 'page14.pre' &&
+ $shar_touch -am 1019203498 '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
+bfc300ca2da5b82a5e452713cbf2f544 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!"
+ test 540 -eq "$shar_count" ||
+ $echo 'page14.pre:' 'original size' '540,' 'current size' "$shar_count!"
fi
fi
# ============= page15.pre ==============
@@ -632,18 +631,21 @@ if test -f 'page15.pre' && test "$first_param" != -c; then
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.
+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>
-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.
+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 'page15.pre' &&
@@ -653,12 +655,12 @@ SHAR_EOF
&& ( 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
+0d79137eaedd73b820037fcafe6e16b6 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!"
+ test 770 -eq "$shar_count" ||
+ $echo 'page15.pre:' 'original size' '770,' 'current size' "$shar_count!"
fi
fi
# ============= page16.pre ==============
@@ -667,10 +669,18 @@ if test -f 'page16.pre' && test "$first_param" != -c; then
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.
+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 'page16.pre' &&
@@ -680,12 +690,12 @@ SHAR_EOF
&& ( 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
+2d89b3c894cfcfdfef47ae506913cdad 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!"
+ test 660 -eq "$shar_count" ||
+ $echo 'page16.pre:' 'original size' '660,' 'current size' "$shar_count!"
fi
fi
# ============= page17.pre ==============
@@ -694,8 +704,10 @@ if test -f 'page17.pre' && test "$first_param" != -c; then
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
+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 'page17.pre' &&
@@ -705,12 +717,212 @@ SHAR_EOF
&& ( 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
+7db337f2c6ec74d75560534dec550b0e 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!"
+ test 213 -eq "$shar_count" ||
+ $echo 'page17.pre:' 'original size' '213,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page18.pre ==============
+if test -f 'page18.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page18.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page18.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page18.pre' &&
+This and the next three pages present the protocol objects that
+provide compression and encryption. If you were hoping to learn the
+secrets of compression and encryption then I'm going to disappoint
+you. There are some really good libraries out there that do this
+stuff though and if anyone wants to integrate one of them into the
+tutorial I'll be glad to take it!
+<HR>
+SHAR_EOF
+ $shar_touch -am 1019203698 'page18.pre' &&
+ chmod 0664 'page18.pre' ||
+ $echo 'restore of' 'page18.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 'page18.pre:' 'MD5 check failed'
+dc5f706bd5a27009aed167c0b137648e page18.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page18.pre'`"
+ test 372 -eq "$shar_count" ||
+ $echo 'page18.pre:' 'original size' '372,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page19.pre ==============
+if test -f 'page19.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page19.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page19.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page19.pre' &&
+Here we implement the details of our compression. By having both
+compression and decompression in one object it's easier to keep track
+of implementation details. Splitting Xmit/Recv like I did will make
+things more difficult if something has to change in their interaction.
+<HR>
+SHAR_EOF
+ $shar_touch -am 1019195798 'page19.pre' &&
+ chmod 0664 'page19.pre' ||
+ $echo 'restore of' 'page19.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 'page19.pre:' 'MD5 check failed'
+4eb5dcd181f180d6c460971903efb288 page19.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page19.pre'`"
+ test 281 -eq "$shar_count" ||
+ $echo 'page19.pre:' 'original size' '281,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page20.pre ==============
+if test -f 'page20.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page20.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page20.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page20.pre' &&
+While I might be able to come up with a competitive compressor, I
+don't have a snowball's chance to code up encryption. I'd be better
+off piping the data through the standard Unix crypt command.
+<P>
+So, while I was lazy with Compress, I'm realistic with Crypt. I'll
+show you the hooks and entry points and let someone else contribute an
+encryptor.
+<HR>
+SHAR_EOF
+ $shar_touch -am 1019203798 'page20.pre' &&
+ chmod 0664 'page20.pre' ||
+ $echo 'restore of' 'page20.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 'page20.pre:' 'MD5 check failed'
+7f75130d385a34b2c421a8d7cae69cc3 page20.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page20.pre'`"
+ test 356 -eq "$shar_count" ||
+ $echo 'page20.pre:' 'original size' '356,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page21.pre ==============
+if test -f 'page21.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page21.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page21.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page21.pre' &&
+The encryption implementation isn't any smarter than that of the
+compressor. Still, the hooks are there for you to insert your
+favorite library.
+<HR>
+SHAR_EOF
+ $shar_touch -am 1019203898 'page21.pre' &&
+ chmod 0664 'page21.pre' ||
+ $echo 'restore of' 'page21.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 'page21.pre:' 'MD5 check failed'
+7f0f64452098cdef38c5496340a4b6c7 page21.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page21.pre'`"
+ test 151 -eq "$shar_count" ||
+ $echo 'page21.pre:' 'original size' '151,' 'current size' "$shar_count!"
+ fi
+fi
+# ============= page22.pre ==============
+if test -f 'page22.pre' && test "$first_param" != -c; then
+ $echo 'x -' SKIPPING 'page22.pre' '(file already exists)'
+else
+ $echo 'x -' extracting 'page22.pre' '(text)'
+ sed 's/^X//' << 'SHAR_EOF' > 'page22.pre' &&
+Well, this has certainly been one of the more verbose tutorials to
+date. I must say "thanks" to everyone who stuck it out this far!
+<P>
+A quick review of what we've done:
+<UL>
+X
+<LI>Create a simple client application and Client object that uses a
+Protocol Stream without really knowing how they work. The app (and
+object) rely on the public interface of the Protocol Stream to get the
+job done. At this level the protocol details are irrelevant.
+<P>
+<LI>Next, we create a simple server application and Server object
+similar to the client. The Protocol Stream is of course used and we
+have to know a little more so that we can insert a <i>reader</i> that
+will ultimately process the data from the client.
+<P>
+<LI>We then go into the details of the Protocol_Stream implementation
+and it's Protocol_Task object that forms the basis for the stream
+tasks. Each object is kept as small and simple as possible to improve
+reusability and future maintenance.
+<P>
+<LI>Finally, the individual protocol objects are discused. Separate
+objects for the peer interface were created as well as the bogus
+compressor and encryptor. The protocol can be extended or modified by
+creating new such objects and installing them in the Protocol_Stream's
+open() method.
+X
+</UL>
+<P>
+X
+It doesn't sound like much but it certainly took a bunch of files to
+get there. It's easy to get lost in the details when there's so much
+to cover so you're encouraged to go over things a couple of times.
+As always, enhancments of the tutorials is welcome!
+<P>
+Here's the complete file list:
+<UL>
+<LI><A HREF="client">Makefile</A>
+<P>
+<LI><A HREF="Makefile.client">client Makefile</A>
+<LI><A HREF="client.cpp">client.cpp</A>
+<LI><A HREF="Client.h">Client.h</A>
+<LI><A HREF="Client.cpp">Client.cpp</A>
+<P>
+<LI><A HREF="Makefile.server">Server Makefile</A>
+<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>
+<P>
+<LI><A HREF="Protocol_Stream.cpp">Protocol_Stream.cpp</A>
+<LI><A HREF="Protocol_Stream.h">Protocol_Stream.h</A>
+<LI><A HREF="Protocol_Task.cpp">Protocol_Task.cpp</A>
+<LI><A HREF="Protocol_Task.h">Protocol_Task.h</A>
+<P>
+<LI><A HREF="Xmit.cpp">Xmit.cpp</A>
+<LI><A HREF="Xmit.h">Xmit.h</A>
+<LI><A HREF="Recv.cpp">Recv.cpp</A>
+<LI><A HREF="Recv.h">Recv.h</A>
+<P>
+<LI><A HREF="Compressor.cpp">Compressor.cpp</A>
+<LI><A HREF="Compressor.h">Compressor.h</A>
+<LI><A HREF="Crypt.cpp">Crypt.cpp</A>
+<LI><A HREF="Crypt.h">Crypt.h</A>
+</UL>
+SHAR_EOF
+ $shar_touch -am 1019204198 'page22.pre' &&
+ chmod 0664 'page22.pre' ||
+ $echo 'restore of' 'page22.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 'page22.pre:' 'MD5 check failed'
+448ab5a481b51ec392aaac814a0bd005 page22.pre
+SHAR_EOF
+ else
+ shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page22.pre'`"
+ test 2551 -eq "$shar_count" ||
+ $echo 'page22.pre:' 'original size' '2551,' 'current size' "$shar_count!"
fi
fi
# ============= page04.pst ==============
@@ -726,6 +938,7 @@ X followed by an equally simple Client object.
<P>
For a quick look back:
<UL>
+<LI><A HREF="Makefile.client">client Makefile</A>
<LI><A HREF="client.cpp">client.cpp</A>
<LI><A HREF="Client.h">Client.h</A>
<LI><A HREF="Client.cpp">Client.cpp</A>
@@ -733,19 +946,19 @@ 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 1019163798 'page04.pst' &&
+ $shar_touch -am 1019202198 'page04.pst' &&
chmod 0664 'page04.pst' ||
$echo 'restore of' 'page04.pst' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo 'page04.pst:' 'MD5 check failed'
-67b8e000792dd12883f8c89c09f14f13 page04.pst
+8fcb88614fd879307c71cea1d53e276f page04.pst
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page04.pst'`"
- test 348 -eq "$shar_count" ||
- $echo 'page04.pst:' 'original size' '348,' 'current size' "$shar_count!"
+ test 398 -eq "$shar_count" ||
+ $echo 'page04.pst:' 'original size' '398,' 'current size' "$shar_count!"
fi
fi
# ============= page09.pst ==============
@@ -763,23 +976,23 @@ 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="Makefile.server">Server Makefile</A>
<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' &&
+ $shar_touch -am 1019202198 '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
+22cfa1a8dadd48fccc7ed0c82aeba842 page09.pst
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'page09.pst'`"
@@ -787,5 +1000,5 @@ SHAR_EOF
$echo 'page09.pst:' 'original size' '609,' 'current size' "$shar_count!"
fi
fi
-rm -fr _sh23762
+rm -fr _sh02329
exit 0
diff --git a/docs/tutorials/015/page04.html b/docs/tutorials/015/page04.html
index 3835dcffb39..5bd479f186e 100644
--- a/docs/tutorials/015/page04.html
+++ b/docs/tutorials/015/page04.html
@@ -85,6 +85,7 @@ Ok, that's it for the client. We've seen a very simple main()
<P>
For a quick look back:
<UL>
+<LI><A HREF="Makefile.client">client Makefile</A>
<LI><A HREF="client.cpp">client.cpp</A>
<LI><A HREF="Client.h">Client.h</A>
<LI><A HREF="Client.cpp">Client.cpp</A>
diff --git a/docs/tutorials/015/page09.html b/docs/tutorials/015/page09.html
index 6a842298010..e714c7334cf 100644
--- a/docs/tutorials/015/page09.html
+++ b/docs/tutorials/015/page09.html
@@ -213,12 +213,12 @@ 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="Makefile.server">Server Makefile</A>
<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%">
diff --git a/docs/tutorials/015/page14.html b/docs/tutorials/015/page14.html
index 2478f40606e..36e0b02f932 100644
--- a/docs/tutorials/015/page14.html
+++ b/docs/tutorials/015/page14.html
@@ -12,21 +12,15 @@
<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.
+The Xmit object knows how to send data to the peer. It sits at the
+tail of the stream and gets everything that flows down from the head.
+In keeping with the spirit of things, this object does only one thing
+and doesn't concern itself with anyone else' details.
<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.
+The only thing you might want to do is combine it with Recv. Why?
+As you'll realize in a page or two, the Xmit and Recv objects must
+interact if you're going to ensure a safe transit. By having a single
+object it's easier to coordinate and maintain the interaction.
<HR>
<PRE>
diff --git a/docs/tutorials/015/page15.html b/docs/tutorials/015/page15.html
index cae5a12f871..708e19b7183 100644
--- a/docs/tutorials/015/page15.html
+++ b/docs/tutorials/015/page15.html
@@ -12,18 +12,21 @@
<P>
<HR WIDTH="100%">
-Recv is the sibling to Xmit. Again, they could be combined into a
-single object if you want.
+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>
-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.
+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>
diff --git a/docs/tutorials/015/page16.html b/docs/tutorials/015/page16.html
index 75ca714d7dd..0ff4b76ec8a 100644
--- a/docs/tutorials/015/page16.html
+++ b/docs/tutorials/015/page16.html
@@ -12,10 +12,18 @@
<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.
+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>
diff --git a/docs/tutorials/015/page17.html b/docs/tutorials/015/page17.html
index f563bfabd9a..d506014a77d 100644
--- a/docs/tutorials/015/page17.html
+++ b/docs/tutorials/015/page17.html
@@ -12,8 +12,10 @@
<P>
<HR WIDTH="100%">
-This and the next three pages present the protocol objects that
-provide compression and encryption. If you were hoping to
+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>
@@ -103,4 +105,4 @@ int <font color=#008888>Recv::recv</font>(ACE_Message_Block * message, ACE_Time_
}
</PRE>
<P><HR WIDTH="100%">
-<CENTER>[<A HREF="..">Tutorial Index</A>] </CENTER>
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page18.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page18.html b/docs/tutorials/015/page18.html
new file mode 100644
index 00000000000..7f5603946b8
--- /dev/null
+++ b/docs/tutorials/015/page18.html
@@ -0,0 +1,68 @@
+<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 learn the
+secrets of compression and encryption then I'm going to disappoint
+you. There are some really good libraries out there that do this
+stuff though and if anyone wants to integrate one of them into the
+tutorial I'll be glad to take it!
+<HR>
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#ifndef</font> <font color=purple>COMPRESSOR_H</font>
+<font color=blue>#define</font> <font color=purple>COMPRESSOR_h</font>
+
+<font color=blue>#include</font> "<font color=green>Protocol_Task.h</font>"
+
+<font color=red>/* A reallly dumb compression object. (It actually adds 3 bytes to
+ every message block.)
+*/</font>
+class Compressor : public Protocol_Task
+{
+public:
+
+ typedef Protocol_Task inherited;
+
+ <font color=red>// I've given you the option of creating this task derivative</font>
+ <font color=red>// with a number of threads. In retro-spect that really isn't </font>
+ <font color=red>// a good idea. Most client/server systems rely on requests</font>
+ <font color=red>// and responses happening in a predicatable order. Introduce </font>
+ <font color=red>// a thread pool and message queue and that ordering goes</font>
+ <font color=red>// right out the window. In other words: Don't ever use the</font>
+ <font color=red>// constructor parameter!</font>
+ Compressor( int _thr_count = 0 );
+
+ ~Compressor(void);
+
+protected:
+
+ <font color=red>// This is called when the compressor is on the downstream</font>
+ <font color=red>// side. We'll take the message, compress it and move it</font>
+ <font color=red>// along to the next module.</font>
+ int send(ACE_Message_Block *message,
+ ACE_Time_Value *timeout);
+
+ <font color=red>// This one is called on the upstream side. No surprise: we</font>
+ <font color=red>// decompress the data and send it on up the stream.</font>
+ int recv(ACE_Message_Block *message,
+ ACE_Time_Value *timeout);
+};
+
+<font color=blue>#endif</font> <font color=red>// COMPRESSOR_H</font>
+</PRE>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page19.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page19.html b/docs/tutorials/015/page19.html
new file mode 100644
index 00000000000..73103ff15bf
--- /dev/null
+++ b/docs/tutorials/015/page19.html
@@ -0,0 +1,119 @@
+<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%">
+Here we implement the details of our compression. By having both
+compression and decompression in one object it's easier to keep track
+of implementation details. Splitting Xmit/Recv like I did will make
+things more difficult if something has to change in their interaction.
+<HR>
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#include</font> "<font color=green>Compressor.h</font>"
+<font color=blue>#include</font> "<font color=green>ace/SOCK_Stream.h</font>"
+
+<font color=red>/* Construct our baseclass with the proper thread count. I really
+ should remove this option...
+ */</font>
+<font color=#008888>Compressor::Compressor</font>( int _thr_count )
+ : inherited(_thr_count)
+{
+ ;
+}
+
+<font color=#008888>Compressor::~Compressor</font>(void)
+{
+ ;
+}
+
+<font color=red>/* This is where you insert your compression code. Most compressors
+ want to work on a block of data instead of a byte-stream.
+ Fortunately the message block has a block that can be compressed.
+ Take a look at libz for a quick way to add compression to your
+ apps
+ */</font>
+int <font color=#008888>Compressor::send</font>(ACE_Message_Block *message, ACE_Time_Value *timeout)
+{
+ ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t) <font color=#008888>Compressor::send</font>() compressing (%s)\n</font>", message->rd_ptr() ));
+
+ <font color=red>// Create a block to hold the compressed data. I belive libz</font>
+ <font color=red>// recommends a buffer about 10-20% larger than the source.</font>
+ <font color=red>// Other libraries/algorithms may have their own quirks.</font>
+ ACE_Message_Block * compressed = new ACE_Message_Block( message->size() );
+
+ <font color=red>// Perform a bogus compression algorithm. 'CD' just tells me</font>
+ <font color=red>// that this is compressed data and when we "<font color=green>decompress</font>" we'll </font>
+ <font color=red>// look for this signature to validate the data received.</font>
+ <font color=#008888>ACE_OS::sprintf</font>( compressed->wr_ptr(), "<font color=green>CD:%s</font>", message->rd_ptr() );
+ compressed->wr_ptr( strlen(compressed->wr_ptr())+1 );
+
+ <font color=red>// Send the compressed data down the stream to the next module</font>
+ this->put_next( compressed );
+
+ <font color=red>// We're done here.</font>
+ message->release();
+
+ return( 0 );
+}
+
+<font color=red>/* And here's the decompression side. We've written Xmit/Recv so that
+ we're guaranteed to get an entire block of compressed data. If
+ we'd used recv() in the Recv object then we might have gotten a
+ partial block and that may not decompress very nicely.
+ */</font>
+int <font color=#008888>Compressor::recv</font>(ACE_Message_Block *message, ACE_Time_Value *timeout)
+{
+ ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t) <font color=#008888>Compress::recv</font>() decompressing (%s)\n</font>", message->rd_ptr() ));
+
+ <font color=red>// Room for the decompressed data. In the real world you</font>
+ <font color=red>// would probably want to send the original (uncompressed)</font>
+ <font color=red>// data size in the message. You can predict the maximum</font>
+ <font color=red>// possible decompression size but it's cheap and easy just to </font>
+ <font color=red>// send that along. Look again at how I do exacly that</font>
+ <font color=red>// between Xmit and Recv.</font>
+ ACE_Message_Block * decompressed = new ACE_Message_Block( message->size() );
+
+ <font color=red>// Check for our signature. Even when you use a real</font>
+ <font color=red>// compression algorithm you may want to include your own</font>
+ <font color=red>// signature so that you can verify the block. It pays to be</font>
+ <font color=red>// paranoid!</font>
+ if( <font color=#008888>ACE_OS::strncmp</font>( message->rd_ptr(), "<font color=green>CD:</font>", 3 ) )
+ {
+ ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t) Improperly encompressed data.\n</font>" ));
+ message->release();
+ return(-1);
+ }
+
+ <font color=red>// Skip past the signature before going any further.</font>
+ message->rd_ptr( 3 );
+
+ <font color=red>// Perform a bogus decompression algorithm. This is where you </font>
+ <font color=red>// would feed to libz or your favorite decompressor. (It's</font>
+ <font color=red>// costly but you could invoke popen() on gzip!)</font>
+ <font color=#008888>ACE_OS::sprintf</font>( decompressed->wr_ptr(), "<font color=green>%s</font>", message->rd_ptr() );
+ decompressed->wr_ptr( strlen(decompressed->wr_ptr())+1 );
+
+ <font color=red>// Recv the decompressed data down the stream to the next module</font>
+ this->put_next( decompressed );
+
+ <font color=red>// We're done here.</font>
+ message->release();
+
+ return( 0 );
+}
+
+</PRE>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page20.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page20.html b/docs/tutorials/015/page20.html
new file mode 100644
index 00000000000..6f81ebfa5ec
--- /dev/null
+++ b/docs/tutorials/015/page20.html
@@ -0,0 +1,61 @@
+<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%">
+While I might be able to come up with a competitive compressor, I
+don't have a snowball's chance to code up encryption. I'd be better
+off piping the data through the standard Unix crypt command.
+<P>
+So, while I was lazy with Compress, I'm realistic with Crypt. I'll
+show you the hooks and entry points and let someone else contribute an
+encryptor.
+<HR>
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#ifndef</font> <font color=purple>CRYPT_H</font>
+<font color=blue>#define</font> <font color=purple>CRYPT_h</font>
+
+<font color=blue>#include</font> "<font color=green>Protocol_Task.h</font>"
+
+<font color=red>/* An interface (adaptor) between your favorite encryption method and
+ an ACE_Stream.
+*/</font>
+class Crypt : public Protocol_Task
+{
+public:
+
+ typedef Protocol_Task inherited;
+
+ <font color=red>// Again we have the option of multiple threads and again I</font>
+ <font color=red>// regret tempting folks to use it.</font>
+ Crypt( int _thr_count = 0 );
+
+ ~Crypt(void);
+
+protected:
+
+ <font color=red>// Moving downstream will encrypt the data</font>
+ int send(ACE_Message_Block *message,
+ ACE_Time_Value *timeout);
+
+ <font color=red>// And moving upstream will decrypt it.</font>
+ int recv(ACE_Message_Block *message,
+ ACE_Time_Value *timeout);
+};
+
+<font color=blue>#endif</font> <font color=red>// CRYPT_H</font>
+</PRE>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page21.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page21.html b/docs/tutorials/015/page21.html
new file mode 100644
index 00000000000..5b017dcf609
--- /dev/null
+++ b/docs/tutorials/015/page21.html
@@ -0,0 +1,99 @@
+<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 encryption implementation isn't any smarter than that of the
+compressor. Still, the hooks are there for you to insert your
+favorite library.
+<HR>
+<PRE>
+
+<font color=red>// $Id$</font>
+
+<font color=blue>#include</font> "<font color=green>Crypt.h</font>"
+<font color=blue>#include</font> "<font color=green>ace/SOCK_Stream.h</font>"
+
+<font color=red>/* The expected constructor...
+ */</font>
+<font color=#008888>Crypt::Crypt</font>( int _thr_count )
+ : inherited(_thr_count)
+{
+}
+
+<font color=#008888>Crypt::~Crypt</font>(void)
+{
+}
+
+<font color=red>/* To send the data we'll apply a signature and encryption.
+ */</font>
+int <font color=#008888>Crypt::send</font>(ACE_Message_Block *message, ACE_Time_Value *timeout)
+{
+ ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t) <font color=#008888>Crypt::send</font>() encrypting (%s)\n</font>", message->rd_ptr() ));
+
+ <font color=red>// I suspect that some encryptors might change the data size.</font>
+ <font color=red>// It probably isn't safe to create a same-size destination buffer.</font>
+ ACE_Message_Block * encrypted = new ACE_Message_Block( message->size() );
+
+ <font color=red>// Perform a bogus encryption algorithm and add our safety</font>
+ <font color=red>// signature. Adding the original data size is also probably</font>
+ <font color=red>// a good idea that I haven't encorporated here.</font>
+ <font color=#008888>ACE_OS::sprintf</font>( encrypted->wr_ptr(), "<font color=green>ED:%s</font>", message->rd_ptr() );
+ encrypted->wr_ptr( strlen(encrypted->wr_ptr())+1 );
+
+ <font color=red>// Send the encrypted data down the stream to the next module</font>
+ this->put_next( encrypted );
+
+ <font color=red>// We're done here.</font>
+ message->release();
+
+ return( 0 );
+}
+
+<font color=red>/* The upstream movement requires that we decrypt what the peer has
+ given us.
+*/</font>
+int <font color=#008888>Crypt::recv</font>(ACE_Message_Block *message, ACE_Time_Value *timeout)
+{
+ ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t) <font color=#008888>Crypt::recv</font>() decrypting (%s)\n</font>", message->rd_ptr() ));
+
+ <font color=red>// Create a destination for the decrypted data. The same</font>
+ <font color=red>// block size caveat exists of course.</font>
+ ACE_Message_Block * decrypted = new ACE_Message_Block( message->size() );
+
+ <font color=red>// Check the signature as expected.</font>
+ if( <font color=#008888>ACE_OS::strncmp</font>( message->rd_ptr(), "<font color=green>ED:</font>", 3 ) )
+ {
+ ACE_DEBUG ((LM_INFO, "<font color=green>(%P|%t) Improperly encrypted data.\n</font>" ));
+ message->release();
+ return(-1);
+ }
+
+ <font color=red>// Don't forget to skip past the signature before decrypting</font>
+ <font color=red>// or things will be quite exciting!</font>
+ message->rd_ptr( 3 );
+
+ <font color=red>// Perform a bogus decryption algorithm</font>
+ <font color=#008888>ACE_OS::sprintf</font>( decrypted->wr_ptr(), "<font color=green>%s</font>", message->rd_ptr() );
+ decrypted->wr_ptr( strlen(decrypted->wr_ptr())+1 );
+
+ <font color=red>// Send the decrypted data down the stream to the next module</font>
+ this->put_next( decrypted );
+
+ <font color=red>// We're done here.</font>
+ message->release();
+
+ return( 0 );
+}
+</PRE>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] [<A HREF="page22.html">Continue This Tutorial</A>]</CENTER>
diff --git a/docs/tutorials/015/page22.html b/docs/tutorials/015/page22.html
new file mode 100644
index 00000000000..90843ab2d31
--- /dev/null
+++ b/docs/tutorials/015/page22.html
@@ -0,0 +1,82 @@
+<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%">
+Well, this has certainly been one of the more verbose tutorials to
+date. I must say "thanks" to everyone who stuck it out this far!
+<P>
+A quick review of what we've done:
+<UL>
+
+<LI>Create a simple client application and Client object that uses a
+Protocol Stream without really knowing how they work. The app (and
+object) rely on the public interface of the Protocol Stream to get the
+job done. At this level the protocol details are irrelevant.
+<P>
+<LI>Next, we create a simple server application and Server object
+similar to the client. The Protocol Stream is of course used and we
+have to know a little more so that we can insert a <i>reader</i> that
+will ultimately process the data from the client.
+<P>
+<LI>We then go into the details of the Protocol_Stream implementation
+and it's Protocol_Task object that forms the basis for the stream
+tasks. Each object is kept as small and simple as possible to improve
+reusability and future maintenance.
+<P>
+<LI>Finally, the individual protocol objects are discused. Separate
+objects for the peer interface were created as well as the bogus
+compressor and encryptor. The protocol can be extended or modified by
+creating new such objects and installing them in the Protocol_Stream's
+open() method.
+
+</UL>
+<P>
+
+It doesn't sound like much but it certainly took a bunch of files to
+get there. It's easy to get lost in the details when there's so much
+to cover so you're encouraged to go over things a couple of times.
+As always, enhancments of the tutorials is welcome!
+<P>
+Here's the complete file list:
+<UL>
+<LI><A HREF="client">Makefile</A>
+<P>
+<LI><A HREF="Makefile.client">client Makefile</A>
+<LI><A HREF="client.cpp">client.cpp</A>
+<LI><A HREF="Client.h">Client.h</A>
+<LI><A HREF="Client.cpp">Client.cpp</A>
+<P>
+<LI><A HREF="Makefile.server">Server Makefile</A>
+<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>
+<P>
+<LI><A HREF="Protocol_Stream.cpp">Protocol_Stream.cpp</A>
+<LI><A HREF="Protocol_Stream.h">Protocol_Stream.h</A>
+<LI><A HREF="Protocol_Task.cpp">Protocol_Task.cpp</A>
+<LI><A HREF="Protocol_Task.h">Protocol_Task.h</A>
+<P>
+<LI><A HREF="Xmit.cpp">Xmit.cpp</A>
+<LI><A HREF="Xmit.h">Xmit.h</A>
+<LI><A HREF="Recv.cpp">Recv.cpp</A>
+<LI><A HREF="Recv.h">Recv.h</A>
+<P>
+<LI><A HREF="Compressor.cpp">Compressor.cpp</A>
+<LI><A HREF="Compressor.h">Compressor.h</A>
+<LI><A HREF="Crypt.cpp">Crypt.cpp</A>
+<LI><A HREF="Crypt.h">Crypt.h</A>
+</UL>
+<P><HR WIDTH="100%">
+<CENTER>[<A HREF="..">Tutorial Index</A>] </CENTER>
diff --git a/docs/tutorials/index.html b/docs/tutorials/index.html
index 1ba3fbcc63a..bbdd6e350e7 100644
--- a/docs/tutorials/index.html
+++ b/docs/tutorials/index.html
@@ -93,6 +93,19 @@ A word about ACE_Message_Queue</H4>
<LI>
<A HREF="013/page01.html">Task chains and state machines</A></LI>
</OL>
+
+<P><HR WIDTH="50%" align=left><P>
+
+<H4>
+Paddling down (and up) the ACE_Stream</H4>
+
+<OL>
+<LI>
+<A HREF="014/page01.html">ACE_Stream Tutorial, Of Sorts</A></LI>
+<LI>
+<A HREF="015/page01.html">A certain amount of Protocol is required!</A></LI>
+</OL>
+
<HR>
<P>Back to the <A