bcast_spocketer.cpp

Go to the documentation of this file.
00001 /*****************************************************************************\
00002 *                                                                             *
00003 *  Name   : broadcast_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 "bcast_spocketer.h"
00016 
00017 #include <basis/astring.h>
00018 #include <basis/byte_array.h>
00019 #include <basis/functions.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 #include <unit_test/unit_base.h>
00031 
00032 #include <errno.h>
00033 
00034 //using namespace application;
00035 using namespace basis;
00036 //using namespace filesystem;
00037 using namespace loggers;
00038 using namespace mathematics;
00039 using namespace sockets;
00040 using namespace structures;
00041 //using namespace textual;
00042 using namespace timely;
00043 using namespace unit_test;
00044 
00045 #define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print))
00046 
00047 const int MAXIMUM_WINSOCK_MTU = 1500;
00048   // the largest chunk of bytes we can receive at once.
00049 
00050 const int MAXIMUM_TRANSFER_WAIT = 40 * SECOND_ms;
00051   // the longest amount of time we wait in trying to receive data.
00052 
00053 static abyte receive_buffer[MAXIMUM_WINSOCK_MTU + 1];
00054   // used for dumping received data into.
00055 
00056 //#define DEBUG_SPOCKET_TESTER
00057   // uncomment for noisy version.
00058 
00059 broadcast_spocket_tester::broadcast_spocket_tester
00060     (const internet_address &where, bool unicast)
00061 : _where(new internet_address(where)),
00062   _stack(new tcpip_stack),
00063   _socket(NIL),
00064   _raw(new raw_socket),
00065   _ucast(unicast)
00066 {
00067 }
00068 
00069 broadcast_spocket_tester::~broadcast_spocket_tester()
00070 {
00071   WHACK(_socket);
00072   WHACK(_stack);
00073   WHACK(_where);
00074   WHACK(_raw);
00075 }
00076 
00077 bool broadcast_spocket_tester::connect()
00078 {
00079   spocket::sock_types type = spocket::BROADCAST;
00080   if (_ucast) type = spocket::UNICAST;
00081   if (!_socket) {
00082     _socket = new spocket(*_where, type);
00083   }
00084   outcome ret = _socket->connect();
00085   return ret == spocket::OKAY;
00086 }
00087 
00088 bool broadcast_spocket_tester::do_a_send(const internet_address &where_to,
00089     abyte *buffer, int size, testing_statistics &stats)
00090 {
00091   time_stamp start_time;
00092   int len_sent;
00093   time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
00094   outcome worked;
00095 
00096 #ifdef DEBUG_SPOCKET_TESTER
00097   LOG(a_sprintf("into do a send with %d bytes", size));
00098 #endif
00099 
00100   while (time_stamp() < when_to_leave) {
00101     worked = _socket->send_to(where_to, buffer, size, len_sent);
00102     if (worked == spocket::NONE_READY) {
00103       time_control::sleep_ms(20);
00104       continue;
00105     } else if (worked == spocket::PARTIAL) {
00106 //danger danger if we get wrong info.
00107       buffer += len_sent;
00108       size -= len_sent;
00109       time_control::sleep_ms(20);
00110       continue;
00111     } else break;
00112   }
00113 #ifdef DEBUG_SPOCKET_TESTER
00114   LOG("got out of loop");
00115 #endif
00116 
00117   stats.send_time += int(time_stamp().value() - start_time.value());
00118 
00119   if ( (worked != spocket::OKAY) || !len_sent) {
00120     LOG("No data went out on the socket.");
00121     return false;
00122   }
00123   if (len_sent != size) {
00124     LOG(a_sprintf("The full chunk didn't get sent out: %d bytes instead of %d",
00125         len_sent, size));
00126 //more bad news.  what do we do about getting the rest out?
00127     return false;
00128   }
00129   stats.bytes_sent += len_sent;
00130 
00131   time_stamp end_time;
00132 //  int time_taken = int(end_time.value() - start_time.value());
00133 
00134   return true;
00135 }
00136 
00137 bool broadcast_spocket_tester::do_a_receive(int size_expected,
00138     testing_statistics &stats)
00139 {
00140   time_stamp start_time;
00141 
00142 #ifdef DEBUG_SPOCKET_TESTER
00143   LOG("into do a rcv");
00144 #endif
00145 
00146   time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
00147   int full_length = 0;
00148   while ( (full_length < size_expected) && (time_stamp() < when_to_leave) ) {
00149     time_stamp start_of_receive;
00150     int len = MAXIMUM_WINSOCK_MTU;
00151     internet_address where_from;
00152     outcome ret = _socket->receive_from(receive_buffer, len, where_from);
00154     if (ret != spocket::OKAY) {
00155       if (ret == spocket::NONE_READY) {
00156         time_control::sleep_ms(20);
00157         continue;
00158       } else break;
00159     }
00160     // reset our time if we've gotten good data.
00161     if (ret == spocket::OKAY)
00162       when_to_leave.reset(MAXIMUM_TRANSFER_WAIT);
00163 
00164     int receive_duration = int(time_stamp().value()
00165         - start_of_receive.value());
00166     stats.receive_time += receive_duration;
00167 
00168 #ifdef DEBUG_SPOCKET_TESTER
00169     LOG(a_sprintf("did recv, len=%d", len));
00170 #endif
00171 
00172     if (!len) {
00173       LOG("Our socket has been disconnected.");
00174       return false;
00175     } else if (len < 0) {
00176       if (errno == SOCK_EWOULDBLOCK) continue;  // no data.
00177       LOG(astring("The receive failed with an error ")
00178           + critical_events::system_error_text(errno));
00179       return false;
00180     }
00181     full_length += len;
00182     stats.bytes_received += len;
00183   }
00184 
00185   if (full_length != size_expected)
00186     LOG(a_sprintf("Did not get the full size expected (wanted %d and "
00187         "got %d bytes).", size_expected, full_length));
00188 
00189   return true;
00190 }
00191 
00192 bool broadcast_spocket_tester::perform_test(const internet_address &dest,
00193     int size, int count, testing_statistics &stats)
00194 {
00195 #ifdef DEBUG_SPOCKET_TESTER
00196   LOG("into perf test");
00197 #endif
00198 
00199   // the statics are used to generate our random buffer for sending.
00200   static abyte garbage_buffer[MAXIMUM_WINSOCK_MTU + 1];
00201   static bool garbage_initialized = false;
00202   chaos randomizer;
00203 
00204   // if our static buffer full of random stuff was never initialized, we do
00205   // so now.  this supports efficiently re-using the tester if desired.
00206   if (!garbage_initialized) {
00207     // note the less than or equal; we know we have one more byte to fill.
00208     for (int i = 0; i <= MAXIMUM_WINSOCK_MTU; i++)
00209       garbage_buffer[i] = randomizer.inclusive(0, 255);
00210     garbage_initialized = true;
00211   }
00212 
00213   // reset the statistical package.
00214   stats.total_runs = 0;
00215   stats.send_time = 0;
00216   stats.receive_time = 0;
00217   stats.bytes_sent = 0;
00218   stats.bytes_received = 0;
00219 
00220   // check that they aren't trying to do too big of a send.
00221   if (size > MAXIMUM_WINSOCK_MTU) {
00222     LOG("The size is over our limit.  To fix this, edit the "
00223         "send_data function.");
00224     return false;
00225   }
00226 
00227   // check that our socket is usable.
00228   if (!_socket) {
00229     LOG("One cannot send data on an uninitialized tester!");
00230     return false;
00231   }
00232 
00233   int runs_completed = 0;
00234     // counts up how many times we've done our test cycle.
00235 
00236   while (runs_completed < count) {
00237 #ifdef DEBUG_SPOCKET_TESTER
00238     LOG(a_sprintf("iter %d", runs_completed));
00239 #endif
00240     // we're doing the client side routine here.
00241     time_stamp trip_start;
00242 #ifdef DEBUG_SPOCKET_TESTER
00243     LOG("client about to send");
00244 #endif
00245     if (!do_a_send(dest, garbage_buffer, size, stats)) {
00246       LOG("We failed on a send.  Now quitting.");
00247       return false;
00248     }
00249 #ifdef DEBUG_SPOCKET_TESTER
00250     LOG("client about to rcv");
00251 #endif
00252     if (!do_a_receive(size, stats)) {
00253       LOG("We failed on a receive.  Now quitting.");
00254       return false;
00255     }
00256     stats.round_trip_time += int(time_stamp().value() - trip_start.value());
00257 
00258     runs_completed++;  // finished a run.
00259     stats.total_runs++;  // count it in the overall stats too.
00260     if ( !(runs_completed % 10) )
00261       LOG(a_sprintf("Completed test #%d.", runs_completed));
00262   }
00263 
00264   return true;
00265 }
00266 
Generated on Sat Jan 28 04:22:54 2012 for hoople2 project by  doxygen 1.6.3