IBR-DTNSuite  0.8
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 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 }