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

Generated on Fri Oct 10 04:28:58 2008 for HOOPLE Libraries by  doxygen 1.5.1