spocket_tester.cpp

Go to the documentation of this file.
00001 /*****************************************************************************\
00002 *                                                                             *
00003 *  Name   : spocket_tester                                                    *
00004 *  Author : Chris Koeritz                                                     *
00005 *                                                                             *
00006 *******************************************************************************
00007 * Copyright (c) 2000-$now By Author.  This program is free software; you can  *
00008 * redistribute it and/or modify it under the terms of the GNU General Public  *
00009 * License as published by the Free Software Foundation; either version 2 of   *
00010 * the License or (at your option) any later version.  This is online at:      *
00011 *     http://www.fsf.org/copyleft/gpl.html                                    *
00012 * Please send any updates to: fred@gruntose.com                               *
00013 \*****************************************************************************/
00014 
00015 #include "spocket_tester.h"
00016 
00017 #include <basis/byte_array.h>
00018 #include <basis/functions.h>
00019 #include <basis/astring.h>
00020 #include <loggers/critical_events.h>
00021 #include <loggers/program_wide_logger.h>
00022 #include <mathematics/chaos.h>
00023 #include <sockets/internet_address.h>
00024 #include <sockets/raw_socket.h>
00025 #include <sockets/spocket.h>
00026 #include <sockets/tcpip_definitions.h>
00027 #include <sockets/tcpip_stack.h>
00028 #include <timely/time_control.h>
00029 #include <timely/time_stamp.h>
00030 
00031 #include <errno.h>
00032 
00033 //using namespace application;
00034 using namespace basis;
00035 //using namespace filesystem;
00036 using namespace loggers;
00037 using namespace mathematics;
00038 using namespace sockets;
00039 using namespace structures;
00040 using namespace textual;
00041 using namespace timely;
00042 //using namespace unit_test;
00043 
00044 #define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print))
00045 
00046 const int MAXIMUM_WINSOCK_MTU = 100000;
00047   // the largest chunk of bytes we will receive at one time.
00048 
00049 const int MAXIMUM_TRANSFER_WAIT = 40 * SECOND_ms;
00050   // the longest amount of time we wait in trying to receive data.
00051 
00052 static abyte receive_buffer[MAXIMUM_WINSOCK_MTU + 1];
00053   // used for dumping received data into.
00054 
00055 const int PAUSE_TIME = 200;
00056   // the snooze interval when we encounter socket underflow or overflow.
00057 
00058 //#define DEBUG_SPOCKET_TESTER
00059   // uncomment for noisy version.
00060 
00061 spocket_tester::spocket_tester(const internet_address &where)
00062 : _where(new internet_address(where)),
00063   _stack(new tcpip_stack),
00064   _socket(NIL),
00065   _root_server(NIL),
00066   _raw(new raw_socket)
00067 {
00068 }
00069 
00070 spocket_tester::~spocket_tester()
00071 {
00072   WHACK(_socket);
00073   WHACK(_root_server);
00074   WHACK(_stack);
00075   WHACK(_where);
00076   WHACK(_raw);
00077 }
00078 
00079 bool spocket_tester::connect()
00080 {
00081   if (!_socket) {
00082     _socket = new spocket(*_where);
00083   }
00084   outcome ret = spocket::NO_CONNECTION;
00085   while (true) {
00086     ret = _socket->connect();
00087     if (ret == spocket::OKAY) break;
00088     if (ret != spocket::NO_CONNECTION) break;
00089     time_control::sleep_ms(100);
00090   }
00091   return ret == spocket::OKAY;
00092 }
00093 
00094 bool spocket_tester::accept(bool wait)
00095 {
00096   if (!_root_server) {
00097     _root_server = new spocket(*_where);
00098   }
00099   if (_socket) {
00100     LOG("already have a socket for accept!");
00101     return true;
00102   }
00103   outcome ret = spocket::NO_CONNECTION;
00104   while (true) {
00105     ret = _root_server->accept(_socket, false);
00106     if (ret == spocket::OKAY) break;
00107     if (ret != spocket::NO_CONNECTION) break;
00108     if (!wait) return true;  // we accepted at least once.
00109     time_control::sleep_ms(100);
00110   }
00111   return ret == spocket::OKAY;
00112 }
00113 
00114 bool spocket_tester::do_a_send(abyte *buffer, int size,
00115     testing_statistics &stats)
00116 {
00117   time_stamp start_time;
00118   int len_sent;
00119   time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
00120   outcome worked;
00121 
00122 #ifdef DEBUG_SPOCKET_TESTER
00123   LOG("into do a send");
00124 #endif
00125 
00126   while (time_stamp() < when_to_leave) {
00127     worked = _socket->send(buffer, size, len_sent);
00128     if (worked == spocket::NONE_READY) {
00130       _socket->await_writable(PAUSE_TIME);
00131       continue;
00132     } else if (worked == spocket::PARTIAL) {
00133 //danger danger if we get wrong info.
00134       buffer += len_sent;
00135       size -= len_sent;
00136       stats.bytes_sent += len_sent;
00138       _socket->await_writable(PAUSE_TIME);
00139       continue;
00140     } else break;
00141   }
00142 #ifdef DEBUG_SPOCKET_TESTER
00143   LOG("got out of loop");
00144 #endif
00145 
00146   stats.send_time += int(time_stamp().value() - start_time.value());
00147   stats.bytes_sent += len_sent;
00148 
00149   if ( (worked != spocket::OKAY) && (worked != spocket::PARTIAL) ) {
00150     LOG(astring("No data went out on the socket: ")
00151         + spocket::outcome_name(worked));
00152     return false;
00153   }
00154   if (len_sent != size) {
00155     LOG(a_sprintf("partial send on socket, %d bytes instead of %d, recurse.", 
00156         len_sent, size));
00157     return do_a_send(buffer + len_sent, size - len_sent, stats);
00158   }
00159 
00160   time_stamp end_time;
00161 //  int time_taken = int(end_time.value() - start_time.value());
00162 
00163   return true;
00164 }
00165 
00166 bool spocket_tester::do_a_receive(int size_expected, testing_statistics &stats)
00167 {
00168   time_stamp start_time;
00169 
00170 #ifdef DEBUG_SPOCKET_TESTER
00171   LOG("into do a rcv");
00172 #endif
00173 
00174   time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
00175   int full_length = 0;
00176   while ( (full_length < size_expected) && (time_stamp() < when_to_leave) ) {
00177     time_stamp start_of_receive;
00178     int len = MAXIMUM_WINSOCK_MTU;
00179     outcome ret = _socket->receive(receive_buffer, len);
00180     if (ret != spocket::OKAY) {
00181       if (ret == spocket::NONE_READY) {
00182 if (len != 0) LOG(a_sprintf("supposedly nothing was received (%d bytes)", len));
00184         _socket->await_readable(PAUSE_TIME);
00185         continue;
00186       } else break;
00187     }
00188     // reset our time if we've gotten good data.
00189     if (ret == spocket::OKAY)
00190       when_to_leave.reset(MAXIMUM_TRANSFER_WAIT);
00191 
00192     int receive_duration = int(time_stamp().value()
00193         - start_of_receive.value());
00194     stats.receive_time += receive_duration;
00195 
00196 #ifdef DEBUG_SPOCKET_TESTER
00197     LOG(a_sprintf("did recv, len=%d", len));
00198 #endif
00199 
00200     if (!len) {
00201       LOG("Our socket has been disconnected.");
00202       return false;
00203     } else if (len < 0) {
00204       if (errno == SOCK_EWOULDBLOCK) continue;  // no data.
00205       LOG(astring("The receive failed with an error ")
00206           + critical_events::system_error_text(errno));
00207       return false;
00208     }
00209     full_length += len;
00210     stats.bytes_received += len;
00211   }
00212 
00213   if (full_length != size_expected)
00214     LOG(a_sprintf("Did not get the full size expected (wanted %d and "
00215         "got %d bytes).", size_expected, full_length));
00216 
00217   return true;
00218 }
00219 
00220 bool spocket_tester::perform_test(int size, int count,
00221     testing_statistics &stats)
00222 {
00223 #ifdef DEBUG_SPOCKET_TESTER
00224   LOG("into perf test");
00225 #endif
00226 
00227   // the statics are used to generate our random buffer for sending.
00228   static abyte garbage_buffer[MAXIMUM_WINSOCK_MTU + 1];
00229   static bool garbage_initialized = false;
00230   chaos randomizer;
00231 
00232   // if our static buffer full of random stuff was never initialized, we do
00233   // so now.  this supports efficiently re-using the tester if desired.
00234   if (!garbage_initialized) {
00235     LOG("initializing random send buffer.");
00236     // note the less than or equal; we know we have one more byte to fill.
00237     for (int i = 0; i <= MAXIMUM_WINSOCK_MTU; i++)
00238       garbage_buffer[i] = randomizer.inclusive(0, 255);
00239     garbage_initialized = true;
00240     LOG("random send buffer initialized.");
00241   }
00242 
00243   // reset the statistical package.
00244   stats.total_runs = 0;
00245   stats.send_time = 0;
00246   stats.receive_time = 0;
00247   stats.bytes_sent = 0;
00248   stats.bytes_received = 0;
00249 
00250   // check that they aren't trying to do too big of a send.
00251   if (size > MAXIMUM_WINSOCK_MTU) {
00252     LOG("The size is over our limit.  To fix this, edit the "
00253         "send_data function.");
00254     return false;
00255   }
00256 
00257   // check that our socket is usable.
00258   if (!_socket) {
00259     LOG("One cannot send data on an uninitialized tester!");
00260     return false;
00261   }
00262 
00263   int runs_completed = 0;
00264     // counts up how many times we've done our test cycle.
00265 
00266   while (runs_completed < count) {
00267 #ifdef DEBUG_SPOCKET_TESTER
00268     LOG(a_sprintf("iter %d", runs_completed));
00269 #endif
00270     if (_socket->client()) {
00271       // we're doing the client side routine here.
00272       time_stamp trip_start;
00273 #ifdef DEBUG_SPOCKET_TESTER
00274       LOG("client about to send");
00275 #endif
00276       if (!do_a_send(garbage_buffer, size, stats)) {
00277         LOG("We failed on a send.  Now quitting.");
00278         return false;
00279       }
00280 #ifdef DEBUG_SPOCKET_TESTER
00281       LOG("client about to rcv");
00282 #endif
00283       if (!do_a_receive(size, stats)) {
00284         LOG("We failed on a receive.  Now quitting.");
00285         return false;
00286       }
00287       stats.round_trip_time += int(time_stamp().value() - trip_start.value());
00288     } else {
00289       // we're doing the server side routine here.
00290       time_stamp trip_start;
00291 #ifdef DEBUG_SPOCKET_TESTER
00292       LOG("server about to rcv");
00293 #endif
00294       if (!do_a_receive(size, stats)) {
00295         LOG("We failed on a receive.  Now quitting.");
00296         return false;
00297       }
00298 #ifdef DEBUG_SPOCKET_TESTER
00299       LOG("server about to send");
00300 #endif
00301       if (!do_a_send(garbage_buffer, size, stats)) {
00302         LOG("We failed on a send.  Now quitting.");
00303         return false;
00304       }
00305       stats.round_trip_time += int(time_stamp().value() - trip_start.value());
00306     }
00307 
00308     runs_completed++;  // finished a run.
00309     stats.total_runs++;  // count it in the overall stats too.
00310     if ( !(runs_completed % 10) )
00311       LOG(a_sprintf("Completed test #%d.", runs_completed));
00312   }
00313 
00314   return true;
00315 }
00316 
Generated on Sat Jan 28 04:22:54 2012 for hoople2 project by  doxygen 1.6.3