00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "bcast_spocketer.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 = 1500;
00033
00034
00035 const int MAXIMUM_TRANSFER_WAIT = 40 * SECOND_ms;
00036
00037
00038 static byte receive_buffer[MAXIMUM_WINSOCK_MTU + 1];
00039
00040
00041 #define LOG(s) program_wide_logger().log(s)
00042
00043
00044
00045
00046 broadcast_spocket_tester::broadcast_spocket_tester
00047 (const internet_address &where, bool unicast)
00048 : _where(new internet_address(where)),
00049 _stack(new tcpip_stack),
00050 _socket(NIL),
00051 _raw(new raw_socket),
00052 _ucast(unicast)
00053 {
00054 }
00055
00056 broadcast_spocket_tester::~broadcast_spocket_tester()
00057 {
00058 WHACK(_socket);
00059 WHACK(_stack);
00060 WHACK(_where);
00061 WHACK(_raw);
00062 }
00063
00064 bool broadcast_spocket_tester::connect()
00065 {
00066 spocket::sock_types type = spocket::BROADCAST;
00067 if (_ucast) type = spocket::UNICAST;
00068 if (!_socket) {
00069 _socket = new spocket(*_where, type);
00070 }
00071 outcome ret = _socket->connect();
00072 return ret == spocket::OKAY;
00073 }
00074
00075 bool broadcast_spocket_tester::do_a_send(const internet_address &where_to,
00076 byte *buffer, int size, testing_statistics &stats)
00077 {
00078 time_stamp start_time;
00079 int len_sent;
00080 time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
00081 outcome worked;
00082
00083 #ifdef DEBUG_SPOCKET_TESTER
00084 LOG(isprintf("into do a send with %d bytes", size));
00085 #endif
00086
00087 while (time_stamp() < when_to_leave) {
00088 worked = _socket->send_to(where_to, buffer, size, len_sent);
00089 if (worked == spocket::NONE_READY) {
00090 portable::sleep_ms(20);
00091 continue;
00092 } else if (worked == spocket::PARTIAL) {
00093
00094 buffer += len_sent;
00095 size -= len_sent;
00096 portable::sleep_ms(20);
00097 continue;
00098 } else break;
00099 }
00100 #ifdef DEBUG_SPOCKET_TESTER
00101 LOG("got out of loop");
00102 #endif
00103
00104 stats.send_time += int(time_stamp().value() - start_time.value());
00105
00106 if ( (worked != spocket::OKAY) || !len_sent) {
00107 LOG("No data went out on the socket.");
00108 return false;
00109 }
00110 if (len_sent != size) {
00111 LOG(isprintf("The full chunk didn't get sent out: %d bytes instead of %d",
00112 len_sent, size));
00113
00114 return false;
00115 }
00116 stats.bytes_sent += len_sent;
00117
00118 time_stamp end_time;
00119
00120
00121 return true;
00122 }
00123
00124 bool broadcast_spocket_tester::do_a_receive(int size_expected,
00125 testing_statistics &stats)
00126 {
00127 time_stamp start_time;
00128
00129 #ifdef DEBUG_SPOCKET_TESTER
00130 LOG("into do a rcv");
00131 #endif
00132
00133 time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
00134 int full_length = 0;
00135 while ( (full_length < size_expected) && (time_stamp() < when_to_leave) ) {
00136 time_stamp start_of_receive;
00137 int len = MAXIMUM_WINSOCK_MTU;
00138 internet_address where_from;
00139 outcome ret = _socket->receive_from(receive_buffer, len, where_from);
00141 if (ret != spocket::OKAY) {
00142 if (ret == spocket::NONE_READY) {
00143 portable::sleep_ms(20);
00144 continue;
00145 } else break;
00146 }
00147
00148 if (ret == spocket::OKAY)
00149 when_to_leave.reset(MAXIMUM_TRANSFER_WAIT);
00150
00151 int receive_duration = int(time_stamp().value()
00152 - start_of_receive.value());
00153 stats.receive_time += receive_duration;
00154
00155 #ifdef DEBUG_SPOCKET_TESTER
00156 LOG(isprintf("did recv, len=%d", len));
00157 #endif
00158
00159 if (!len) {
00160 LOG("Our socket has been disconnected.");
00161 return false;
00162 } else if (len < 0) {
00163 if (errno == SOCK_EWOULDBLOCK) continue;
00164 LOG(istring("The receive failed with an error ")
00165 + portable::system_error_text(errno));
00166 return false;
00167 }
00168 full_length += len;
00169 stats.bytes_received += len;
00170 }
00171
00172 if (full_length != size_expected)
00173 LOG(isprintf("Did not get the full size expected (wanted %d and "
00174 "got %d bytes).", size_expected, full_length));
00175
00176 return true;
00177 }
00178
00179 bool broadcast_spocket_tester::perform_test(const internet_address &dest,
00180 int size, int count, testing_statistics &stats)
00181 {
00182 #ifdef DEBUG_SPOCKET_TESTER
00183 LOG("into perf test");
00184 #endif
00185
00186
00187 static byte garbage_buffer[MAXIMUM_WINSOCK_MTU + 1];
00188 static bool garbage_initialized = false;
00189 chaos randomizer;
00190
00191
00192
00193 if (!garbage_initialized) {
00194
00195 for (int i = 0; i <= MAXIMUM_WINSOCK_MTU; i++)
00196 garbage_buffer[i] = randomizer.inclusive(0, 255);
00197 garbage_initialized = true;
00198 }
00199
00200
00201 stats.total_runs = 0;
00202 stats.send_time = 0;
00203 stats.receive_time = 0;
00204 stats.bytes_sent = 0;
00205 stats.bytes_received = 0;
00206
00207
00208 if (size > MAXIMUM_WINSOCK_MTU) {
00209 LOG("The size is over our limit. To fix this, edit the "
00210 "send_data function.");
00211 return false;
00212 }
00213
00214
00215 if (!_socket) {
00216 LOG("One cannot send data on an uninitialized tester!");
00217 return false;
00218 }
00219
00220 int runs_completed = 0;
00221
00222
00223 while (runs_completed < count) {
00224 #ifdef DEBUG_SPOCKET_TESTER
00225 LOG(isprintf("iter %d", runs_completed));
00226 #endif
00227
00228 time_stamp trip_start;
00229 #ifdef DEBUG_SPOCKET_TESTER
00230 LOG("client about to send");
00231 #endif
00232 if (!do_a_send(dest, garbage_buffer, size, stats)) {
00233 LOG("We failed on a send. Now quitting.");
00234 return false;
00235 }
00236 #ifdef DEBUG_SPOCKET_TESTER
00237 LOG("client about to rcv");
00238 #endif
00239 if (!do_a_receive(size, stats)) {
00240 LOG("We failed on a receive. Now quitting.");
00241 return false;
00242 }
00243 stats.round_trip_time += int(time_stamp().value() - trip_start.value());
00244
00245 runs_completed++;
00246 stats.total_runs++;
00247 if ( !(runs_completed % 10) )
00248 LOG(isprintf("Completed test #%d.", runs_completed));
00249 }
00250
00251 return true;
00252 }
00253