IBR-DTNSuite  0.12
dtnping.cpp
Go to the documentation of this file.
1 /*
2  * dtnping.cpp
3  *
4  * Copyright (C) 2011 IBR, TU Braunschweig
5  *
6  * Written-by: Johannes Morgenroth <morgenroth@ibr.cs.tu-bs.de>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21 
22 #include "config.h"
23 #include "ibrdtn/api/Client.h"
24 #include "ibrcommon/net/socket.h"
25 #include "ibrcommon/thread/Mutex.h"
29 
30 #include <iostream>
31 #include <stdint.h>
32 
33 #define CREATE_CHUNK_SIZE 2048
34 
35 class EchoClient : public dtn::api::Client
36 {
37  public:
38  EchoClient(dtn::api::Client::COMMUNICATION_MODE mode, string app, ibrcommon::socketstream &stream)
39  : dtn::api::Client(app, stream, mode), _stream(stream)
40  {
41  seq=0;
42  }
43 
44  virtual ~EchoClient()
45  {
46  }
47 
48  const dtn::data::Bundle waitForReply(const int timeout)
49  {
50  double wait=(timeout*1000);
52  while ( wait > 0 )
53  {
54  try {
55  tm.start();
56  dtn::data::Bundle b = this->getBundle((int)(wait/1000));
57  tm.stop();
58  checkReply(b);
59  return b;
60  } catch (const std::string &errmsg) {
61  std::cerr << errmsg << std::endl;
62  }
63  wait = wait - tm.getMilliseconds();
64  }
65  throw ibrcommon::Exception("timeout is set to zero");
66  }
67 
68  void echo(EID destination, int size, int lifetime, bool encryption = false, bool sign = false)
69  {
70  lastdestination=destination.getString();
71  seq++;
72 
73  // create a bundle
75 
76  // set bundle destination
77  b.destination = destination;
78 
79  // enable encryption if requested
81 
82  // enable signature if requested
84 
85  // set lifetime
86  b.lifetime = lifetime;
87 
88  // create a new blob
90 
91  // append the blob as payload block to the bundle
92  b.push_back(ref);
93 
94  // open the iostream
95  {
96  ibrcommon::BLOB::iostream stream = ref.iostream();
97 
98  // Add magic seqno
99  (*stream).write((char*)&seq, 4);
100 
101  if (size > 4)
102  {
103  size-=4;
104 
105  // create testing pattern, chunkwise to ocnserve memory
106  char pattern[CREATE_CHUNK_SIZE];
107  for (size_t i = 0; i < sizeof(pattern); ++i)
108  {
109  pattern[i] = static_cast<char>(static_cast<int>('0') + (i % 10));
110  }
111 
112  while (size > CREATE_CHUNK_SIZE) {
113  (*stream).write(pattern, CREATE_CHUNK_SIZE);
114  size -= CREATE_CHUNK_SIZE;
115  }
116 
117  (*stream).write(pattern, size);
118  }
119  }
120 
121  // send the bundle
122  (*this) << b;
123 
124  // ... flush out
125  flush();
126 
127  }
128 
129  void checkReply(dtn::data::Bundle &bundle) {
130  size_t reply_seq = 0;
131  ibrcommon::BLOB::Reference blob = bundle.find<dtn::data::PayloadBlock>().getBLOB();
132  blob.iostream()->read((char *)(&reply_seq),4 );
133 
134  if (reply_seq != seq) {
135  std::stringstream ss;
136  ss << "sequence number mismatch, awaited " << seq << ", got " << reply_seq;
137  throw ss.str();
138  }
139  if (bundle.source.getString() != lastdestination) {
140  throw std::string("ignoring bundle from source " + bundle.source.getString() + " awaited " + lastdestination);
141  }
142  }
143 
144  private:
145  ibrcommon::socketstream &_stream;
146  uint32_t seq;
147  string lastdestination;
148 };
149 
150 
151 
153 {
154  cout << "-- dtnping (IBR-DTN) --" << endl;
155  cout << "Syntax: dtnping [options] <dst>" << endl;
156  cout << " <dst> Set the destination eid (e.g. dtn://node/echo)" << endl << endl;
157  cout << "* optional parameters *" << endl;
158  cout << " -h|--help Display this text" << endl;
159  cout << " --src <name> Set the source application name (e.g. echo-client)" << endl;
160  cout << " --nowait Do not wait for a reply" << endl;
161  cout << " --abortfail Abort after first packetloss" << endl;
162  cout << " --size The size of the payload" << endl;
163  cout << " --count <n> Send X echo in a row" << endl;
164  cout << " --delay <seconds>" << endl;
165  cout << " Delay between a received response and the next request" << endl;
166  cout << " --lifetime <seconds>" << endl;
167  cout << " Set the lifetime of outgoing bundles; default: 30" << endl;
168  cout << " --encrypt Request encryption on the bundle layer" << endl;
169  cout << " --sign Request signature on the bundle layer" << endl;
170  cout << " -U <socket> Connect to UNIX domain socket API" << endl;
171 }
172 
173 size_t _received = 0, _transmitted = 0;
174 double _min = 0.0, _max = 0.0, _avg = 0.0;
178 
180 bool __exit = false;
181 
183 {
184  _runtime.stop();
185 
186  double loss = 0;
187  if (_transmitted > 0) loss = ((static_cast<double>(_transmitted) - static_cast<double>(_received)) / static_cast<double>(_transmitted)) * 100.0;
188 
189  double avg_value = 0;
190  if (_received > 0) avg_value = ( _avg / static_cast<double>(_received) );
191 
192  std::cout << std::endl << "--- " << _addr.getString() << " echo statistics --- " << std::endl;
193  std::cout << _transmitted << " bundles transmitted, " << _received << " received, " << loss << "% bundle loss, time " << _runtime << std::endl;
194  std::cout << "rtt min/avg/max = ";
195  ibrcommon::TimeMeasurement::format(std::cout, _min) << "/";
196  ibrcommon::TimeMeasurement::format(std::cout, avg_value) << "/";
197  ibrcommon::TimeMeasurement::format(std::cout, _max) << " ms" << std::endl;
198 }
199 
200 void term(int signal)
201 {
202  if (signal >= 1)
203  {
204  if (!__exit)
205  {
206  ibrcommon::MutexLock l(__pause);
207  if (__client != NULL) __client->abort();
208  __exit = true;
209  __pause.abort();
210  }
211  }
212 }
213 
214 int main(int argc, char *argv[])
215 {
216  // catch process signals
217  ibrcommon::SignalHandler sighandler(term);
218  sighandler.handle(SIGINT);
219  sighandler.handle(SIGTERM);
220  sighandler.initialize();
221 
222  string ping_destination = "dtn://local/echo";
223  string ping_source = "";
224  int ping_size = 64;
225  unsigned int lifetime = 30;
226  bool wait_for_reply = true;
227  bool stop_after_first_fail = false;
228  bool nonstop = true;
229  size_t interval_pause = 1;
230  size_t count = 0;
232  ibrcommon::File unixdomain;
233  bool bundle_encryption = false;
234  bool bundle_signed = false;
235 
236  if (argc == 1)
237  {
238  print_help();
239  return 0;
240  }
241 
242  for (int i = 1; i < argc; ++i)
243  {
244  string arg = argv[i];
245 
246  // print help if requested
247  if ((arg == "-h") || (arg == "--help"))
248  {
249  print_help();
250  return 0;
251  }
252 
253  else if (arg == "--encrypt")
254  {
255  bundle_encryption = true;
256  }
257 
258  else if (arg == "--sign")
259  {
260  bundle_signed = true;
261  }
262 
263  else if (arg == "--nowait")
264  {
266  wait_for_reply = false;
267  }
268 
269  else if ( arg == "--abortfail") {
270  stop_after_first_fail=true;
271  }
272 
273  else if (arg == "--src" && argc > i)
274  {
275  ping_source = argv[i + 1];
276  i++;
277  }
278 
279  else if (arg == "--size" && argc > i)
280  {
281  stringstream str_size;
282  str_size.str( argv[i + 1] );
283  str_size >> ping_size;
284  i++;
285  }
286 
287  else if (arg == "--count" && argc > i)
288  {
289  stringstream str_count;
290  str_count.str( argv[i + 1] );
291  str_count >> count;
292  i++;
293  nonstop = false;
294  }
295 
296  else if (arg == "--delay" && argc > i)
297  {
298  stringstream str_delay;
299  str_delay.str( argv[i + 1] );
300  str_delay >> interval_pause;
301  i++;
302  }
303 
304  else if (arg == "--lifetime" && argc > i)
305  {
306  stringstream data; data << argv[i + 1];
307  data >> lifetime;
308  i++;
309  }
310  else if (arg == "-U" && argc > i)
311  {
312  if (++i > argc)
313  {
314  std::cout << "argument missing!" << std::endl;
315  return -1;
316  }
317 
318  unixdomain = ibrcommon::File(argv[i]);
319  }
320  }
321 
322  // the last parameter is always the destination
323  ping_destination = argv[argc - 1];
324 
325  // target address
326  _addr = EID(ping_destination);
327 
329 
330 
331  try {
332  // Create a stream to the server using TCP.
333  ibrcommon::clientsocket *sock = NULL;
334 
335  // check if the unixdomain socket exists
336  if (unixdomain.exists())
337  {
338  // connect to the unix domain socket
339  sock = new ibrcommon::filesocket(unixdomain);
340  }
341  else
342  {
343  // connect to the standard local api port
344  ibrcommon::vaddress addr("localhost", 4550);
345  sock = new ibrcommon::tcpsocket(addr);
346  }
347 
348  ibrcommon::socketstream conn(sock);
349 
350  // Initiate a derivated client
351  EchoClient client(mode, ping_source, conn);
352 
353  // set the global client pointer
354  {
355  ibrcommon::MutexLock l(__pause);
356  __client = &client;
357  }
358 
359  // Connect to the server. Actually, this function initiate the
360  // stream protocol by starting the thread and sending the contact header.
361  client.connect();
362 
363  std::cout << "ECHO " << _addr.getString() << " " << ping_size << " bytes of data." << std::endl;
364 
365  // measure runtime
366  _runtime.start();
367 
368  try {
369  for (unsigned int i = 0; (i < count) || nonstop; ++i)
370  {
371  // set sending time
372  tm.start();
373 
374  // Call out a ECHO
375  client.echo( _addr, ping_size, lifetime, bundle_encryption, bundle_signed );
376  _transmitted++;
377 
378  if (wait_for_reply)
379  {
380  try {
381  try {
382  dtn::data::Bundle response = client.waitForReply(2*lifetime);
383 
384  // print out measurement result
385  tm.stop();
386 
387  size_t reply_seq = 0;
388  size_t payload_size = 0;
389 
390  // check for min/max/avg
391  _avg += tm.getMilliseconds();
392  if ((_min > tm.getMilliseconds()) || _min == 0) _min = static_cast<double>(tm.getMilliseconds());
393  if ((_max < tm.getMilliseconds()) || _max == 0) _max = static_cast<double>(tm.getMilliseconds());
394 
395  {
396  ibrcommon::BLOB::Reference blob = response.find<dtn::data::PayloadBlock>().getBLOB();
397  blob.iostream()->read((char *)(&reply_seq),4 );
398  payload_size = blob.size();
399  }
400 
401  std::cout << payload_size << " bytes from " << response.source.getString() << ": seq=" << reply_seq << " ttl=" << response.lifetime.toString() << " time=" << tm << std::endl;
402  _received++;
403  } catch (const dtn::api::ConnectionTimeoutException &e) {
404  if (stop_after_first_fail)
406 
407  std::cout << "Timeout." << std::endl;
408  }
409 
410  if (interval_pause > 0)
411  {
412  ibrcommon::MutexLock l(__pause);
413  __pause.wait(interval_pause * 1000);
414  }
417  {
418  continue;
419  }
420  // aborted
421  break;
422  } catch (const dtn::api::ConnectionAbortedException&) {
423  // aborted... do a clean shutdown
424  break;
425  } catch (const dtn::api::ConnectionTimeoutException &ex) {
426  if (stop_after_first_fail)
427  {
428  std::cout << "No response, aborting." << std::endl;
429  break;
430  }
431  }
432  }
433  }
434  } catch (const dtn::api::ConnectionException&) {
435  std::cerr << "Disconnected." << std::endl;
436  } catch (const ibrcommon::IOException&) {
437  std::cerr << "Error while receiving a bundle." << std::endl;
438  }
439 
440  // Shutdown the client connection.
441  client.close();
442  conn.close();
443  } catch (const ibrcommon::socket_exception&) {
444  std::cerr << "Can not connect to the daemon. Does it run?" << std::endl;
445  return -1;
446  } catch (const std::exception&) {
447  std::cerr << "unknown error" << std::endl;
448  }
449 
450  print_summary();
451 
452  return 0;
453 }