IBR-DTNSuite  0.10
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"
28 
29 #include <iostream>
30 #include <csignal>
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;
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 X send X echo in a row" << endl;
164  cout << " --delay X delay (seconds) after a successful response" << endl;
165  cout << " --lifetime <seconds> set the lifetime of outgoing bundles; default: 30" << endl;
166  cout << " --encrypt request encryption on the bundle layer" << endl;
167  cout << " --sign request signature on the bundle layer" << endl;
168  cout << " -U <socket> use UNIX domain sockets" << endl;
169 }
170 
171 size_t _received = 0, _transmitted = 0;
172 double _min = 0.0, _max = 0.0, _avg = 0.0;
176 
178 bool __exit = false;
179 
181 {
182  _runtime.stop();
183 
184  double loss = 0;
185  if (_transmitted > 0) loss = ((static_cast<double>(_transmitted) - static_cast<double>(_received)) / static_cast<double>(_transmitted)) * 100.0;
186 
187  double avg_value = 0;
188  if (_received > 0) avg_value = ( _avg / static_cast<double>(_received) );
189 
190  std::cout << std::endl << "--- " << _addr.getString() << " echo statistics --- " << std::endl;
191  std::cout << _transmitted << " bundles transmitted, " << _received << " received, " << loss << "% bundle loss, time " << _runtime << std::endl;
192  std::cout << "rtt min/avg/max = ";
193  ibrcommon::TimeMeasurement::format(std::cout, _min) << "/";
194  ibrcommon::TimeMeasurement::format(std::cout, avg_value) << "/";
195  ibrcommon::TimeMeasurement::format(std::cout, _max) << " ms" << std::endl;
196 }
197 
198 void term(int signal)
199 {
200  if (signal >= 1)
201  {
202  if (!__exit)
203  {
204  ibrcommon::MutexLock l(__pause);
205  if (__client != NULL) __client->abort();
206  __exit = true;
207  __pause.abort();
208  }
209  }
210 }
211 
212 int main(int argc, char *argv[])
213 {
214  // catch process signals
215  signal(SIGINT, term);
216  signal(SIGTERM, term);
217 
218  string ping_destination = "dtn://local/echo";
219  string ping_source = "";
220  int ping_size = 64;
221  unsigned int lifetime = 30;
222  bool wait_for_reply = true;
223  bool stop_after_first_fail = false;
224  bool nonstop = true;
225  size_t interval_pause = 1;
226  size_t count = 0;
228  ibrcommon::File unixdomain;
229  bool bundle_encryption = false;
230  bool bundle_signed = false;
231 
232  if (argc == 1)
233  {
234  print_help();
235  return 0;
236  }
237 
238  for (int i = 1; i < argc; ++i)
239  {
240  string arg = argv[i];
241 
242  // print help if requested
243  if ((arg == "-h") || (arg == "--help"))
244  {
245  print_help();
246  return 0;
247  }
248 
249  else if (arg == "--encrypt")
250  {
251  bundle_encryption = true;
252  }
253 
254  else if (arg == "--sign")
255  {
256  bundle_signed = true;
257  }
258 
259  else if (arg == "--nowait")
260  {
262  wait_for_reply = false;
263  }
264 
265  else if ( arg == "--abortfail") {
266  stop_after_first_fail=true;
267  }
268 
269  else if (arg == "--src" && argc > i)
270  {
271  ping_source = argv[i + 1];
272  i++;
273  }
274 
275  else if (arg == "--size" && argc > i)
276  {
277  stringstream str_size;
278  str_size.str( argv[i + 1] );
279  str_size >> ping_size;
280  i++;
281  }
282 
283  else if (arg == "--count" && argc > i)
284  {
285  stringstream str_count;
286  str_count.str( argv[i + 1] );
287  str_count >> count;
288  i++;
289  nonstop = false;
290  }
291 
292  else if (arg == "--delay" && argc > i)
293  {
294  stringstream str_delay;
295  str_delay.str( argv[i + 1] );
296  str_delay >> interval_pause;
297  i++;
298  }
299 
300  else if (arg == "--lifetime" && argc > i)
301  {
302  stringstream data; data << argv[i + 1];
303  data >> lifetime;
304  i++;
305  }
306  else if (arg == "-U" && argc > i)
307  {
308  if (++i > argc)
309  {
310  std::cout << "argument missing!" << std::endl;
311  return -1;
312  }
313 
314  unixdomain = ibrcommon::File(argv[i]);
315  }
316  }
317 
318  // the last parameter is always the destination
319  ping_destination = argv[argc - 1];
320 
321  // target address
322  _addr = EID(ping_destination);
323 
325 
326 
327  try {
328  // Create a stream to the server using TCP.
329  ibrcommon::clientsocket *sock = NULL;
330 
331  // check if the unixdomain socket exists
332  if (unixdomain.exists())
333  {
334  // connect to the unix domain socket
335  sock = new ibrcommon::filesocket(unixdomain);
336  }
337  else
338  {
339  // connect to the standard local api port
340  ibrcommon::vaddress addr("localhost", 4550);
341  sock = new ibrcommon::tcpsocket(addr);
342  }
343 
344  ibrcommon::socketstream conn(sock);
345 
346  // Initiate a derivated client
347  EchoClient client(mode, ping_source, conn);
348 
349  // set the global client pointer
350  {
351  ibrcommon::MutexLock l(__pause);
352  __client = &client;
353  }
354 
355  // Connect to the server. Actually, this function initiate the
356  // stream protocol by starting the thread and sending the contact header.
357  client.connect();
358 
359  std::cout << "ECHO " << _addr.getString() << " " << ping_size << " bytes of data." << std::endl;
360 
361  // measure runtime
362  _runtime.start();
363 
364  try {
365  for (unsigned int i = 0; (i < count) || nonstop; ++i)
366  {
367  // set sending time
368  tm.start();
369 
370  // Call out a ECHO
371  client.echo( _addr, ping_size, lifetime, bundle_encryption, bundle_signed );
372  _transmitted++;
373 
374  if (wait_for_reply)
375  {
376  try {
377  try {
378  dtn::data::Bundle response = client.waitForReply(2*lifetime);
379 
380  // print out measurement result
381  tm.stop();
382 
383  size_t reply_seq = 0;
384  size_t payload_size = 0;
385 
386  // check for min/max/avg
387  _avg += tm.getMilliseconds();
388  if ((_min > tm.getMilliseconds()) || _min == 0) _min = static_cast<double>(tm.getMilliseconds());
389  if ((_max < tm.getMilliseconds()) || _max == 0) _max = static_cast<double>(tm.getMilliseconds());
390 
391  {
392  ibrcommon::BLOB::Reference blob = response.find<dtn::data::PayloadBlock>().getBLOB();
393  blob.iostream()->read((char *)(&reply_seq),4 );
394  payload_size = blob.iostream().size();
395  }
396 
397  std::cout << payload_size << " bytes from " << response.source.getString() << ": seq=" << reply_seq << " ttl=" << response.lifetime.toString() << " time=" << tm << std::endl;
398  _received++;
399  } catch (const dtn::api::ConnectionTimeoutException &e) {
400  if (stop_after_first_fail)
402 
403  std::cout << "Timeout." << std::endl;
404  }
405 
406  if (interval_pause > 0)
407  {
408  ibrcommon::MutexLock l(__pause);
409  __pause.wait(interval_pause * 1000);
410  }
413  {
414  continue;
415  }
416  // aborted
417  break;
418  } catch (const dtn::api::ConnectionAbortedException&) {
419  // aborted... do a clean shutdown
420  break;
421  } catch (const dtn::api::ConnectionTimeoutException &ex) {
422  if (stop_after_first_fail)
423  {
424  std::cout << "No response, aborting." << std::endl;
425  break;
426  }
427  }
428  }
429  }
430  } catch (const dtn::api::ConnectionException&) {
431  std::cerr << "Disconnected." << std::endl;
432  } catch (const ibrcommon::IOException&) {
433  std::cerr << "Error while receiving a bundle." << std::endl;
434  }
435 
436  // Shutdown the client connection.
437  client.close();
438  conn.close();
439  } catch (const ibrcommon::socket_exception&) {
440  std::cerr << "Can not connect to the daemon. Does it run?" << std::endl;
441  return -1;
442  } catch (const std::exception&) {
443  std::cerr << "unknown error" << std::endl;
444  }
445 
446  print_summary();
447 
448  return 0;
449 }