feisty meow concerns codebase  2.140
spocket_tester.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : 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 "spocket_tester.h"
16 
17 #include <basis/byte_array.h>
18 #include <basis/functions.h>
19 #include <basis/astring.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 
31 #include <errno.h>
32 
33 using namespace basis;
34 using namespace loggers;
35 using namespace mathematics;
36 using namespace sockets;
37 using namespace structures;
38 using namespace textual;
39 using namespace timely;
40 
41 #define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print))
42 
43 const int MAXIMUM_WINSOCK_MTU = 100000;
44  // the largest chunk of bytes we will receive at one time.
45 
47  // the longest amount of time we wait in trying to receive data.
48 
49 static abyte receive_buffer[MAXIMUM_WINSOCK_MTU + 1];
50  // used for dumping received data into.
51 
52 const int PAUSE_TIME = 200;
53  // the snooze interval when we encounter socket underflow or overflow.
54 
55 //#define DEBUG_SPOCKET_TESTER
56  // uncomment for noisy version.
57 
59 : _where(new internet_address(where)),
60  _stack(new tcpip_stack),
61  _socket(NULL_POINTER),
62  _root_server(NULL_POINTER),
63  _raw(new raw_socket)
64 {
65 }
66 
68 {
69  WHACK(_socket);
70  WHACK(_root_server);
71  WHACK(_stack);
72  WHACK(_where);
73  WHACK(_raw);
74 }
75 
77 {
78  if (!_socket) {
79  _socket = new spocket(*_where);
80  }
81  outcome ret = spocket::NO_CONNECTION;
82  while (true) {
83  ret = _socket->connect();
84  if (ret == spocket::OKAY) break;
85  if (ret != spocket::NO_CONNECTION) break;
86  time_control::sleep_ms(100);
87  }
88  return ret == spocket::OKAY;
89 }
90 
91 bool spocket_tester::accept(bool wait)
92 {
93  if (!_root_server) {
94  _root_server = new spocket(*_where);
95  }
96  if (_socket) {
97  LOG("already have a socket for accept!");
98  return true;
99  }
100  outcome ret = spocket::NO_CONNECTION;
101  while (true) {
102  ret = _root_server->accept(_socket, false);
103  if (ret == spocket::OKAY) break;
104  if (ret != spocket::NO_CONNECTION) break;
105  if (!wait) return true; // we tried to accept at least once.
106  time_control::sleep_ms(100); // snooze to avoid slamming with accepts.
107  }
108 
109  return ret == spocket::OKAY;
110 }
111 
112 bool spocket_tester::do_a_send(abyte *buffer, int size,
113  testing_statistics &stats)
114 {
115  time_stamp start_time;
116  int len_sent;
117  time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
118  outcome worked;
119 
120 #ifdef DEBUG_SPOCKET_TESTER
121  LOG("into do a send");
122 #endif
123 
124  while (time_stamp() < when_to_leave) {
125  worked = _socket->send(buffer, size, len_sent);
126  if (worked == spocket::NONE_READY) {
128  _socket->await_writable(PAUSE_TIME);
129  continue;
130  } else if (worked == spocket::PARTIAL) {
131 //danger danger if we get wrong info.
132  buffer += len_sent;
133  size -= len_sent;
134  stats.bytes_sent += len_sent;
136  _socket->await_writable(PAUSE_TIME);
137  continue;
138  } else break;
139  }
140 #ifdef DEBUG_SPOCKET_TESTER
141  LOG("got out of loop");
142 #endif
143 
144  stats.send_time += int(time_stamp().value() - start_time.value());
145  stats.bytes_sent += len_sent;
146 
147  if ( (worked != spocket::OKAY) && (worked != spocket::PARTIAL) ) {
148  LOG(astring("No data went out on the socket: ")
149  + spocket::outcome_name(worked));
150  return false;
151  }
152  if (len_sent != size) {
153  LOG(a_sprintf("partial send on socket, %d bytes instead of %d, recurse.",
154  len_sent, size));
155  return do_a_send(buffer + len_sent, size - len_sent, stats);
156  }
157 
158  time_stamp end_time;
159 // int time_taken = int(end_time.value() - start_time.value());
160 
161  return true;
162 }
163 
164 bool spocket_tester::do_a_receive(int size_expected, testing_statistics &stats)
165 {
166  time_stamp start_time;
167 
168 #ifdef DEBUG_SPOCKET_TESTER
169  LOG("into do a rcv");
170 #endif
171 
172  time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
173  int full_length = 0;
174  while ( (full_length < size_expected) && (time_stamp() < when_to_leave) ) {
175  time_stamp start_of_receive;
176  int len = MAXIMUM_WINSOCK_MTU;
177  outcome ret = _socket->receive(receive_buffer, len);
178  if (ret != spocket::OKAY) {
179  if (ret == spocket::NONE_READY) {
180 if (len != 0) LOG(a_sprintf("supposedly nothing was received (%d bytes)", len));
181  _socket->await_readable(PAUSE_TIME);
182  continue;
183  } else break;
184  }
185  // reset our time if we've gotten good data.
186  if (ret == spocket::OKAY)
187  when_to_leave.reset(MAXIMUM_TRANSFER_WAIT);
188 
189  int receive_duration = int(time_stamp().value()
190  - start_of_receive.value());
191  stats.receive_time += receive_duration;
192 
193 #ifdef DEBUG_SPOCKET_TESTER
194  LOG(a_sprintf("did recv, len=%d", len));
195 #endif
196 
197  if (!len) {
198  LOG("Our socket has been disconnected.");
199  return false;
200  } else if (len < 0) {
201  if (errno == SOCK_EWOULDBLOCK) continue; // no data.
202  LOG(astring("The receive failed with an error ")
203  + critical_events::system_error_text(errno));
204  return false;
205  }
206  full_length += len;
207  stats.bytes_received += len;
208  }
209 
210  if (full_length != size_expected)
211  LOG(a_sprintf("Did not get the full size expected (wanted %d and "
212  "got %d bytes).", size_expected, full_length));
213 
214  return true;
215 }
216 
217 bool spocket_tester::perform_test(int size, int count,
218  testing_statistics &stats)
219 {
220  // the statics are used to generate our random buffer for sending.
221  static abyte garbage_buffer[MAXIMUM_WINSOCK_MTU + 1];
222  static bool garbage_initialized = false;
224 
225  // if our static buffer full of random stuff was never initialized, we do
226  // so now. this supports efficiently re-using the tester if desired.
227  if (!garbage_initialized) {
228  // note the less than or equal; we know we have one more byte to fill.
229  for (int i = 0; i <= MAXIMUM_WINSOCK_MTU; i++)
230  garbage_buffer[i] = randomizer.inclusive(0, 255);
231  garbage_initialized = true;
232  }
233 
234  // reset the statistical package.
235  stats.total_runs = 0;
236  stats.send_time = 0;
237  stats.receive_time = 0;
238  stats.bytes_sent = 0;
239  stats.bytes_received = 0;
240 
241  // check that they aren't trying to do too big of a send.
242  if (size > MAXIMUM_WINSOCK_MTU) {
243  LOG("The size is over our limit. To fix this, edit the "
244  "send_data function.");
245  return false;
246  }
247 
248  // check that our socket is usable.
249  if (!_socket) {
250  LOG("One cannot send data on an uninitialized tester!");
251  return false;
252  }
253 
254  int runs_completed = 0;
255  // counts up how many times we've done our test cycle.
256 
257  while (runs_completed < count) {
258 #ifdef DEBUG_SPOCKET_TESTER
259  LOG(a_sprintf("iter %d", runs_completed));
260 #endif
261  if (_socket->client()) {
262  // we're doing the client side routine here.
263  time_stamp trip_start;
264 #ifdef DEBUG_SPOCKET_TESTER
265  LOG("client about to send");
266 #endif
267  if (!do_a_send(garbage_buffer, size, stats)) {
268  LOG("We failed on a send. Now quitting.");
269  return false;
270  }
271 #ifdef DEBUG_SPOCKET_TESTER
272  LOG("client about to rcv");
273 #endif
274  if (!do_a_receive(size, stats)) {
275  LOG("We failed on a receive. Now quitting.");
276  return false;
277  }
278  stats.round_trip_time += int(time_stamp().value() - trip_start.value());
279  } else {
280  // we're doing the server side routine here.
281  time_stamp trip_start;
282 #ifdef DEBUG_SPOCKET_TESTER
283  LOG("server about to rcv");
284 #endif
285  if (!do_a_receive(size, stats)) {
286  LOG("We failed on a receive. Now quitting.");
287  return false;
288  }
289 #ifdef DEBUG_SPOCKET_TESTER
290  LOG("server about to send");
291 #endif
292  if (!do_a_send(garbage_buffer, size, stats)) {
293  LOG("We failed on a send. Now quitting.");
294  return false;
295  }
296  stats.round_trip_time += int(time_stamp().value() - trip_start.value());
297  }
298 
299  runs_completed++; // finished a run.
300  stats.total_runs++; // count it in the overall stats too.
301  if ( !(runs_completed % 10) )
302  LOG(a_sprintf("Completed test #%d.", runs_completed));
303  }
304 
305  return true;
306 }
307 
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
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 await_writable(int timeout)
Definition: spocket.cpp:256
bool client() const
Definition: spocket.h:152
basis::outcome send(const basis::abyte *buffer, int size, int &len_sent)
Definition: spocket.cpp:573
basis::outcome connect(int communication_wait=20 *basis::SECOND_ms)
Definition: spocket.cpp:274
basis::outcome accept(spocket *&sock, bool wait)
Definition: spocket.cpp:460
basis::outcome await_readable(int timeout)
Definition: spocket.cpp:238
basis::outcome receive(basis::abyte *buffer, int &size)
Definition: spocket.cpp:664
Helpful functions for interacting with TCP/IP stacks.
Definition: tcpip_stack.h:38
bool perform_test(int size, int count, testing_statistics &stats_to_fill)
bool accept(bool wait=true)
spocket_tester(const sockets::internet_address &where)
bool do_a_receive(int size_expected, testing_statistics &stats)
bool do_a_send(basis::abyte *buffer, int size, testing_statistics &stats)
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
const int MAXIMUM_WINSOCK_MTU
const int PAUSE_TIME
#define LOG(to_print)
const int MAXIMUM_TRANSFER_WAIT
#define randomizer()
#define SOCK_EWOULDBLOCK