feisty meow concerns codebase  2.140
bcast_spocketer.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : broadcast_spocket_tester *
4 * Author : Chris Koeritz *
5 * *
6 *******************************************************************************
7 * Copyright (c) 2000-$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 
17 #include <basis/astring.h>
18 #include <basis/byte_array.h>
19 #include <basis/functions.h>
22 #include <mathematics/chaos.h>
24 #include <sockets/raw_socket.h>
25 #include <sockets/spocket.h>
27 #include <sockets/tcpip_stack.h>
28 #include <timely/time_control.h>
29 #include <timely/time_stamp.h>
30 #include <unit_test/unit_base.h>
31 
32 #include <errno.h>
33 
34 using namespace basis;
35 using namespace loggers;
36 using namespace mathematics;
37 using namespace sockets;
38 using namespace structures;
39 using namespace timely;
40 using namespace unit_test;
41 
42 #define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print))
43 
44 const int MAXIMUM_WINSOCK_MTU = 1500;
45  // the largest chunk of bytes we can receive at once.
46 
48  // the longest amount of time we wait in trying to receive data.
49 
50 static abyte receive_buffer[MAXIMUM_WINSOCK_MTU + 1];
51  // used for dumping received data into.
52 
53 //#define DEBUG_SPOCKET_TESTER
54  // uncomment for noisy version.
55 
57  (const internet_address &where, bool unicast)
58 : _where(new internet_address(where)),
59  _stack(new tcpip_stack),
60  _socket(NULL_POINTER),
61  _raw(new raw_socket),
62  _ucast(unicast)
63 {
64 }
65 
67 {
68  WHACK(_socket);
69  WHACK(_stack);
70  WHACK(_where);
71  WHACK(_raw);
72 }
73 
75 {
76  spocket::sock_types type = spocket::BROADCAST;
77  if (_ucast) type = spocket::UNICAST;
78  if (!_socket) {
79  _socket = new spocket(*_where, type);
80  }
81  outcome ret = _socket->connect();
82  return ret == spocket::OKAY;
83 }
84 
86  abyte *buffer, int size, testing_statistics &stats)
87 {
88  time_stamp start_time;
89  int len_sent;
90  time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
91  outcome worked;
92 
93 #ifdef DEBUG_SPOCKET_TESTER
94  LOG(a_sprintf("into do a send with %d bytes", size));
95 #endif
96 
97  while (time_stamp() < when_to_leave) {
98  worked = _socket->send_to(where_to, buffer, size, len_sent);
99  if (worked == spocket::NONE_READY) {
100  time_control::sleep_ms(20);
101  continue;
102  } else if (worked == spocket::PARTIAL) {
103 //danger danger if we get wrong info.
104  buffer += len_sent;
105  size -= len_sent;
106  time_control::sleep_ms(20);
107  continue;
108  } else break;
109  }
110 #ifdef DEBUG_SPOCKET_TESTER
111  LOG("got out of loop");
112 #endif
113 
114  stats.send_time += int(time_stamp().value() - start_time.value());
115 
116  if ( (worked != spocket::OKAY) || !len_sent) {
117  LOG("No data went out on the socket.");
118  return false;
119  }
120  if (len_sent != size) {
121  LOG(a_sprintf("The full chunk didn't get sent out: %d bytes instead of %d",
122  len_sent, size));
123 //more bad news. what do we do about getting the rest out?
124  return false;
125  }
126  stats.bytes_sent += len_sent;
127 
128  time_stamp end_time;
129 // int time_taken = int(end_time.value() - start_time.value());
130 
131  return true;
132 }
133 
135  testing_statistics &stats)
136 {
137  time_stamp start_time;
138 
139 #ifdef DEBUG_SPOCKET_TESTER
140  LOG("into do a rcv");
141 #endif
142 
143  time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
144  int full_length = 0;
145  while ( (full_length < size_expected) && (time_stamp() < when_to_leave) ) {
146  time_stamp start_of_receive;
147  int len = MAXIMUM_WINSOCK_MTU;
148  internet_address where_from;
149  outcome ret = _socket->receive_from(receive_buffer, len, where_from);
151  if (ret != spocket::OKAY) {
152  if (ret == spocket::NONE_READY) {
153  time_control::sleep_ms(20);
154  continue;
155  } else break;
156  }
157  // reset our time if we've gotten good data.
158  if (ret == spocket::OKAY)
159  when_to_leave.reset(MAXIMUM_TRANSFER_WAIT);
160 
161  int receive_duration = int(time_stamp().value()
162  - start_of_receive.value());
163  stats.receive_time += receive_duration;
164 
165 #ifdef DEBUG_SPOCKET_TESTER
166  LOG(a_sprintf("did recv, len=%d", len));
167 #endif
168 
169  if (!len) {
170  LOG("Our socket has been disconnected.");
171  return false;
172  } else if (len < 0) {
173  if (errno == SOCK_EWOULDBLOCK) continue; // no data.
174  LOG(astring("The receive failed with an error ")
175  + critical_events::system_error_text(errno));
176  return false;
177  }
178  full_length += len;
179  stats.bytes_received += len;
180  }
181 
182  if (full_length != size_expected)
183  LOG(a_sprintf("Did not get the full size expected (wanted %d and "
184  "got %d bytes).", size_expected, full_length));
185 
186  return true;
187 }
188 
190  int size, int count, testing_statistics &stats)
191 {
192 #ifdef DEBUG_SPOCKET_TESTER
193  LOG("into perf test");
194 #endif
195 
196  // the statics are used to generate our random buffer for sending.
197  static abyte garbage_buffer[MAXIMUM_WINSOCK_MTU + 1];
198  static bool garbage_initialized = false;
200 
201  // if our static buffer full of random stuff was never initialized, we do
202  // so now. this supports efficiently re-using the tester if desired.
203  if (!garbage_initialized) {
204  // note the less than or equal; we know we have one more byte to fill.
205  for (int i = 0; i <= MAXIMUM_WINSOCK_MTU; i++)
206  garbage_buffer[i] = randomizer.inclusive(0, 255);
207  garbage_initialized = true;
208  }
209 
210  // reset the statistical package.
211  stats.total_runs = 0;
212  stats.send_time = 0;
213  stats.receive_time = 0;
214  stats.bytes_sent = 0;
215  stats.bytes_received = 0;
216 
217  // check that they aren't trying to do too big of a send.
218  if (size > MAXIMUM_WINSOCK_MTU) {
219  LOG("The size is over our limit. To fix this, edit the "
220  "send_data function.");
221  return false;
222  }
223 
224  // check that our socket is usable.
225  if (!_socket) {
226  LOG("One cannot send data on an uninitialized tester!");
227  return false;
228  }
229 
230  int runs_completed = 0;
231  // counts up how many times we've done our test cycle.
232 
233  while (runs_completed < count) {
234 #ifdef DEBUG_SPOCKET_TESTER
235  LOG(a_sprintf("iter %d", runs_completed));
236 #endif
237  // we're doing the client side routine here.
238  time_stamp trip_start;
239 #ifdef DEBUG_SPOCKET_TESTER
240  LOG("client about to send");
241 #endif
242  if (!do_a_send(dest, garbage_buffer, size, stats)) {
243  LOG("We failed on a send. Now quitting.");
244  return false;
245  }
246 #ifdef DEBUG_SPOCKET_TESTER
247  LOG("client about to rcv");
248 #endif
249  if (!do_a_receive(size, stats)) {
250  LOG("We failed on a receive. Now quitting.");
251  return false;
252  }
253  stats.round_trip_time += int(time_stamp().value() - trip_start.value());
254 
255  runs_completed++; // finished a run.
256  stats.total_runs++; // count it in the overall stats too.
257  if ( !(runs_completed % 10) )
258  LOG(a_sprintf("Completed test #%d.", runs_completed));
259  }
260 
261  return true;
262 }
263 
const int MAXIMUM_WINSOCK_MTU
#define LOG(to_print)
const int MAXIMUM_TRANSFER_WAIT
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
Outcomes describe the state of completion for an operation.
Definition: outcome.h:31
bool do_a_send(const sockets::internet_address &where_to, basis::abyte *buffer, int size, testing_statistics &stats)
bool perform_test(const sockets::internet_address &dest, int size, int count, testing_statistics &stats_to_fill)
broadcast_spocket_tester(const sockets::internet_address &where, bool unicast=false)
bool do_a_receive(int size_expected, testing_statistics &stats)
a platform-independent way to acquire random numbers in a specific range.
Definition: chaos.h:51
this type of address describes a destination out on the internet.
Abstraction for a higher-level BSD socket that is platform independent.
Definition: spocket.h:40
basis::outcome connect(int communication_wait=20 *basis::SECOND_ms)
Definition: spocket.cpp:274
basis::outcome receive_from(basis::abyte *buffer, int &size, internet_address &where_from)
Definition: spocket.cpp:713
basis::outcome send_to(const internet_address &where_to, const basis::abyte *buffer, int size, int &len_sent)
Definition: spocket.cpp:628
Helpful functions for interacting with TCP/IP stacks.
Definition: tcpip_stack.h:38
Represents a point in time relative to the operating system startup time.
Definition: time_stamp.h:38
void reset()
sets the stamp time back to now.
Definition: time_stamp.cpp:59
time_representation value() const
returns the time_stamp in terms of the lower level type.
Definition: time_stamp.h:61
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition: functions.h:121
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
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
#define randomizer()
#define SOCK_EWOULDBLOCK