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                         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 }