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
34using namespace basis;
35using namespace loggers;
36using namespace mathematics;
37using namespace sockets;
38using namespace structures;
39using namespace timely;
40using namespace unit_test;
41
42#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print))
43
44const 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
50static 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{
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) {
101 continue;
102 } else if (worked == spocket::PARTIAL) {
103//danger danger if we get wrong info.
104 buffer += len_sent;
105 size -= len_sent;
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) {
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 ")
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
const int MAXIMUM_TRANSFER_WAIT
#define LOG(s)
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)
static basis::astring system_error_text(basis::un_int error_to_show)
returns the OS's string form of the "error_to_show".
a platform-independent way to acquire random numbers in a specific range.
Definition chaos.h:51
int inclusive(int low, int high) const
< Returns a pseudo-random number r, such that "low" <= r <= "high".
Definition chaos.h:88
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
static void sleep_ms(basis::un_int msec)
a system independent name for a forced snooze measured in milliseconds.
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.
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.
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
#define SOCK_EWOULDBLOCK
#define randomizer()