feisty meow concerns codebase  2.140
test_ucast_spocket.cpp
Go to the documentation of this file.
1 /*
2 * Name : test_ucast_spocket
3 * Author : Chris Koeritz
4 * Purpose: This is the "main" program for our sockets tester. It parses command
5 * line parameters and starts up the tester class.
6 **
7 * Copyright (c) 2001-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 */
14 
15 #include "bcast_spocketer.h"
16 
18 #include <basis/byte_array.h>
19 #include <basis/astring.h>
24 #include <timely/time_control.h>
25 #include <unit_test/unit_base.h>
26 
27 #include <stdio.h>
28 #include <string.h>
29 
30 using namespace application;
31 using namespace basis;
32 using namespace loggers;
33 using namespace mathematics;
34 using namespace processes;
35 using namespace sockets;
36 using namespace structures;
37 using namespace textual;
38 using namespace timely;
39 using namespace unit_test;
40 
41 #define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print))
42 
43 const int INITIAL_DELAY = 2; // number of seconds before starting sends.
44 
46 
47 class test_ucast_spocket : public virtual application_shell, public virtual unit_base
48 {
49 public:
50  test_ucast_spocket() {}
51  DEFINE_CLASS_NAME("test_ucast_spocket");
52  virtual int execute();
53 
54  bool parse_address(const astring &input, ip_address_holder &ip_address);
55 };
56 
57 //hmmm: extract this to function in sockets.
58 bool test_ucast_spocket::parse_address(const astring &input, ip_address_holder &ip_address)
59 {
60  int index = 0; // current storage position in our array.
61  int current_byte = 0;
62  const char *last_period = 0; // helps check for non-empty numbers.
63  bool got_digit = false;
64  for (const char *address = input.s(); *address; address++) {
65  if ( (*address <= '9') && (*address >= '0') ) {
66  current_byte *= 10; // shift over.
67  current_byte += *address - '0'; // add in current character.
68  got_digit = true;
69  } else if (*address == '.') {
70  got_digit = false;
71  if (last_period + 1 == address) {
72  LOG("The IP address entry has an empty digit. Exiting.");
73  return false;
74  }
75  last_period = address; // set our last period location.
76  if (current_byte > 255) {
77  LOG("The IP address entry has an illegal abyte. Exiting.");
78  return false;
79  }
80  ip_address[index] = abyte(current_byte); // store current byte.
81  current_byte = 0; // reset.
82  index++; // next place in array.
83  if (index > 3) break;
84  // stop if there are too many periods, but keep accumulated address.
85  } else {
86  LOG("The IP address entry has illegal characters. Exiting.");
87  return false;
88  }
89  }
90  // catch the case where we ran out of chars before adding the last byte.
91  if ( (index == 3) && got_digit) {
92  if (current_byte > 255) {
93  LOG("The IP address entry has an illegal abyte. Exiting.");
94  return false;
95  }
96  ip_address[index] = current_byte;
97  } else if (index < 4) {
98  LOG("The IP address entry is too short. Exiting.");
99  return false;
100  }
101  return true;
102 }
103 
104 int test_ucast_spocket::execute()
105 {
106 //this preamble with command line parsing is identical to bcast version,
107 //and will be very close to regular spocket version.
108 //get it extracted to a helper class for use by all three.
109  FUNCDEF("execute");
110  ip_address_holder ip_address; // accumulates the source address.
111  ip_address_holder dest_addr; // where to send stuff to.
112  int rcv_port = 0;
113  int send_port = 0;
114  int send_size = 0;
115  int send_count = 0;
116 
117  const char *DEFAULT_HOST = "127.0.0.1";
118  const int DEFAULT_PORT = 12342;
119  const int DEFAULT_SEND_SIZE = 1008;
120  const int DEFAULT_SEND_COUNT = 10;
121 
122  if (_global_argc < 7) {
123  if (_global_argc > 1) {
124  LOG("\
125 This program takes six command line arguments to begin operation.\n\
126 These arguments (in order) are:\n\
127 \tIP address for src\tIn the form w.x.y.z\n\
128 \tIP address for dest\tIn the form w.x.y.z\n\
129 \tReceive Port number\tAs a short integer\n\
130 \tSending Port number\tAs a short integer\n\
131 \tSend size\t\tThe size of the data to exchange.\n\
132 \tSend count\t\tThe number of \"packets\" to exchange.\n\
133 Note: it is expected that the testers have equal send sizes; this\n\
134 allows the receiver to know when it's gotten all the data that's\n\
135 expected during a cycle.");
136  return 1; // bail if they provided anything; otherwise we test.
137  } else {
138  parse_address(DEFAULT_HOST, ip_address);
139  parse_address(DEFAULT_HOST, dest_addr);
140  rcv_port = DEFAULT_PORT;
141  send_port = DEFAULT_PORT + 1;
142  send_size = DEFAULT_SEND_SIZE;
143  send_count = DEFAULT_SEND_COUNT;
144  }
145  }
146 
147  // only parse the parameters if we got enough from the user; otherwise we accept our
148  // defaults to do a simple test run.
149  if (_global_argc >= 7) {
150 
151  if (!parse_address(_global_argv[1], ip_address)) {
152  LOG("failed to parse source address.");
153  return 9283;
154  }
155 
156  LOG(a_sprintf("\tParsed a source of: \"%d.%d.%d.%d\".",
157  (int)ip_address[0], (int)ip_address[1], (int)ip_address[2],
158  (int)ip_address[3]));
159 
160  if (!parse_address(_global_argv[2], dest_addr)) {
161  LOG("failed to parse dest address.");
162  return 9283;
163  }
164 
165  LOG(a_sprintf("\tParsed a destination of: \"%d.%d.%d.%d\".",
166  (int)dest_addr[0], (int)dest_addr[1], (int)dest_addr[2],
167  (int)dest_addr[3]));
168 
169  // parse the third parameter: the port.
170  if (sscanf(_global_argv[3], "%d", &rcv_port) < 1) {
171  LOG("The port entry is malformed. Exiting.");
172  return 3;
173  }
174  LOG(a_sprintf("\tGot a receive port of %d.", rcv_port));
175 
176  // parse the fourth parameter: the port.
177  if (sscanf(_global_argv[4], "%d", &send_port) < 1) {
178  LOG("The port entry is malformed. Exiting.");
179  return 3;
180  }
181  LOG(a_sprintf("\tGot a send port of %d.", send_port));
182 
183  // parse the fifth parameter: the size of the sends.
184  if (sscanf(_global_argv[5], "%d", &send_size) < 1) {
185  LOG("The send size entry is malformed. Exiting.");
186  return 5;
187  }
188  LOG(a_sprintf("\tGot a send size of %d.", send_size));
189 
190  // parse the sixth parameter: the number of sends.
191  if (sscanf(_global_argv[6], "%d", &send_count) < 1) {
192  LOG("The send count entry is malformed. Exiting.");
193  return 5;
194  }
195  LOG(a_sprintf("\tGot a send count of %d.", send_count));
196  }
197 
198  if (_global_argc == 1) {
199  // launch a paired duplicate of our test so we can chat.
200  launch_process zingit;
201  un_int kidnum;
202  un_int result = zingit.run(_global_argv[0],
203  astring(DEFAULT_HOST) + " " + DEFAULT_HOST + " "
204  /* we have reversed the send and receive ports. */
205  + a_sprintf("%d", DEFAULT_PORT + 1) + " " + a_sprintf("%d", DEFAULT_PORT)
206  + " " + a_sprintf("%d", DEFAULT_SEND_SIZE) + " " + a_sprintf("%d", DEFAULT_SEND_COUNT),
207  launch_process::RETURN_IMMEDIATELY, kidnum);
208  ASSERT_EQUAL(result, 0, "launching paired process should start successfully");
209  }
210 
211  // package our parameters in a form the tester likes.
212  internet_address to_pass(byte_array(4, ip_address), "", rcv_port);
213  internet_address dest(byte_array(4, dest_addr), "", send_port);
214 
215  // now, construct our tester object.
216  broadcast_spocket_tester tester(to_pass, true);
217 
218  // choose the appropriate action based on our role.
219  bool outcome = tester.connect();
220  if (!outcome) {
221  LOG(astring("Failed to connect on the tester."));
222  return 10;
223  }
224 
225  LOG(a_sprintf("you now have %d seconds; get other side ready.",
226  INITIAL_DELAY));
227  time_control::sleep_ms(INITIAL_DELAY * SECOND_ms);
228  LOG("starting test");
229 
230  // so, we're connected. try sending the test packages out.
231  testing_statistics stats; // to be filled by the tester.
232  outcome = tester.perform_test(dest, send_size, send_count * 2, stats);
233  // multiply send_count since we count each side as one.
234  if (!outcome) {
235  LOG("Failed out of send_data; maybe other side terminated.");
236  }
237 
238  stats.total_runs /= 2; // cut down to the real number of tests.
239 
240  if (!stats.total_runs)
241  stats.total_runs = 1;
242 
243  // now report on the stats that we get from the data sending.
244  LOG(a_sprintf("Report for %d completed test cycles.", stats.total_runs));
245  LOG("");
246  LOG("\t\tsend stats\t\treceive stats");
247  LOG("\t\t----------\t\t-------------");
248  LOG(a_sprintf("bytes\t\t%d\t\t\t%d", stats.bytes_sent,
249  stats.bytes_received));
250  LOG(a_sprintf("time\t\t%d\t\t\t%d", stats.send_time, stats.receive_time));
251  LOG(a_sprintf("avg. bytes\t%d\t\t\t%d", stats.bytes_sent
252  / stats.total_runs / 2, stats.bytes_received / stats.total_runs / 2));
253  LOG("");
254  LOG(a_sprintf("round trip time: %d ms", stats.round_trip_time));
255 //hmmm: use the bandwidth measurer object!!!
256  double bandwidth = double(stats.bytes_sent + stats.bytes_received)
257  / stats.round_trip_time / 1024.0 * 1000.0;
258  LOG(a_sprintf("bandwidth overall: %f K/s", bandwidth));
259 
260  if (_global_argc == 1) return final_report();
261  else return 0; // no unit test report for non-top-level process
262 }
263 
264 HOOPLE_MAIN(test_ucast_spocket, );
265 
The application_shell is a base object for console programs.
a_sprintf is a specialization of astring that provides printf style support.
Definition: astring.h:440
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition: astring.h:113
A very common template for a dynamic array of bytes.
Definition: byte_array.h:36
Outcomes describe the state of completion for an operation.
Definition: outcome.h:31
Provides the capability to start processes in a variety of ways to run other applications.
static basis::un_int run(const basis::astring &app_name, const basis::astring &command_line, int flag, basis::un_int &child_id)
starts an application using the "app_name" as the executable to run.
this type of address describes a destination out on the internet.
#define DEFINE_CLASS_NAME(objname)
Defines the name of a class by providing a couple standard methods.
Definition: enhance_cpp.h:45
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:57
Provides macros that implement the 'main' program of an application.
Implements an application lock to ensure only one is running at once.
char ** _global_argv
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
unsigned char abyte
A fairly important unit which is seldom defined...
Definition: definitions.h:51
const int SECOND_ms
Number of milliseconds in a second.
Definition: definitions.h:120
unsigned int un_int
Abbreviated name for unsigned integers.
Definition: definitions.h:62
A logger that sends to the console screen using the standard output device.
An extension to floating point primitives providing approximate equality.
Definition: averager.h:21
Provides access to the operating system's socket methods.
Definition: base_address.h:26
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
#include <time.h>
Definition: earth_time.cpp:37
Useful support functions for unit testing, especially within hoople.
Definition: unit_base.cpp:35
abyte ip_address_holder[4]
const int INITIAL_DELAY
#define LOG(to_print)
HOOPLE_MAIN(test_ucast_spocket,)
#define ASSERT_EQUAL(a, b, test_name)
Definition: unit_base.h:38