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