summaryrefslogtreecommitdiff
path: root/docs/tutorials/015/page09.html
blob: 558ba8ce8c3d51ef42f16ba09513cc71a954f997 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
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="Makefile.server">Server Makefile</A>
<LI><A HREF="server.cpp">server.cpp</A>
<LI><A HREF="Server_i.h">Server_i.h</A>
<LI><A HREF="Server_i.cpp">Server_i.cpp</A>
<LI><A HREF="Handler.h">Handler.h</A>
<LI><A HREF="Handler.cpp">Handler.cpp</A>
</UL>
<P>
<P><HR WIDTH="100%">
<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page10.html">Continue This Tutorial</A>]</CENTER>