IBR-DTNSuite
0.8
|
00001 /* 00002 * dtnping.cpp 00003 * 00004 * Created on: 24.06.2009 00005 * Author: morgenro 00006 */ 00007 00008 #include "config.h" 00009 #include "ibrdtn/api/Client.h" 00010 #include "ibrdtn/api/StringBundle.h" 00011 #include "ibrcommon/net/tcpclient.h" 00012 #include "ibrcommon/thread/Mutex.h" 00013 #include "ibrcommon/thread/MutexLock.h" 00014 #include "ibrcommon/TimeMeasurement.h" 00015 00016 #include <iostream> 00017 #include <csignal> 00018 #include <stdint.h> 00019 00020 #define CREATE_CHUNK_SIZE 2048 00021 00022 class EchoClient : public dtn::api::Client 00023 { 00024 public: 00025 EchoClient(dtn::api::Client::COMMUNICATION_MODE mode, string app, ibrcommon::tcpstream &stream) 00026 : dtn::api::Client(app, stream, mode), _stream(stream) 00027 { 00028 seq=0; 00029 } 00030 00031 virtual ~EchoClient() 00032 { 00033 } 00034 00035 const dtn::api::Bundle waitForReply(const int timeout) 00036 { 00037 double wait=(timeout*1000); 00038 ibrcommon::TimeMeasurement tm; 00039 while ( wait > 0 ) 00040 { 00041 try { 00042 tm.start(); 00043 dtn::api::Bundle b = this->getBundle((int)(wait/1000)); 00044 tm.stop(); 00045 checkReply(b); 00046 return b; 00047 } catch (const std::string &errmsg) { 00048 std::cerr << errmsg << std::endl; 00049 } 00050 wait=wait-tm.getMilliseconds(); 00051 } 00052 throw ibrcommon::Exception("timeout is set to zero"); 00053 } 00054 00055 void echo(EID destination, int size, int lifetime, bool encryption = false, bool sign = false) 00056 { 00057 lastdestination=destination.getString(); 00058 seq++; 00059 00060 // create a bundle 00061 dtn::api::StringBundle b(destination); 00062 00063 // enable encryption if requested 00064 if (encryption) b.requestEncryption(); 00065 00066 // enable signature if requested 00067 if (sign) b.requestSigned(); 00068 00069 // set lifetime 00070 b.setLifetime(lifetime); 00071 00072 //Add magic seqnr. Hmm, how to to do this without string? 00073 b.append(string((char *)(&seq),4)); 00074 00075 if (size > 4) 00076 { 00077 size-=4; 00078 00079 // create testing pattern, chunkwise to ocnserve memory 00080 char pattern[CREATE_CHUNK_SIZE]; 00081 for (size_t i = 0; i < sizeof(pattern); i++) 00082 { 00083 pattern[i] = '0'; 00084 pattern[i] += i % 10; 00085 } 00086 string chunk=string(pattern,CREATE_CHUNK_SIZE); 00087 00088 while (size > CREATE_CHUNK_SIZE) { 00089 b.append(chunk); 00090 size-=CREATE_CHUNK_SIZE; 00091 } 00092 b.append(chunk.substr(0,size)); 00093 } 00094 00095 // send the bundle 00096 (*this) << b; 00097 00098 // ... flush out 00099 flush(); 00100 00101 } 00102 00103 void checkReply(dtn::api::Bundle &bundle) { 00104 size_t reply_seq = 0; 00105 ibrcommon::BLOB::Reference blob = bundle.getData(); 00106 blob.iostream()->read((char *)(&reply_seq),4 ); 00107 00108 if (reply_seq != seq) { 00109 std::stringstream ss; 00110 ss << "sequence number mismatch, awaited " << seq << ", got " << reply_seq; 00111 throw ss.str(); 00112 } 00113 if (bundle.getSource().getString() != lastdestination) { 00114 throw std::string("ignoring bundle from source " + bundle.getSource().getString() + " awaited " + lastdestination); 00115 } 00116 } 00117 00118 private: 00119 ibrcommon::tcpstream &_stream; 00120 uint32_t seq; 00121 string lastdestination; 00122 }; 00123 00124 00125 00126 void print_help() 00127 { 00128 cout << "-- dtnping (IBR-DTN) --" << endl; 00129 cout << "Syntax: dtnping [options] <dst>" << endl; 00130 cout << " <dst> set the destination eid (e.g. dtn://node/echo)" << endl; 00131 cout << "* optional parameters *" << endl; 00132 cout << " -h|--help display this text" << endl; 00133 cout << " --src <name> set the source application name (e.g. echo-client)" << endl; 00134 cout << " --nowait do not wait for a reply" << endl; 00135 cout << " --abortfail Abort after first packetloss" << endl; 00136 cout << " --size the size of the payload" << endl; 00137 cout << " --count X send X echo in a row" << endl; 00138 cout << " --delay X delay (seconds) after a successful response" << endl; 00139 cout << " --lifetime <seconds> set the lifetime of outgoing bundles; default: 30" << endl; 00140 cout << " --encrypt request encryption on the bundle layer" << endl; 00141 cout << " --sign request signature on the bundle layer" << endl; 00142 cout << " -U <socket> use UNIX domain sockets" << endl; 00143 } 00144 00145 size_t _received = 0, _transmitted = 0; 00146 double _min = 0.0, _max = 0.0, _avg = 0.0; 00147 ibrcommon::TimeMeasurement _runtime; 00148 ibrcommon::Conditional __pause; 00149 dtn::api::Client *__client = NULL; 00150 00151 EID _addr; 00152 bool __exit = false; 00153 00154 void print_summary() 00155 { 00156 _runtime.stop(); 00157 00158 double loss = 0; if (_transmitted > 0) loss = (((double)_transmitted - (double)_received) / (double)_transmitted) * 100.0; 00159 double avg_value = 0; if (_received > 0) avg_value = (_avg/_received); 00160 00161 std::cout << std::endl << "--- " << _addr.getString() << " echo statistics --- " << std::endl; 00162 std::cout << _transmitted << " bundles transmitted, " << _received << " received, " << loss << "% bundle loss, time " << _runtime << std::endl; 00163 std::cout << "rtt min/avg/max = "; 00164 ibrcommon::TimeMeasurement::format(std::cout, _min) << "/"; 00165 ibrcommon::TimeMeasurement::format(std::cout, avg_value) << "/"; 00166 ibrcommon::TimeMeasurement::format(std::cout, _max) << " ms" << std::endl; 00167 } 00168 00169 void term(int signal) 00170 { 00171 if (signal >= 1) 00172 { 00173 if (!__exit) 00174 { 00175 ibrcommon::MutexLock l(__pause); 00176 if (__client != NULL) __client->abort(); 00177 __exit = true; 00178 __pause.abort(); 00179 } 00180 } 00181 } 00182 00183 int main(int argc, char *argv[]) 00184 { 00185 // catch process signals 00186 signal(SIGINT, term); 00187 signal(SIGTERM, term); 00188 00189 string ping_destination = "dtn://local/echo"; 00190 string ping_source = ""; 00191 int ping_size = 64; 00192 unsigned int lifetime = 30; 00193 bool wait_for_reply = true; 00194 bool stop_after_first_fail = false; 00195 bool nonstop = true; 00196 size_t interval_pause = 1; 00197 size_t count = 0; 00198 dtn::api::Client::COMMUNICATION_MODE mode = dtn::api::Client::MODE_BIDIRECTIONAL; 00199 ibrcommon::File unixdomain; 00200 bool bundle_encryption = false; 00201 bool bundle_signed = false; 00202 00203 if (argc == 1) 00204 { 00205 print_help(); 00206 return 0; 00207 } 00208 00209 for (int i = 1; i < argc; i++) 00210 { 00211 string arg = argv[i]; 00212 00213 // print help if requested 00214 if ((arg == "-h") || (arg == "--help")) 00215 { 00216 print_help(); 00217 return 0; 00218 } 00219 00220 else if (arg == "--encrypt") 00221 { 00222 bundle_encryption = true; 00223 } 00224 00225 else if (arg == "--sign") 00226 { 00227 bundle_signed = true; 00228 } 00229 00230 else if (arg == "--nowait") 00231 { 00232 mode = dtn::api::Client::MODE_SENDONLY; 00233 wait_for_reply = false; 00234 } 00235 00236 else if ( arg == "--abortfail") { 00237 stop_after_first_fail=true; 00238 } 00239 00240 else if (arg == "--src" && argc > i) 00241 { 00242 ping_source = argv[i + 1]; 00243 i++; 00244 } 00245 00246 else if (arg == "--size" && argc > i) 00247 { 00248 stringstream str_size; 00249 str_size.str( argv[i + 1] ); 00250 str_size >> ping_size; 00251 i++; 00252 } 00253 00254 else if (arg == "--count" && argc > i) 00255 { 00256 stringstream str_count; 00257 str_count.str( argv[i + 1] ); 00258 str_count >> count; 00259 i++; 00260 nonstop = false; 00261 } 00262 00263 else if (arg == "--delay" && argc > i) 00264 { 00265 stringstream str_delay; 00266 str_delay.str( argv[i + 1] ); 00267 str_delay >> interval_pause; 00268 i++; 00269 } 00270 00271 else if (arg == "--lifetime" && argc > i) 00272 { 00273 stringstream data; data << argv[i + 1]; 00274 data >> lifetime; 00275 i++; 00276 } 00277 else if (arg == "-U" && argc > i) 00278 { 00279 if (++i > argc) 00280 { 00281 std::cout << "argument missing!" << std::endl; 00282 return -1; 00283 } 00284 00285 unixdomain = ibrcommon::File(argv[i]); 00286 } 00287 } 00288 00289 // the last parameter is always the destination 00290 ping_destination = argv[argc - 1]; 00291 00292 // target address 00293 _addr = EID(ping_destination); 00294 00295 ibrcommon::TimeMeasurement tm; 00296 00297 00298 try { 00299 // Create a stream to the server using TCP. 00300 ibrcommon::tcpclient conn; 00301 00302 // check if the unixdomain socket exists 00303 if (unixdomain.exists()) 00304 { 00305 // connect to the unix domain socket 00306 conn.open(unixdomain); 00307 } 00308 else 00309 { 00310 // connect to the standard local api port 00311 conn.open("127.0.0.1", 4550); 00312 00313 // enable nodelay option 00314 conn.enableNoDelay(); 00315 } 00316 00317 // Initiate a derivated client 00318 EchoClient client(mode, ping_source, conn); 00319 00320 // set the global client pointer 00321 { 00322 ibrcommon::MutexLock l(__pause); 00323 __client = &client; 00324 } 00325 00326 // Connect to the server. Actually, this function initiate the 00327 // stream protocol by starting the thread and sending the contact header. 00328 client.connect(); 00329 00330 std::cout << "ECHO " << _addr.getString() << " " << ping_size << " bytes of data." << std::endl; 00331 00332 // measure runtime 00333 _runtime.start(); 00334 00335 try { 00336 for (unsigned int i = 0; (i < count) || nonstop; i++) 00337 { 00338 // set sending time 00339 tm.start(); 00340 00341 // Call out a ECHO 00342 client.echo( _addr, ping_size, lifetime, bundle_encryption, bundle_signed ); 00343 _transmitted++; 00344 00345 if (wait_for_reply) 00346 { 00347 try { 00348 try { 00349 dtn::api::Bundle response = client.waitForReply(2*lifetime); 00350 00351 // print out measurement result 00352 tm.stop(); 00353 00354 size_t reply_seq = 0; 00355 size_t payload_size = 0; 00356 00357 // check for min/max/avg 00358 _avg += tm.getMilliseconds(); 00359 if ((_min > tm.getMilliseconds()) || _min == 0) _min = tm.getMilliseconds(); 00360 if ((_max < tm.getMilliseconds()) || _max == 0) _max = tm.getMilliseconds(); 00361 00362 { 00363 ibrcommon::BLOB::Reference blob = response.getData(); 00364 blob.iostream()->read((char *)(&reply_seq),4 ); 00365 payload_size = blob.iostream().size(); 00366 } 00367 00368 std::cout << payload_size << " bytes from " << response.getSource().getString() << ": seq=" << reply_seq << " ttl=" << response.getLifetime() << " time=" << tm << std::endl; 00369 _received++; 00370 } catch (const dtn::api::ConnectionTimeoutException &e) { 00371 std::cout << "Timeout." << std::endl; 00372 } 00373 00374 if (interval_pause > 0) 00375 { 00376 ibrcommon::MutexLock l(__pause); 00377 __pause.wait(interval_pause * 1000); 00378 } 00379 } catch (const ibrcommon::Conditional::ConditionalAbortException &e) { 00380 if (e.reason == ibrcommon::Conditional::ConditionalAbortException::COND_TIMEOUT) 00381 { 00382 continue; 00383 } 00384 // aborted 00385 break; 00386 } catch (const dtn::api::ConnectionAbortedException&) { 00387 // aborted... do a clean shutdown 00388 break; 00389 } catch (const dtn::api::ConnectionTimeoutException &ex) { 00390 if (stop_after_first_fail) 00391 { 00392 std::cout << "No response, aborting." << std::endl; 00393 break; 00394 } 00395 } 00396 } 00397 } 00398 } catch (const dtn::api::ConnectionException&) { 00399 std::cerr << "Disconnected." << std::endl; 00400 } catch (const ibrcommon::IOException&) { 00401 std::cerr << "Error while receiving a bundle." << std::endl; 00402 } 00403 00404 // Shutdown the client connection. 00405 client.close(); 00406 conn.close(); 00407 00408 } catch (const ibrcommon::tcpclient::SocketException&) { 00409 std::cerr << "Can not connect to the daemon. Does it run?" << std::endl; 00410 return -1; 00411 } catch (const std::exception&) { 00412 std::cerr << "unknown error" << std::endl; 00413 } 00414 00415 print_summary(); 00416 00417 return 0; 00418 }