IBR-DTNSuite 0.6

tools/src/dtnping.cpp

Go to the documentation of this file.
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 }