ACE Tutorial 015
Building a protocol stream

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!

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.

*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!

Before we get into the code, here's a picture that's shows what's going on here.

    // page10.html,v 1.16 2003/08/19 15:08:26 schmidt Exp

    #ifndef PROTOCOL_STREAM_H
    #define PROTOCOL_STREAM_H

    #include "ace/SOCK_Stream.h"

    #if !defined (ACE_LACKS_PRAGMA_ONCE)
    # pragma once
    #endif /* ACE_LACKS_PRAGMA_ONCE */

    #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:
      Protocol_Stream (void);
      ~Protocol_Stream (void);

      // Provide the stream with an ACE_SOCK_Stream on which it can
      // communicate.  If _reader is non-null, it will be added as the
      // reader task just below the stream head so that it can process
      // data read from the peer.
      int open (ACE_SOCK_Stream &peer,
                Protocol_Task *reader = 0);

      // Close the stream.  All of the tasks & modules will also be
      // closed.
      int close (void);

      // putting data onto the stream will pass it through all protocol
      // levels and send it to the peer.
      int put (ACE_Message_Block *&message,
               ACE_Time_Value *timeout = 0);

      // get will cause the Recv task (at the tail of the stream) to read
      // some data from the peer and pass it upstream.  The message block
      // is then taken from the stream reader task's message queue.
      int get (ACE_Message_Block *&response,
               ACE_Time_Value *timeout = 0);

      // Tell the Recv task to read some data and send it upstream.  The
      // data will pass through the protocol tasks and be queued into the
      // stream head reader task's message queue.  If you've installed a
      // _reader in open() then that task's recv() method will see the
      // message and may consume it instead of passing it to the stream
      // head for queueing.
      int get (void);

      ACE_SOCK_Stream &peer (void)
      {
        return this->peer_;
      }

    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.
      // Note that this is only useful by client-side applications.
      Recv *recv_;

      Stream &stream (void)
      {
        return this->stream_;
      }

      // Install the protocol tasks into the stream.
      int open (void);
    };

    #endif /* PROTOCOL_STREAM_H */

[Tutorial Index] [Continue This Tutorial]