1 //=============================================================================
3 * @file IOStream_Test.cpp
5 * This is a simple test of the IOStream class that illustrates
6 * how to use iostream operations on almost arbitrary I/O classes.
8 * @author James CE Johnson <jcej@lads.com>
10 //=============================================================================
13 #include "test_config.h"
14 #include "ace/Thread.h"
15 #include "ace/Acceptor.h"
16 #include "ace/SOCK_Connector.h"
17 #include "ace/SOCK_Acceptor.h"
18 #include "ace/IOStream.h"
19 #include "ace/OS_NS_sys_wait.h"
21 #if !defined (ACE_LACKS_ACE_IOSTREAM)
22 # include "ace/OS_NS_unistd.h"
23 # include "ace/OS_NS_ctype.h" // Needed for isspace() function
25 typedef ACE_IOStream
<ACE_SOCK_Stream
> ACE_SOCK_IOStream
;
27 /* The biggest drawback to an iostream is that it generally
28 eats up whitespace when performing a get (>>) operation.
30 That may be good if you're reading non-textual data but
31 if you're trying to read a stream of words with embedded
32 whitespace, it isn't going to be pleasant.
34 If you've been blessed with the GNU String class, I've
35 already provided a derived class, QuotedString, that
36 makes dealing with strings very easy.
38 If you're stuck with an array of characters then you
39 will probably need somthing like I have below.
41 On the other hand, one of the biggest advantages to an
42 iostream is that it eats up whitespace :-)
44 If you put (<<) your non-textual data to the iostream
45 with any number of whitespace between the data then
46 you can easily get (>>) the data from the iostream
47 without having to worry about delimeters and such.
49 The main thing to keep in mind when using an iostream
50 between peers is that you MUST keep the data "fields"
51 in sync. That is, if the "putter" puts an int followed
52 by a float followed by a double, you must make sure
53 that the "getter" will be attempting to get an int
54 then a float then a double.
57 // Since I can't rely on GNU's String class being everywhere (yet),
58 // here's a simple class that will work with quoted strings. Use at
59 // your own risk! It is very incomplete!
64 qchar (void) { c_
= '\0'; }
66 qchar (char c
) : c_ (c
) { };
68 operator char () const { return c_
; }
70 qchar
operator= (char c
) { return c_
= c
; }
72 bool operator== (char c
) { return c_
== c
; }
74 friend ACE_SOCK_IOStream
&operator>> (ACE_SOCK_IOStream
& stream
, qchar
* buf
);
75 friend ACE_SOCK_IOStream
&operator<< (ACE_SOCK_IOStream
& stream
, qchar
* buf
);
76 friend ostream
&operator<< (ostream
& stream
, qchar
* buf
);
82 // This is taken almost directly from the QuotedString object that has
83 // been derived from GNU's String class. The advantage to using
84 // QuotedString is that it is MUCH more robust than qchar will every
88 operator>> (ACE_SOCK_IOStream
& stream
, qchar
*buf
)
92 *buf
= '\0'; // Initialize the string
96 if (!stream
) // eat space up to the first char
99 // if we don't have a quote, append until we see space
102 (void *) stream
.get (c
) && !ACE_OS::ace_isspace (c
);
106 for (; (void *) stream
.get (c
) && c
!= '"'; *buf
++ = c
)
120 operator<< (ACE_SOCK_IOStream
&stream
, qchar
*buf
)
129 stream
.put ((char) *buf
++);
138 operator<< (ostream
&stream
, qchar
*buf
)
141 stream
.put ((char) *buf
++);
146 // Our client thread will initiate the test by sending some data to
150 client (void *arg
= 0)
152 ACE_UNUSED_ARG (arg
);
154 // We don't _need_ to dynamically allocate the ACE_SOCK_IOStream.
155 // But if we don't, it doesn't get destroyed on some platforms,
156 // e.g., g++ 2.7.2.1 and Sun C++ 4.2 on Solaris 2.5.1. (It does work
157 // on Linux, so the code seems fine.) If we manage the storage
158 // ourselves, we _will_ destroy it at the end of this function.
159 ACE_SOCK_IOStream server
;
161 ACE_INET_Addr
*remote_addr
= (ACE_INET_Addr
*) arg
;
162 ACE_INET_Addr
addr (remote_addr
->get_port_number (),
163 ACE_DEFAULT_SERVER_HOST
);
164 ACE_SOCK_Connector connector
;
166 if (connector
.connect (server
, addr
) == -1)
167 ACE_ERROR_RETURN ((LM_ERROR
,
168 ACE_TEXT (" (%t) %p\n"),
169 ACE_TEXT ("Failed to connect to server thread")),
172 // Send a string to the server which it can interpret as a qchar[]
173 const char *str
= "\"This is a test string.\"";
174 ACE_DEBUG ((LM_DEBUG
, ACE_TEXT (" (%P|%t) Client Sending: (%C)\n"),
176 server
<< str
<< endl
;
178 // Allow the server to get the string and echo it to the user. (The
179 // iostream doesn't need this, but humans do :)
182 // Send another string but this time the server will read it as a
183 // char[]. Notice how the server's output doesn't include all of
184 // the spaces sent by the client.
186 str
= "\"THIS IS A TEST STRING.\"";
187 ACE_DEBUG ((LM_DEBUG
,
188 ACE_TEXT (" (%P|%t) Client Sending: (%s)\n"),
190 server
<< str
<< endl
;
192 // Again, give the server time to display the happenings to the
196 // Read from the server an int, float, long, float double. The
197 // iostream will pull them out by using the whitespace provided by
200 ACE_DEBUG ((LM_DEBUG
,
201 ACE_TEXT (" (%P|%t) Client Receiving\n")));
203 ACE_Time_Value
timeout (2);
204 ACE_Time_Value
*timeoutp
= &timeout
;
213 while (! (server
>> i
))
215 int eof
= server
.eof ();
218 ACE_DEBUG ((LM_DEBUG
,
219 ACE_TEXT (" (%P|%t) Unrecoverable stream error/eof\n")));
224 ACE_DEBUG ((LM_DEBUG
,
225 ACE_TEXT (" (%P|%t) Recoverable stream error/timed out)\n")));
235 ACE_DEBUG ((LM_DEBUG
,
236 ACE_TEXT (" (%P|%t) Client Received: int %d float %f long %d float %f double %f\n"),
243 // Check for proper received values.
244 ACE_TEST_ASSERT (i
== 1 && (f1
>= 0.123420 && f1
<= 0.123422)
245 && l
== 666555444 && (f2
>= 23.44 && f2
<= 23.46)
246 && (d
>= -47.1e+9 && d
<= -45.9e+9));
247 // Reset the precision to limit ourselves to two significant digits.
248 server
.precision (2);
250 // Now, make a little change & send 'em back.
251 i
*= -1; server
<< i
<< " ";
252 f1
*= -1.0; server
<< f1
<< " ";
253 l
*= -1; server
<< l
<< " ";
254 f2
*= -1.0; server
<< f2
<< " ";
255 d
*= -1; server
<< d
<< " ";
258 // Shut down the test.
264 // Test the server's ability to receive data from the client and then
265 // begin a two-way conversation.
268 server (void *arg
= 0)
270 // We don't _need_ to dynamically allocate the ACE_SOCK_IOStream.
271 // But if we don't, it doesn't get destroyed on some platforms,
272 // e.g., g++ 2.7.2.1 and Sun C++ 4.2 on Solaris 2.5.1. (It does work
273 // on Linux, so the code seems fine.) If we manage the storage
274 // ourselves, we _will_ destroy it at the end of this function.
275 ACE_SOCK_IOStream client_handler
;
277 ACE_INET_Addr server_addr
;
278 ACE_SOCK_Acceptor
*acceptor
=
279 reinterpret_cast<ACE_SOCK_Acceptor
*> (arg
);
281 if (acceptor
->get_local_addr (server_addr
) == -1)
282 ACE_ERROR_RETURN ((LM_ERROR
,
284 ACE_TEXT ("get_local_addr")),
287 #if defined (ACE_HAS_THREADS)
288 if (ACE_Thread_Manager::instance ()->spawn (ACE_THR_FUNC (client
),
289 (void *) &server_addr
,
290 THR_NEW_LWP
| THR_DETACHED
) == -1)
291 ACE_ERROR_RETURN ((LM_ERROR
,
292 ACE_TEXT (" (%t) %p\n"),
293 ACE_TEXT ("spawing client thread")),
295 #endif /* ACE_HAS_THREADS */
297 if (acceptor
->accept (client_handler
) == -1)
298 ACE_ERROR_RETURN ((LM_ERROR
,
299 ACE_TEXT (" (%P|%t) Failed to accept new client_handler")),
302 // Read a qbuf[] from the client. Notice that all of the client's
303 // whitespace is preserved.
305 ACE_OS::memset (qbuf
, 0, sizeof qbuf
);
306 client_handler
>> qbuf
;
308 ACE_DEBUG ((LM_DEBUG
,
309 ACE_TEXT (" (%P|%t) Server Received: (\"%C\")\n"),
312 // Give the client time to announce the next test to the user.
315 // Now we try to use a char[] to get a string from the client.
316 // Compared to the method above, this is quite messy. Notice also
317 // that whitespace is lost.
319 #if defined (ACE_HAS_STRING_CLASS) && defined (ACE_HAS_STANDARD_CPP_LIBRARY)
320 ACE_IOStream_String buf
;
321 ACE_DEBUG ((LM_DEBUG
,
322 " (%P|%t) Server Received: ("));
324 while (client_handler
&&
325 (buf
.length () == 0 || buf
[buf
.length () - 1] != '"'))
327 if (! (client_handler
>> buf
))
330 if (buf
.length () > 0)
331 ACE_DEBUG ((LM_DEBUG
,
336 ACE_DEBUG ((LM_DEBUG
,
340 ACE_OS::memset (buf
, 0, sizeof buf
);
341 ACE_DEBUG ((LM_DEBUG
,
342 ACE_TEXT (" (%P|%t) Server Received: (")));
344 while (ACE_OS::strlen (buf
) == 0
345 || buf
[ACE_OS::strlen (buf
) - 1] != '"')
347 if (! (client_handler
>> buf
))
349 ACE_DEBUG ((LM_DEBUG
,
354 ACE_DEBUG ((LM_DEBUG
,
356 #endif /* ACE_HAS_STRING_CLASS */
358 // Send some non-textual data to the client. We use a single
359 // character to separate the fields but we could have used any valid
360 // whitespace. The data will be sent if the iostream's buffer gets
361 // filled or when we flush it with an explicit client.sync ()
362 // command or the implicit <<endl.
364 ACE_DEBUG ((LM_DEBUG
,
365 ACE_TEXT (" (%P|%t) Server sleeping\n")));
368 ACE_DEBUG ((LM_DEBUG
,
369 ACE_TEXT (" (%P|%t) Server Sending: 1 .12342134 666555444 23.45 -46.5e9\n")));
370 client_handler
<< 1 << " ";
371 client_handler
<< .12342134 << " ";
372 client_handler
<< 666555444 << " ";
373 client_handler
<< 23.45 << " ";
374 client_handler
<< -46.5e9
<< " ";
375 client_handler
<< endl
;
377 // The client will have changed the sign of each data field and sent
378 // 'em all back to us. At the same time, the client used the
379 // precision () function to change the significant digits for
380 // non-integer values.
385 client_handler
>> i
>> f1
>> l
>> f2
>> d
;
387 ACE_DEBUG ((LM_DEBUG
,
388 ACE_TEXT (" (%P|%t) Server Received: int %d float %f long %d float %f double %f\n"),
395 // check for proper received values
396 ACE_TEST_ASSERT (i
== -1 && (f1
>= -0.13 && f1
<= -0.11)
397 && l
== -666555444 && (f2
>= -24.0 && f2
<= -22.0)
398 && (d
>= 45e+9 && d
<= 47e+9));
400 client_handler
.close ();
409 ACE_SOCK_Acceptor acceptor
;
411 if (acceptor
.open (ACE_sap_any_cast (const ACE_INET_Addr
&)) == -1)
412 ACE_ERROR_RETURN ((LM_ERROR
,
413 ACE_TEXT (" (%P|%t) %p\n"),
416 #if defined (ACE_HAS_THREADS)
417 else if (ACE_Thread_Manager::instance ()->spawn (ACE_THR_FUNC (server
),
419 THR_NEW_LWP
| THR_DETACHED
) == -1)
420 ACE_ERROR_RETURN ((LM_ERROR
,
422 ACE_TEXT ("spawning server thread")),
425 // Wait for the client and server thread to exit.
426 ACE_Thread_Manager::instance ()->wait ();
428 #elif !defined (ACE_LACKS_FORK)
430 switch (ACE_OS::fork ("child"))
433 ACE_ERROR ((LM_ERROR
,
435 ACE_TEXT ("fork failed")));
439 ACE_APPEND_LOG ("IOStream_Test-children");
440 ACE_INET_Addr server_addr
;
442 if (acceptor
.get_local_addr (server_addr
) == -1)
443 ACE_ERROR ((LM_ERROR
,
445 ACE_TEXT ("get_local_addr")));
447 client ((void *) &server_addr
);
451 default: // In parent
454 // Allow the client to exit, then remove the Process_Mutex.
459 ACE_ERROR_RETURN ((LM_INFO
,
460 ACE_TEXT ("threads *and* processes not supported on this platform\n")),
462 #endif /* ACE_HAS_THREADS */
468 #endif /* !ACE_LACKS_ACE_IOSTREAM */
471 run_main (int, ACE_TCHAR
*[])
473 ACE_START_TEST (ACE_TEXT ("IOStream_Test"));
475 #if !defined (ACE_LACKS_ACE_IOSTREAM)
476 ACE_INIT_LOG (ACE_TEXT ("IOStream_Test-children"));
480 ACE_TEXT ("ACE_IOSTREAM not supported on this platform\n")));
481 #endif /* !ACE_LACKS_ACE_IOSTREAM */