blob: 4abfcecfe7ac2a109424b8167ccd90c57c13ad14 [file] [log] [blame]
Junxiao Shi11b2ae92015-03-21 11:29:19 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/**
3 * Copyright (C) 2014 Arizona Board of Regents
4 *
5 * This file is part of ndn-tlv-ping (Ping Application for Named Data Networking).
6 *
7 * ndn-tlv-ping is a free software: you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-tlv-ping is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * ndn-tlv-ping, e.g., in LICENSE file. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
19 */
20
21#include <ndn-cxx/face.hpp>
22#include <ndn-cxx/name.hpp>
23
24#include <boost/asio.hpp>
25#include <boost/bind.hpp>
26#include <boost/date_time/posix_time/posix_time.hpp>
27#include <boost/noncopyable.hpp>
28
29namespace ndn {
Junxiao Shi0c75f992015-03-24 21:39:47 -070030namespace ping {
Junxiao Shi11b2ae92015-03-21 11:29:19 -070031
Junxiao Shi0c75f992015-03-24 21:39:47 -070032class NdnPing : boost::noncopyable
Junxiao Shi11b2ae92015-03-21 11:29:19 -070033{
34public:
Junxiao Shi11b2ae92015-03-21 11:29:19 -070035 explicit
Junxiao Shi0c75f992015-03-24 21:39:47 -070036 NdnPing(char* programName)
Junxiao Shi11b2ae92015-03-21 11:29:19 -070037 : m_programName(programName)
38 , m_isAllowCachingSet(false)
39 , m_isPrintTimestampSet(false)
40 , m_hasError(false)
41 , m_totalPings(-1)
42 , m_startPingNumber(-1)
43 , m_pingsSent(0)
44 , m_pingsReceived(0)
45 , m_pingInterval(getPingMinimumInterval())
46 , m_clientIdentifier(0)
47 , m_pingTimeoutThreshold(getPingTimeoutThreshold())
48 , m_outstanding(0)
49 , m_face(m_ioService)
50 {
51 }
52
53 class PingStatistics : boost::noncopyable
54 {
55 public:
Junxiao Shi11b2ae92015-03-21 11:29:19 -070056 explicit
57 PingStatistics()
58 : m_sentPings(0)
59 , m_receivedPings(0)
60 , m_pingStartTime(time::steady_clock::now())
61 , m_minimumRoundTripTime(std::numeric_limits<double>::max())
62 , m_averageRoundTripTimeData(0)
63 , m_standardDeviationRoundTripTimeData(0)
64 {
65 }
66
67 void
68 addToPingStatistics(time::nanoseconds roundTripNanoseconds)
69 {
70 double roundTripTime = roundTripNanoseconds.count() / 1000000.0;
71 if (roundTripTime < m_minimumRoundTripTime)
72 m_minimumRoundTripTime = roundTripTime;
73 if (roundTripTime > m_maximumRoundTripTime)
74 m_maximumRoundTripTime = roundTripTime;
Junxiao Shi0c75f992015-03-24 21:39:47 -070075
Junxiao Shi11b2ae92015-03-21 11:29:19 -070076 m_averageRoundTripTimeData += roundTripTime;
Junxiao Shi0c75f992015-03-24 21:39:47 -070077 m_standardDeviationRoundTripTimeData += roundTripTime * roundTripTime;
Junxiao Shi11b2ae92015-03-21 11:29:19 -070078 }
79
Junxiao Shi0c75f992015-03-24 21:39:47 -070080 public:
Junxiao Shi11b2ae92015-03-21 11:29:19 -070081 int m_sentPings;
82 int m_receivedPings;
83 time::steady_clock::TimePoint m_pingStartTime;
84 double m_minimumRoundTripTime;
85 double m_maximumRoundTripTime;
86 double m_averageRoundTripTimeData;
87 double m_standardDeviationRoundTripTimeData;
88
89 };
90
91 void
92 usage()
93 {
94 std::cout << "\n Usage:\n " << m_programName << " ndn:/name/prefix [options]\n"
95 " Ping a NDN name prefix using Interests with name"
96 " ndn:/name/prefix/ping/number.\n"
97 " The numbers in the Interests are randomly generated unless specified.\n"
98 " [-i interval] - set ping interval in seconds (minimum "
99 << getPingMinimumInterval().count() << " milliseconds)\n"
100 " [-c count] - set total number of pings\n"
101 " [-n number] - set the starting number, the number is incremented by 1"
102 " after each Interest\n"
103 " [-p identifier] - add identifier to the Interest names before the"
104 " numbers to avoid conflict\n"
105 " [-a] - allow routers to return stale Data from cache\n"
106 " [-t] - print timestamp with messages\n"
107 " [-h] - print this message and exit\n\n";
108 exit(1);
109 }
110
111 time::milliseconds
112 getPingMinimumInterval()
113 {
114 return time::milliseconds(1000);
115 }
116
117 time::milliseconds
118 getPingTimeoutThreshold()
119 {
120 return time::milliseconds(4000);
121 }
122
123 void
124 setTotalPings(int totalPings)
125 {
126 if (totalPings <= 0)
127 usage();
Junxiao Shi0c75f992015-03-24 21:39:47 -0700128
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700129 m_totalPings = totalPings;
130 }
131
132 void
133 setPingInterval(int pingInterval)
134 {
135 if (pingInterval < getPingMinimumInterval().count())
136 usage();
Junxiao Shi0c75f992015-03-24 21:39:47 -0700137
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700138 m_pingInterval = time::milliseconds(pingInterval);
139 }
140
141 void
142 setStartPingNumber(int64_t startPingNumber)
143 {
144 if (startPingNumber < 0)
145 usage();
Junxiao Shi0c75f992015-03-24 21:39:47 -0700146
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700147 m_startPingNumber = startPingNumber;
148 }
149
150 void
151 setAllowCaching()
152 {
153 m_isAllowCachingSet = true;
154 }
155
156 void
157 setPrintTimestamp()
158 {
159 m_isPrintTimestampSet = true;
160 }
161
162 void
163 setClientIdentifier(char* clientIdentifier)
164 {
165 m_clientIdentifier = clientIdentifier;
Junxiao Shi0c75f992015-03-24 21:39:47 -0700166
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700167 if (strlen(clientIdentifier) == 0)
168 usage();
Junxiao Shi0c75f992015-03-24 21:39:47 -0700169
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700170 while (*clientIdentifier != '\0') {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700171 if (isalnum(*clientIdentifier) == 0)
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700172 usage();
173 clientIdentifier++;
174 }
175 }
176
177 void
178 setPrefix(char* prefix)
179 {
180 m_prefix = prefix;
181 }
182
183 bool
184 hasError() const
185 {
186 return m_hasError;
187 }
188
189
190 void
191 onData(const ndn::Interest& interest,
192 Data& data,
193 time::steady_clock::TimePoint timePoint)
194 {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700195 std::string pingReference = interest.getName().toUri();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700196 m_pingsReceived++;
197 m_pingStatistics.m_receivedPings++;
Junxiao Shi0c75f992015-03-24 21:39:47 -0700198 time::nanoseconds roundTripTime = time::steady_clock::now() - timePoint;
199
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700200 if (m_isPrintTimestampSet)
201 std::cout << time::toIsoString(time::system_clock::now()) << " - ";
202 std::cout << "Content From " << m_prefix;
203 std::cout << " - Ping Reference = " <<
204 interest.getName().getSubName(interest.getName().size()-1).toUri().substr(1);
205 std::cout << " \t- Round Trip Time = " <<
206 roundTripTime.count() / 1000000.0 << " ms" << std::endl;
Junxiao Shi0c75f992015-03-24 21:39:47 -0700207
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700208 m_pingStatistics.addToPingStatistics(roundTripTime);
209 this->finish();
210 }
211
212 void
213 onTimeout(const ndn::Interest& interest)
214 {
215 if (m_isPrintTimestampSet)
216 std::cout << time::toIsoString(time::system_clock::now()) << " - ";
217 std::cout << "Timeout From " << m_prefix;
218 std::cout << " - Ping Reference = " <<
219 interest.getName().getSubName(interest.getName().size()-1).toUri().substr(1);
220 std::cout << std::endl;
Junxiao Shi0c75f992015-03-24 21:39:47 -0700221
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700222 this->finish();
223 }
224
225 void
226 printPingStatistics()
227 {
228 std::cout << "\n\n=== " << " Ping Statistics For "<< m_prefix <<" ===" << std::endl;
229 std::cout << "Sent=" << m_pingStatistics.m_sentPings;
230 std::cout << ", Received=" << m_pingStatistics.m_receivedPings;
231 double packetLossRate = m_pingStatistics.m_sentPings - m_pingStatistics.m_receivedPings;
232 packetLossRate /= m_pingStatistics.m_sentPings;
233 std::cout << ", Packet Loss=" << packetLossRate * 100.0 << "%";
234 if (m_pingStatistics.m_sentPings != m_pingStatistics.m_receivedPings)
235 m_hasError = true;
236 std::cout << ", Total Time=" << m_pingStatistics.m_averageRoundTripTimeData << " ms\n";
237 if (m_pingStatistics.m_receivedPings > 0) {
238 double averageRoundTripTime =
239 m_pingStatistics.m_averageRoundTripTimeData / m_pingStatistics.m_receivedPings;
240 double standardDeviationRoundTripTime =
241 m_pingStatistics.m_standardDeviationRoundTripTimeData / m_pingStatistics.m_receivedPings;
242 standardDeviationRoundTripTime -= averageRoundTripTime * averageRoundTripTime;
243 standardDeviationRoundTripTime = std::sqrt(standardDeviationRoundTripTime);
244 std::cout << "Round Trip Time (Min/Max/Avg/MDev) = (";
245 std::cout << m_pingStatistics.m_minimumRoundTripTime << "/";
246 std::cout << m_pingStatistics.m_maximumRoundTripTime << "/";
247 std::cout << averageRoundTripTime << "/";
248 std::cout << standardDeviationRoundTripTime << ") ms\n";
249 }
250 std::cout << std::endl;
251 }
252
253 void
254 performPing(boost::asio::deadline_timer* deadlineTimer)
255 {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700256 if ((m_totalPings < 0) || (m_pingsSent < m_totalPings)) {
257 m_pingsSent++;
258 m_pingStatistics.m_sentPings++;
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700259
Junxiao Shi0c75f992015-03-24 21:39:47 -0700260 //Perform Ping
261 char pingNumberString[20];
262 Name pingPacketName(m_prefix);
263 pingPacketName.append("ping");
264 if (m_clientIdentifier != 0)
265 pingPacketName.append(m_clientIdentifier);
266 std::memset(pingNumberString, 0, 20);
267 if (m_startPingNumber < 0)
268 m_startPingNumber = std::rand();
269 sprintf(pingNumberString, "%lld", static_cast<long long int>(m_startPingNumber));
270 pingPacketName.append(pingNumberString);
271
272 ndn::Interest interest(pingPacketName);
273
274 if (m_isAllowCachingSet)
275 interest.setMustBeFresh(false);
276 else
277 interest.setMustBeFresh(true);
278
279 interest.setInterestLifetime(m_pingTimeoutThreshold);
280 interest.setNonce(m_startPingNumber);
281
282 m_startPingNumber++;
283
284 try {
285 m_face.expressInterest(interest,
286 std::bind(&NdnPing::onData, this, _1, _2,
287 time::steady_clock::now()),
288 std::bind(&NdnPing::onTimeout, this, _1));
289 deadlineTimer->expires_at(deadlineTimer->expires_at() +
290 boost::posix_time::millisec(m_pingInterval.count()));
291 deadlineTimer->async_wait(bind(&NdnPing::performPing,
292 this,
293 deadlineTimer));
294 }
295 catch (std::exception& e) {
296 std::cerr << "ERROR: " << e.what() << std::endl;
297 }
298 ++m_outstanding;
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700299 }
300 else {
301 this->finish();
302 }
303 }
304
305 void
306 finish()
307 {
308 if (--m_outstanding >= 0) {
309 return;
310 }
311 m_face.shutdown();
312 printPingStatistics();
313 m_ioService.stop();
314 }
315
316 void
317 signalHandler()
318 {
319 m_face.shutdown();
320 printPingStatistics();
321 exit(1);
322 }
323
324 void
325 run()
326 {
327 std::cout << "\n=== Pinging " << m_prefix << " ===\n" <<std::endl;
328
329 boost::asio::signal_set signalSet(m_ioService, SIGINT, SIGTERM);
Junxiao Shi0c75f992015-03-24 21:39:47 -0700330 signalSet.async_wait(bind(&NdnPing::signalHandler, this));
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700331
332 boost::asio::deadline_timer deadlineTimer(m_ioService,
333 boost::posix_time::millisec(0));
334
Junxiao Shi0c75f992015-03-24 21:39:47 -0700335 deadlineTimer.async_wait(bind(&NdnPing::performPing,
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700336 this,
337 &deadlineTimer));
338 try {
339 m_face.processEvents();
340 }
341 catch (std::exception& e) {
342 std::cerr << "ERROR: " << e.what() << std::endl;
343 m_hasError = true;
344 m_ioService.stop();
345 }
346 }
347
348private:
349 char* m_programName;
350 bool m_isAllowCachingSet;
351 bool m_isPrintTimestampSet;
352 bool m_hasError;
353 int m_totalPings;
354 int64_t m_startPingNumber;
355 int m_pingsSent;
356 int m_pingsReceived;
357 time::milliseconds m_pingInterval;
358 char* m_clientIdentifier;
359 time::milliseconds m_pingTimeoutThreshold;
360 char* m_prefix;
361 PingStatistics m_pingStatistics;
362 ssize_t m_outstanding;
363
364 boost::asio::io_service m_ioService;
365 Face m_face;
366};
367
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700368int
369main(int argc, char* argv[])
370{
371 std::srand(::time(0));
372 int res;
373
Junxiao Shi0c75f992015-03-24 21:39:47 -0700374 NdnPing program(argv[0]);
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700375 while ((res = getopt(argc, argv, "htai:c:n:p:")) != -1)
376 {
377 switch (res) {
378 case 'a':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700379 program.setAllowCaching();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700380 break;
381 case 'c':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700382 program.setTotalPings(atoi(optarg));
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700383 break;
384 case 'h':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700385 program.usage();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700386 break;
387 case 'i':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700388 program.setPingInterval(atoi(optarg));
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700389 break;
390 case 'n':
391 try {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700392 program.setStartPingNumber(boost::lexical_cast<int64_t>(optarg));
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700393 }
394 catch (boost::bad_lexical_cast&) {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700395 program.usage();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700396 }
397 break;
398 case 'p':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700399 program.setClientIdentifier(optarg);
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700400 break;
401 case 't':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700402 program.setPrintTimestamp();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700403 break;
404 default:
Junxiao Shi0c75f992015-03-24 21:39:47 -0700405 program.usage();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700406 break;
407 }
408 }
409
410 argc -= optind;
411 argv += optind;
412
413 if (argv[0] == 0)
Junxiao Shi0c75f992015-03-24 21:39:47 -0700414 program.usage();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700415
Junxiao Shi0c75f992015-03-24 21:39:47 -0700416 program.setPrefix(argv[0]);
417 program.run();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700418
419 std::cout << std::endl;
420
Junxiao Shi0c75f992015-03-24 21:39:47 -0700421 if (program.hasError())
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700422 return 1;
423 else
424 return 0;
425}
Junxiao Shi0c75f992015-03-24 21:39:47 -0700426
427} // namespace ping
428} // namespace ndn
429
430int
431main(int argc, char** argv)
432{
433 return ndn::ping::main(argc, argv);
434}