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
33using namespace basis;
34using namespace loggers;
35using namespace mathematics;
36using namespace sockets;
37using namespace structures;
38using namespace textual;
39using namespace timely;
40
41#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print))
42
43const 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
49static abyte receive_buffer[MAXIMUM_WINSOCK_MTU + 1];
50 // used for dumping received data into.
51
52const 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 }
82 while (true) {
83 ret = _socket->connect();
84 if (ret == spocket::OKAY) break;
85 if (ret != spocket::NO_CONNECTION) break;
87 }
88 return ret == spocket::OKAY;
89}
90
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 }
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
112bool 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
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) {
180if (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 ")
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
217bool 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
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
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 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
static const char * outcome_name(const basis::outcome &to_name)
Definition spocket.cpp:180
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)
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>
const int PAUSE_TIME
const int MAXIMUM_WINSOCK_MTU
const int MAXIMUM_TRANSFER_WAIT
#define SOCK_EWOULDBLOCK
#define randomizer()