|
IBR-DTNSuite 0.6
|
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 ibrcommon::QueueUnblockedException&) { 00048 throw ibrcommon::Exception("timeout reached"); 00049 } catch (const std::string &errmsg) { 00050 std::cerr << errmsg << std::endl; 00051 } 00052 wait=wait-tm.getMilliseconds(); 00053 } 00054 throw ibrcommon::Exception("timeout is set to zero"); 00055 } 00056 00057 void echo(EID destination, int size, int lifetime, bool encryption = false, bool sign = false) 00058 { 00059 lastdestination=destination.getString(); 00060 seq++; 00061 00062 // create a bundle 00063 dtn::api::StringBundle b(destination); 00064 00065 // enable encryption if requested 00066 if (encryption) b.requestEncryption(); 00067 00068 // enable signature if requested 00069 if (sign) b.requestSigned(); 00070 00071 // set lifetime 00072 b.setLifetime(lifetime); 00073 00074 //Add magic seqnr. Hmm, how to to do this without string? 00075 b.append(string((char *)(&seq),4)); 00076 size-=4; 00077 00078 // create testing pattern, chunkwise to ocnserve memory 00079 char pattern[CREATE_CHUNK_SIZE]; 00080 for (size_t i = 0; i < sizeof(pattern); i++) 00081 { 00082 pattern[i] = '0'; 00083 pattern[i] += i % 10; 00084 } 00085 string chunk=string(pattern,CREATE_CHUNK_SIZE); 00086 00087 while (size > CREATE_CHUNK_SIZE) { 00088 b.append(chunk); 00089 size-=CREATE_CHUNK_SIZE; 00090 } 00091 b.append(chunk.substr(0,size)); 00092 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 << " --lifetime <seconds> set the lifetime of outgoing bundles; default: 30" << endl; 00139 cout << " --encrypt request encryption on the bundle layer" << endl; 00140 cout << " --sign request signature on the bundle layer" << endl; 00141 cout << " -U <socket> use UNIX domain sockets" << endl; 00142 } 00143 00144 size_t _received = 0, _transmitted = 0; 00145 float _min = 0.0, _max = 0.0, _avg = 0.0; 00146 ibrcommon::TimeMeasurement _runtime; 00147 00148 EID _addr; 00149 bool __exit = false; 00150 00151 void print_summary() 00152 { 00153 _runtime.stop(); 00154 00155 float loss = 0; if (_transmitted > 0) loss = ((_transmitted - _received) / _transmitted) * 100.0; 00156 float avg_value = 0; if (_received > 0) avg_value = (_avg/_received); 00157 00158 std::cout << std::endl << "--- " << _addr.getString() << " echo statistics --- " << std::endl; 00159 std::cout << _transmitted << " bundles transmitted, " << _received << " received, " << loss << "% bundle loss, time " << _runtime << std::endl; 00160 std::cout << "rtt min/avg/max = "; 00161 ibrcommon::TimeMeasurement::format(std::cout, _min) << "/"; 00162 ibrcommon::TimeMeasurement::format(std::cout, avg_value) << "/"; 00163 ibrcommon::TimeMeasurement::format(std::cout, _max) << " ms" << std::endl; 00164 } 00165 00166 void term(int signal) 00167 { 00168 if (signal >= 1) 00169 { 00170 if (!__exit) 00171 { 00172 print_summary(); 00173 __exit = true; 00174 } 00175 exit(0); 00176 } 00177 } 00178 00179 int main(int argc, char *argv[]) 00180 { 00181 // catch process signals 00182 signal(SIGINT, term); 00183 signal(SIGTERM, term); 00184 00185 string ping_destination = "dtn://local/echo"; 00186 string ping_source = "echo-client"; 00187 int ping_size = 64; 00188 unsigned int lifetime = 30; 00189 bool wait_for_reply = true; 00190 bool stop_after_first_fail = false; 00191 bool nonstop = true; 00192 size_t count = 0; 00193 dtn::api::Client::COMMUNICATION_MODE mode = dtn::api::Client::MODE_BIDIRECTIONAL; 00194 ibrcommon::File unixdomain; 00195 bool bundle_encryption = false; 00196 bool bundle_signed = false; 00197 00198 if (argc == 1) 00199 { 00200 print_help(); 00201 return 0; 00202 } 00203 00204 for (int i = 1; i < argc; i++) 00205 { 00206 string arg = argv[i]; 00207 00208 // print help if requested 00209 if ((arg == "-h") || (arg == "--help")) 00210 { 00211 print_help(); 00212 return 0; 00213 } 00214 00215 else if (arg == "--encrypt") 00216 { 00217 bundle_encryption = true; 00218 } 00219 00220 else if (arg == "--sign") 00221 { 00222 bundle_signed = true; 00223 } 00224 00225 else if (arg == "--nowait") 00226 { 00227 mode = dtn::api::Client::MODE_SENDONLY; 00228 wait_for_reply = false; 00229 } 00230 00231 else if ( arg == "--abortfail") { 00232 stop_after_first_fail=true; 00233 } 00234 00235 else if (arg == "--src" && argc > i) 00236 { 00237 ping_source = argv[i + 1]; 00238 i++; 00239 } 00240 00241 else if (arg == "--size" && argc > i) 00242 { 00243 stringstream str_size; 00244 str_size.str( argv[i + 1] ); 00245 str_size >> ping_size; 00246 i++; 00247 } 00248 00249 else if (arg == "--count" && argc > i) 00250 { 00251 stringstream str_count; 00252 str_count.str( argv[i + 1] ); 00253 str_count >> count; 00254 i++; 00255 nonstop = false; 00256 } 00257 00258 else if (arg == "--lifetime" && argc > i) 00259 { 00260 stringstream data; data << argv[i + 1]; 00261 data >> lifetime; 00262 i++; 00263 } 00264 else if (arg == "-U" && argc > i) 00265 { 00266 if (++i > argc) 00267 { 00268 std::cout << "argument missing!" << std::endl; 00269 return -1; 00270 } 00271 00272 unixdomain = ibrcommon::File(argv[i]); 00273 } 00274 } 00275 00276 // the last parameter is always the destination 00277 ping_destination = argv[argc - 1]; 00278 00279 // target address 00280 _addr = EID(ping_destination); 00281 00282 ibrcommon::TimeMeasurement tm; 00283 00284 00285 try { 00286 // Create a stream to the server using TCP. 00287 ibrcommon::tcpclient conn; 00288 00289 // check if the unixdomain socket exists 00290 if (unixdomain.exists()) 00291 { 00292 // connect to the unix domain socket 00293 conn.open(unixdomain); 00294 } 00295 else 00296 { 00297 // connect to the standard local api port 00298 conn.open("127.0.0.1", 4550); 00299 00300 // enable nodelay option 00301 conn.enableNoDelay(); 00302 } 00303 00304 // Initiate a derivated client 00305 EchoClient client(mode, ping_source, conn); 00306 00307 // Connect to the server. Actually, this function initiate the 00308 // stream protocol by starting the thread and sending the contact header. 00309 client.connect(); 00310 00311 std::cout << "ECHO " << _addr.getString() << " " << ping_size << " bytes of data." << std::endl; 00312 00313 // measure runtime 00314 _runtime.start(); 00315 00316 try { 00317 for (unsigned int i = 0; (i < count) || nonstop; i++) 00318 { 00319 // set sending time 00320 tm.start(); 00321 00322 // Call out a ECHO 00323 client.echo( _addr, ping_size, lifetime, bundle_encryption, bundle_signed ); 00324 _transmitted++; 00325 00326 if (wait_for_reply) 00327 { 00328 try { 00329 dtn::api::Bundle response = client.waitForReply(2*lifetime); 00330 00331 // print out measurement result 00332 tm.stop(); 00333 00334 size_t reply_seq = 0; 00335 size_t payload_size = 0; 00336 00337 // check for min/max/avg 00338 _avg += tm.getMilliseconds(); 00339 if ((_min > tm.getMilliseconds()) || _min == 0) _min = tm.getMilliseconds(); 00340 if ((_max < tm.getMilliseconds()) || _max == 0) _max = tm.getMilliseconds(); 00341 00342 { 00343 ibrcommon::BLOB::Reference blob = response.getData(); 00344 blob.iostream()->read((char *)(&reply_seq),4 ); 00345 payload_size = blob.iostream().size(); 00346 } 00347 00348 std::cout << payload_size << " bytes from " << response.getSource().getString() << ": seq=" << reply_seq << " ttl=" << response.getLifetime() << " time=" << tm << std::endl; 00349 _received++; 00350 } catch (const ibrcommon::Exception &ex) { 00351 std::cerr << ex.what() << std::endl; 00352 00353 if (stop_after_first_fail) 00354 { 00355 std::cout << "No response, aborting." << std::endl; 00356 break; 00357 } 00358 } 00359 } 00360 00361 if (nonstop) ::sleep(1); 00362 } 00363 } catch (const dtn::api::ConnectionException&) { 00364 std::cerr << "Disconnected." << std::endl; 00365 } catch (const ibrcommon::IOException&) { 00366 std::cerr << "Error while receiving a bundle." << std::endl; 00367 } 00368 00369 // Shutdown the client connection. 00370 client.close(); 00371 conn.close(); 00372 00373 } catch (const ibrcommon::tcpclient::SocketException&) { 00374 std::cerr << "Can not connect to the daemon. Does it run?" << std::endl; 00375 return -1; 00376 } catch (...) { 00377 00378 } 00379 00380 print_summary(); 00381 00382 return 0; 00383 }