Merge pull request #1844 from jrw972/monterey
[ACE_TAO.git] / ACE / tests / IOStream_Test.cpp
blobda0b533132400b540420e68179492e4eb761cfc2
1 //=============================================================================
2 /**
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>
9 */
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!
61 class qchar
63 public:
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);
78 private:
79 char c_;
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
85 // be.
87 ACE_SOCK_IOStream &
88 operator>> (ACE_SOCK_IOStream & stream, qchar *buf)
90 char c;
92 *buf = '\0'; // Initialize the string
94 stream.get (c);
96 if (!stream) // eat space up to the first char
97 return stream;
99 // if we don't have a quote, append until we see space
100 if (c != '"')
101 for (*buf++ = c;
102 (void *) stream.get (c) && !ACE_OS::ace_isspace (c);
103 *buf++ = c)
104 continue;
105 else
106 for (; (void *) stream.get (c) && c != '"'; *buf++ = c)
107 if (c == '\\')
109 stream.get (c);
110 if (c != '"')
111 *buf++ = '\\';
114 *buf = '\0';
116 return stream;
119 ACE_SOCK_IOStream &
120 operator<< (ACE_SOCK_IOStream &stream, qchar *buf)
122 stream.put ('"');
124 while (*buf)
126 if (*buf == '"')
127 stream.put ('\\');
129 stream.put ((char) *buf++);
132 stream.put ('"');
134 return stream;
137 ostream &
138 operator<< (ostream &stream, qchar *buf)
140 while (*buf)
141 stream.put ((char) *buf++);
143 return stream;
146 // Our client thread will initiate the test by sending some data to
147 // the server.
149 static void *
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"),
175 str));
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 :)
180 ACE_OS::sleep (2);
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"),
189 str));
190 server << str << endl;
192 // Again, give the server time to display the happenings to the
193 // user.
194 ACE_OS::sleep (2);
196 // Read from the server an int, float, long, float double. The
197 // iostream will pull them out by using the whitespace provided by
198 // the server.
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;
206 server >> timeoutp;
208 int i;
209 float f1, f2;
210 long l;
211 double d;
213 while (! (server >> i))
215 int eof = server.eof ();
216 if (eof)
218 ACE_DEBUG ((LM_DEBUG,
219 ACE_TEXT (" (%P|%t) Unrecoverable stream error/eof\n")));
220 break;
222 else
224 ACE_DEBUG ((LM_DEBUG,
225 ACE_TEXT (" (%P|%t) Recoverable stream error/timed out)\n")));
226 server.clear (0);
230 server >> f1;
231 server >> l;
232 server >> f2;
233 server >> d;
235 ACE_DEBUG ((LM_DEBUG,
236 ACE_TEXT (" (%P|%t) Client Received: int %d float %f long %d float %f double %f\n"),
239 (int) l,
241 d));
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 << " ";
256 server << endl;
258 // Shut down the test.
259 server.close ();
261 return 0;
264 // Test the server's ability to receive data from the client and then
265 // begin a two-way conversation.
267 static void *
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,
283 ACE_TEXT ("%p\n"),
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.
304 qchar qbuf[BUFSIZ];
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"),
310 (char *) qbuf));
312 // Give the client time to announce the next test to the user.
313 ACE_OS::sleep (2);
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))
328 break;
330 if (buf.length () > 0)
331 ACE_DEBUG ((LM_DEBUG,
332 "%s ",
333 buf.c_str ()));
336 ACE_DEBUG ((LM_DEBUG,
337 ")\n"));
338 #else
339 char buf[BUFSIZ];
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))
348 break;
349 ACE_DEBUG ((LM_DEBUG,
350 ACE_TEXT ("%C "),
351 buf));
354 ACE_DEBUG ((LM_DEBUG,
355 ACE_TEXT (")\n")));
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")));
366 ACE_OS::sleep (5);
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.
381 int i;
382 float f1, f2;
383 long l;
384 double d;
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"),
391 (int) l,
393 d));
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 ();
402 return 0;
405 static int
406 spawn (void)
408 // Acceptor;
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"),
414 ACE_TEXT ("open")),
415 -1);
416 #if defined (ACE_HAS_THREADS)
417 else if (ACE_Thread_Manager::instance ()->spawn (ACE_THR_FUNC (server),
418 &acceptor,
419 THR_NEW_LWP | THR_DETACHED) == -1)
420 ACE_ERROR_RETURN ((LM_ERROR,
421 ACE_TEXT ("%p\n"),
422 ACE_TEXT ("spawning server thread")),
423 -1);
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"))
432 case -1:
433 ACE_ERROR ((LM_ERROR,
434 ACE_TEXT ("%p\n%a"),
435 ACE_TEXT ("fork failed")));
436 ACE_OS::_exit (-1);
437 case 0: // In child
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,
444 ACE_TEXT ("%p\n"),
445 ACE_TEXT ("get_local_addr")));
446 else
447 client ((void *) &server_addr);
448 ACE_END_LOG;
449 break;
451 default: // In parent
452 server (&acceptor);
454 // Allow the client to exit, then remove the Process_Mutex.
455 ACE_OS::wait ();
456 break;
458 #else
459 ACE_ERROR_RETURN ((LM_INFO,
460 ACE_TEXT ("threads *and* processes not supported on this platform\n")),
461 -1);
462 #endif /* ACE_HAS_THREADS */
464 acceptor.close ();
466 return 0;
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"));
477 spawn ();
478 #else
479 ACE_ERROR ((LM_INFO,
480 ACE_TEXT ("ACE_IOSTREAM not supported on this platform\n")));
481 #endif /* !ACE_LACKS_ACE_IOSTREAM */
482 ACE_END_TEST;
483 return 0;