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
30using namespace application;
31using namespace basis;
32using namespace loggers;
33using namespace mathematics;
34using namespace processes;
35using namespace sockets;
36using namespace structures;
37using namespace textual;
38using namespace timely;
39using namespace unit_test;
40
41#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print))
42
43const int INITIAL_DELAY = 2; // number of seconds before starting sends.
44
46
47class test_ucast_spocket : public virtual application_shell, public virtual unit_base
48{
49public:
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.
58bool 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
104int 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("\
125This program takes six command line arguments to begin operation.\n\
126These 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\
133Note: it is expected that the testers have equal send sizes; this\n\
134allows the receiver to know when it's gotten all the data that's\n\
135expected 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),
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.",
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
264HOOPLE_MAIN(test_ucast_spocket, );
265
The application_shell is a base object for console programs.
virtual int execute()=0
< retrieves the command line from the /proc hierarchy on linux.
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.
@ RETURN_IMMEDIATELY
starts the application and comes right back to the caller.
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.
static void sleep_ms(basis::un_int msec)
a system independent name for a forced snooze measured in milliseconds.
#define DEFINE_CLASS_NAME(objname)
Defines the name of a class by providing a couple standard methods.
Definition enhance_cpp.h:42
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition enhance_cpp.h:54
Provides macros that implement the 'main' program of an application.
#define HOOPLE_MAIN(obj_name, obj_args)
options that should work for most unix and linux apps.
Definition hoople_main.h:61
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.
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.
A dynamic container class that holds any kind of object via pointers.
Definition amorph.h:55
#include <time.h>
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)
#define ASSERT_EQUAL(a, b, test_name)
Definition unit_base.h:38