blob: 11591a6540cbb0ed8f28ed97382494f62dd2c735 [file] [log] [blame]
Junxiao Shi1688ded2015-03-29 10:09:26 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2015, Arizona Board of Regents.
4 *
5 * This file is part of ndn-tools (Named Data Networking Essential Tools).
6 * See AUTHORS.md for complete list of ndn-tools authors and contributors.
7 *
8 * ndn-tools is free software: you can redistribute it and/or modify it under the terms
9 * of the GNU General Public License as published by the Free Software Foundation,
10 * either version 3 of the License, or (at your option) any later version.
11 *
12 * ndn-tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * ndn-tools, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
18 */
Junxiao Shi11b2ae92015-03-21 11:29:19 -070019/**
20 * Copyright (C) 2014 Arizona Board of Regents
21 *
22 * This file is part of ndn-tlv-ping (Ping Application for Named Data Networking).
23 *
24 * ndn-tlv-ping is a free software: you can redistribute it and/or modify it under
25 * the terms of the GNU General Public License as published by the Free Software
26 * Foundation, either version 3 of the License, or (at your option) any later version.
27 *
28 * ndn-tlv-ping is distributed in the hope that it will be useful, but WITHOUT ANY
29 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
30 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License along with
33 * ndn-tlv-ping, e.g., in LICENSE file. If not, see <http://www.gnu.org/licenses/>.
34 *
35 * @author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
36 */
37
38#include <ndn-cxx/face.hpp>
39#include <ndn-cxx/name.hpp>
40
41#include <boost/asio.hpp>
42#include <boost/bind.hpp>
43#include <boost/date_time/posix_time/posix_time.hpp>
44#include <boost/noncopyable.hpp>
45
46namespace ndn {
Junxiao Shi0c75f992015-03-24 21:39:47 -070047namespace ping {
Junxiao Shi11b2ae92015-03-21 11:29:19 -070048
Junxiao Shi0c75f992015-03-24 21:39:47 -070049class NdnPing : boost::noncopyable
Junxiao Shi11b2ae92015-03-21 11:29:19 -070050{
51public:
Junxiao Shi11b2ae92015-03-21 11:29:19 -070052 explicit
Junxiao Shi0c75f992015-03-24 21:39:47 -070053 NdnPing(char* programName)
Junxiao Shi11b2ae92015-03-21 11:29:19 -070054 : m_programName(programName)
55 , m_isAllowCachingSet(false)
56 , m_isPrintTimestampSet(false)
57 , m_hasError(false)
58 , m_totalPings(-1)
59 , m_startPingNumber(-1)
60 , m_pingsSent(0)
61 , m_pingsReceived(0)
62 , m_pingInterval(getPingMinimumInterval())
63 , m_clientIdentifier(0)
64 , m_pingTimeoutThreshold(getPingTimeoutThreshold())
65 , m_outstanding(0)
66 , m_face(m_ioService)
67 {
68 }
69
70 class PingStatistics : boost::noncopyable
71 {
72 public:
Junxiao Shi11b2ae92015-03-21 11:29:19 -070073 explicit
74 PingStatistics()
75 : m_sentPings(0)
76 , m_receivedPings(0)
77 , m_pingStartTime(time::steady_clock::now())
78 , m_minimumRoundTripTime(std::numeric_limits<double>::max())
79 , m_averageRoundTripTimeData(0)
80 , m_standardDeviationRoundTripTimeData(0)
81 {
82 }
83
84 void
85 addToPingStatistics(time::nanoseconds roundTripNanoseconds)
86 {
87 double roundTripTime = roundTripNanoseconds.count() / 1000000.0;
88 if (roundTripTime < m_minimumRoundTripTime)
89 m_minimumRoundTripTime = roundTripTime;
90 if (roundTripTime > m_maximumRoundTripTime)
91 m_maximumRoundTripTime = roundTripTime;
Junxiao Shi0c75f992015-03-24 21:39:47 -070092
Junxiao Shi11b2ae92015-03-21 11:29:19 -070093 m_averageRoundTripTimeData += roundTripTime;
Junxiao Shi0c75f992015-03-24 21:39:47 -070094 m_standardDeviationRoundTripTimeData += roundTripTime * roundTripTime;
Junxiao Shi11b2ae92015-03-21 11:29:19 -070095 }
96
Junxiao Shi0c75f992015-03-24 21:39:47 -070097 public:
Junxiao Shi11b2ae92015-03-21 11:29:19 -070098 int m_sentPings;
99 int m_receivedPings;
100 time::steady_clock::TimePoint m_pingStartTime;
101 double m_minimumRoundTripTime;
102 double m_maximumRoundTripTime;
103 double m_averageRoundTripTimeData;
104 double m_standardDeviationRoundTripTimeData;
105
106 };
107
108 void
109 usage()
110 {
111 std::cout << "\n Usage:\n " << m_programName << " ndn:/name/prefix [options]\n"
112 " Ping a NDN name prefix using Interests with name"
113 " ndn:/name/prefix/ping/number.\n"
114 " The numbers in the Interests are randomly generated unless specified.\n"
115 " [-i interval] - set ping interval in seconds (minimum "
116 << getPingMinimumInterval().count() << " milliseconds)\n"
117 " [-c count] - set total number of pings\n"
118 " [-n number] - set the starting number, the number is incremented by 1"
119 " after each Interest\n"
120 " [-p identifier] - add identifier to the Interest names before the"
121 " numbers to avoid conflict\n"
122 " [-a] - allow routers to return stale Data from cache\n"
123 " [-t] - print timestamp with messages\n"
124 " [-h] - print this message and exit\n\n";
125 exit(1);
126 }
127
128 time::milliseconds
129 getPingMinimumInterval()
130 {
131 return time::milliseconds(1000);
132 }
133
134 time::milliseconds
135 getPingTimeoutThreshold()
136 {
137 return time::milliseconds(4000);
138 }
139
140 void
141 setTotalPings(int totalPings)
142 {
143 if (totalPings <= 0)
144 usage();
Junxiao Shi0c75f992015-03-24 21:39:47 -0700145
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700146 m_totalPings = totalPings;
147 }
148
149 void
150 setPingInterval(int pingInterval)
151 {
152 if (pingInterval < getPingMinimumInterval().count())
153 usage();
Junxiao Shi0c75f992015-03-24 21:39:47 -0700154
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700155 m_pingInterval = time::milliseconds(pingInterval);
156 }
157
158 void
159 setStartPingNumber(int64_t startPingNumber)
160 {
161 if (startPingNumber < 0)
162 usage();
Junxiao Shi0c75f992015-03-24 21:39:47 -0700163
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700164 m_startPingNumber = startPingNumber;
165 }
166
167 void
168 setAllowCaching()
169 {
170 m_isAllowCachingSet = true;
171 }
172
173 void
174 setPrintTimestamp()
175 {
176 m_isPrintTimestampSet = true;
177 }
178
179 void
180 setClientIdentifier(char* clientIdentifier)
181 {
182 m_clientIdentifier = clientIdentifier;
Junxiao Shi0c75f992015-03-24 21:39:47 -0700183
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700184 if (strlen(clientIdentifier) == 0)
185 usage();
Junxiao Shi0c75f992015-03-24 21:39:47 -0700186
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700187 while (*clientIdentifier != '\0') {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700188 if (isalnum(*clientIdentifier) == 0)
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700189 usage();
190 clientIdentifier++;
191 }
192 }
193
194 void
195 setPrefix(char* prefix)
196 {
197 m_prefix = prefix;
198 }
199
200 bool
201 hasError() const
202 {
203 return m_hasError;
204 }
205
206
207 void
208 onData(const ndn::Interest& interest,
209 Data& data,
210 time::steady_clock::TimePoint timePoint)
211 {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700212 std::string pingReference = interest.getName().toUri();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700213 m_pingsReceived++;
214 m_pingStatistics.m_receivedPings++;
Junxiao Shi0c75f992015-03-24 21:39:47 -0700215 time::nanoseconds roundTripTime = time::steady_clock::now() - timePoint;
216
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700217 if (m_isPrintTimestampSet)
218 std::cout << time::toIsoString(time::system_clock::now()) << " - ";
219 std::cout << "Content From " << m_prefix;
220 std::cout << " - Ping Reference = " <<
221 interest.getName().getSubName(interest.getName().size()-1).toUri().substr(1);
222 std::cout << " \t- Round Trip Time = " <<
223 roundTripTime.count() / 1000000.0 << " ms" << std::endl;
Junxiao Shi0c75f992015-03-24 21:39:47 -0700224
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700225 m_pingStatistics.addToPingStatistics(roundTripTime);
226 this->finish();
227 }
228
229 void
230 onTimeout(const ndn::Interest& interest)
231 {
232 if (m_isPrintTimestampSet)
233 std::cout << time::toIsoString(time::system_clock::now()) << " - ";
234 std::cout << "Timeout From " << m_prefix;
235 std::cout << " - Ping Reference = " <<
236 interest.getName().getSubName(interest.getName().size()-1).toUri().substr(1);
237 std::cout << std::endl;
Junxiao Shi0c75f992015-03-24 21:39:47 -0700238
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700239 this->finish();
240 }
241
242 void
243 printPingStatistics()
244 {
245 std::cout << "\n\n=== " << " Ping Statistics For "<< m_prefix <<" ===" << std::endl;
246 std::cout << "Sent=" << m_pingStatistics.m_sentPings;
247 std::cout << ", Received=" << m_pingStatistics.m_receivedPings;
248 double packetLossRate = m_pingStatistics.m_sentPings - m_pingStatistics.m_receivedPings;
249 packetLossRate /= m_pingStatistics.m_sentPings;
250 std::cout << ", Packet Loss=" << packetLossRate * 100.0 << "%";
251 if (m_pingStatistics.m_sentPings != m_pingStatistics.m_receivedPings)
252 m_hasError = true;
253 std::cout << ", Total Time=" << m_pingStatistics.m_averageRoundTripTimeData << " ms\n";
254 if (m_pingStatistics.m_receivedPings > 0) {
255 double averageRoundTripTime =
256 m_pingStatistics.m_averageRoundTripTimeData / m_pingStatistics.m_receivedPings;
257 double standardDeviationRoundTripTime =
258 m_pingStatistics.m_standardDeviationRoundTripTimeData / m_pingStatistics.m_receivedPings;
259 standardDeviationRoundTripTime -= averageRoundTripTime * averageRoundTripTime;
260 standardDeviationRoundTripTime = std::sqrt(standardDeviationRoundTripTime);
261 std::cout << "Round Trip Time (Min/Max/Avg/MDev) = (";
262 std::cout << m_pingStatistics.m_minimumRoundTripTime << "/";
263 std::cout << m_pingStatistics.m_maximumRoundTripTime << "/";
264 std::cout << averageRoundTripTime << "/";
265 std::cout << standardDeviationRoundTripTime << ") ms\n";
266 }
267 std::cout << std::endl;
268 }
269
270 void
271 performPing(boost::asio::deadline_timer* deadlineTimer)
272 {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700273 if ((m_totalPings < 0) || (m_pingsSent < m_totalPings)) {
274 m_pingsSent++;
275 m_pingStatistics.m_sentPings++;
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700276
Junxiao Shi0c75f992015-03-24 21:39:47 -0700277 //Perform Ping
278 char pingNumberString[20];
279 Name pingPacketName(m_prefix);
280 pingPacketName.append("ping");
281 if (m_clientIdentifier != 0)
282 pingPacketName.append(m_clientIdentifier);
283 std::memset(pingNumberString, 0, 20);
284 if (m_startPingNumber < 0)
285 m_startPingNumber = std::rand();
286 sprintf(pingNumberString, "%lld", static_cast<long long int>(m_startPingNumber));
287 pingPacketName.append(pingNumberString);
288
289 ndn::Interest interest(pingPacketName);
290
291 if (m_isAllowCachingSet)
292 interest.setMustBeFresh(false);
293 else
294 interest.setMustBeFresh(true);
295
296 interest.setInterestLifetime(m_pingTimeoutThreshold);
297 interest.setNonce(m_startPingNumber);
298
299 m_startPingNumber++;
300
301 try {
302 m_face.expressInterest(interest,
303 std::bind(&NdnPing::onData, this, _1, _2,
304 time::steady_clock::now()),
305 std::bind(&NdnPing::onTimeout, this, _1));
306 deadlineTimer->expires_at(deadlineTimer->expires_at() +
307 boost::posix_time::millisec(m_pingInterval.count()));
308 deadlineTimer->async_wait(bind(&NdnPing::performPing,
309 this,
310 deadlineTimer));
311 }
312 catch (std::exception& e) {
313 std::cerr << "ERROR: " << e.what() << std::endl;
314 }
315 ++m_outstanding;
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700316 }
317 else {
318 this->finish();
319 }
320 }
321
322 void
323 finish()
324 {
325 if (--m_outstanding >= 0) {
326 return;
327 }
328 m_face.shutdown();
329 printPingStatistics();
330 m_ioService.stop();
331 }
332
333 void
334 signalHandler()
335 {
336 m_face.shutdown();
337 printPingStatistics();
338 exit(1);
339 }
340
341 void
342 run()
343 {
344 std::cout << "\n=== Pinging " << m_prefix << " ===\n" <<std::endl;
345
346 boost::asio::signal_set signalSet(m_ioService, SIGINT, SIGTERM);
Junxiao Shi0c75f992015-03-24 21:39:47 -0700347 signalSet.async_wait(bind(&NdnPing::signalHandler, this));
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700348
349 boost::asio::deadline_timer deadlineTimer(m_ioService,
350 boost::posix_time::millisec(0));
351
Junxiao Shi0c75f992015-03-24 21:39:47 -0700352 deadlineTimer.async_wait(bind(&NdnPing::performPing,
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700353 this,
354 &deadlineTimer));
355 try {
356 m_face.processEvents();
357 }
358 catch (std::exception& e) {
359 std::cerr << "ERROR: " << e.what() << std::endl;
360 m_hasError = true;
361 m_ioService.stop();
362 }
363 }
364
365private:
366 char* m_programName;
367 bool m_isAllowCachingSet;
368 bool m_isPrintTimestampSet;
369 bool m_hasError;
370 int m_totalPings;
371 int64_t m_startPingNumber;
372 int m_pingsSent;
373 int m_pingsReceived;
374 time::milliseconds m_pingInterval;
375 char* m_clientIdentifier;
376 time::milliseconds m_pingTimeoutThreshold;
377 char* m_prefix;
378 PingStatistics m_pingStatistics;
379 ssize_t m_outstanding;
380
381 boost::asio::io_service m_ioService;
382 Face m_face;
383};
384
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700385int
386main(int argc, char* argv[])
387{
388 std::srand(::time(0));
389 int res;
390
Junxiao Shi0c75f992015-03-24 21:39:47 -0700391 NdnPing program(argv[0]);
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700392 while ((res = getopt(argc, argv, "htai:c:n:p:")) != -1)
393 {
394 switch (res) {
395 case 'a':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700396 program.setAllowCaching();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700397 break;
398 case 'c':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700399 program.setTotalPings(atoi(optarg));
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700400 break;
401 case 'h':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700402 program.usage();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700403 break;
404 case 'i':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700405 program.setPingInterval(atoi(optarg));
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700406 break;
407 case 'n':
408 try {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700409 program.setStartPingNumber(boost::lexical_cast<int64_t>(optarg));
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700410 }
411 catch (boost::bad_lexical_cast&) {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700412 program.usage();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700413 }
414 break;
415 case 'p':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700416 program.setClientIdentifier(optarg);
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700417 break;
418 case 't':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700419 program.setPrintTimestamp();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700420 break;
421 default:
Junxiao Shi0c75f992015-03-24 21:39:47 -0700422 program.usage();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700423 break;
424 }
425 }
426
427 argc -= optind;
428 argv += optind;
429
430 if (argv[0] == 0)
Junxiao Shi0c75f992015-03-24 21:39:47 -0700431 program.usage();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700432
Junxiao Shi0c75f992015-03-24 21:39:47 -0700433 program.setPrefix(argv[0]);
434 program.run();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700435
436 std::cout << std::endl;
437
Junxiao Shi0c75f992015-03-24 21:39:47 -0700438 if (program.hasError())
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700439 return 1;
440 else
441 return 0;
442}
Junxiao Shi0c75f992015-03-24 21:39:47 -0700443
444} // namespace ping
445} // namespace ndn
446
447int
448main(int argc, char** argv)
449{
450 return ndn::ping::main(argc, argv);
451}