blob: 3479e736d79109fec8fd3b3c2cf0c54a3b0319d4 [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 {
30
31class NdnTlvPing : boost::noncopyable
32{
33public:
34
35 explicit
36 NdnTlvPing(char* programName)
37 : 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:
56
57 explicit
58 PingStatistics()
59 : m_sentPings(0)
60 , m_receivedPings(0)
61 , m_pingStartTime(time::steady_clock::now())
62 , m_minimumRoundTripTime(std::numeric_limits<double>::max())
63 , m_averageRoundTripTimeData(0)
64 , m_standardDeviationRoundTripTimeData(0)
65 {
66 }
67
68 void
69 addToPingStatistics(time::nanoseconds roundTripNanoseconds)
70 {
71 double roundTripTime = roundTripNanoseconds.count() / 1000000.0;
72 if (roundTripTime < m_minimumRoundTripTime)
73 m_minimumRoundTripTime = roundTripTime;
74 if (roundTripTime > m_maximumRoundTripTime)
75 m_maximumRoundTripTime = roundTripTime;
76 m_averageRoundTripTimeData += roundTripTime;
77 m_standardDeviationRoundTripTimeData += roundTripTime*roundTripTime;
78 }
79
80 int m_sentPings;
81 int m_receivedPings;
82 time::steady_clock::TimePoint m_pingStartTime;
83 double m_minimumRoundTripTime;
84 double m_maximumRoundTripTime;
85 double m_averageRoundTripTimeData;
86 double m_standardDeviationRoundTripTimeData;
87
88 };
89
90 void
91 usage()
92 {
93 std::cout << "\n Usage:\n " << m_programName << " ndn:/name/prefix [options]\n"
94 " Ping a NDN name prefix using Interests with name"
95 " ndn:/name/prefix/ping/number.\n"
96 " The numbers in the Interests are randomly generated unless specified.\n"
97 " [-i interval] - set ping interval in seconds (minimum "
98 << getPingMinimumInterval().count() << " milliseconds)\n"
99 " [-c count] - set total number of pings\n"
100 " [-n number] - set the starting number, the number is incremented by 1"
101 " after each Interest\n"
102 " [-p identifier] - add identifier to the Interest names before the"
103 " numbers to avoid conflict\n"
104 " [-a] - allow routers to return stale Data from cache\n"
105 " [-t] - print timestamp with messages\n"
106 " [-h] - print this message and exit\n\n";
107 exit(1);
108 }
109
110 time::milliseconds
111 getPingMinimumInterval()
112 {
113 return time::milliseconds(1000);
114 }
115
116 time::milliseconds
117 getPingTimeoutThreshold()
118 {
119 return time::milliseconds(4000);
120 }
121
122 void
123 setTotalPings(int totalPings)
124 {
125 if (totalPings <= 0)
126 usage();
127 m_totalPings = totalPings;
128 }
129
130 void
131 setPingInterval(int pingInterval)
132 {
133 if (pingInterval < getPingMinimumInterval().count())
134 usage();
135 m_pingInterval = time::milliseconds(pingInterval);
136 }
137
138 void
139 setStartPingNumber(int64_t startPingNumber)
140 {
141 if (startPingNumber < 0)
142 usage();
143 m_startPingNumber = startPingNumber;
144 }
145
146 void
147 setAllowCaching()
148 {
149 m_isAllowCachingSet = true;
150 }
151
152 void
153 setPrintTimestamp()
154 {
155 m_isPrintTimestampSet = true;
156 }
157
158 void
159 setClientIdentifier(char* clientIdentifier)
160 {
161 m_clientIdentifier = clientIdentifier;
162 if (strlen(clientIdentifier) == 0)
163 usage();
164 while (*clientIdentifier != '\0') {
165 if( isalnum(*clientIdentifier) == 0 )
166 usage();
167 clientIdentifier++;
168 }
169 }
170
171 void
172 setPrefix(char* prefix)
173 {
174 m_prefix = prefix;
175 }
176
177 bool
178 hasError() const
179 {
180 return m_hasError;
181 }
182
183
184 void
185 onData(const ndn::Interest& interest,
186 Data& data,
187 time::steady_clock::TimePoint timePoint)
188 {
189 std::string pingReference;
190 time::nanoseconds roundTripTime;
191 pingReference = interest.getName().toUri();
192 m_pingsReceived++;
193 m_pingStatistics.m_receivedPings++;
194 roundTripTime = time::steady_clock::now() - timePoint;
195 if (m_isPrintTimestampSet)
196 std::cout << time::toIsoString(time::system_clock::now()) << " - ";
197 std::cout << "Content From " << m_prefix;
198 std::cout << " - Ping Reference = " <<
199 interest.getName().getSubName(interest.getName().size()-1).toUri().substr(1);
200 std::cout << " \t- Round Trip Time = " <<
201 roundTripTime.count() / 1000000.0 << " ms" << std::endl;
202 m_pingStatistics.addToPingStatistics(roundTripTime);
203 this->finish();
204 }
205
206 void
207 onTimeout(const ndn::Interest& interest)
208 {
209 if (m_isPrintTimestampSet)
210 std::cout << time::toIsoString(time::system_clock::now()) << " - ";
211 std::cout << "Timeout From " << m_prefix;
212 std::cout << " - Ping Reference = " <<
213 interest.getName().getSubName(interest.getName().size()-1).toUri().substr(1);
214 std::cout << std::endl;
215 this->finish();
216 }
217
218 void
219 printPingStatistics()
220 {
221 std::cout << "\n\n=== " << " Ping Statistics For "<< m_prefix <<" ===" << std::endl;
222 std::cout << "Sent=" << m_pingStatistics.m_sentPings;
223 std::cout << ", Received=" << m_pingStatistics.m_receivedPings;
224 double packetLossRate = m_pingStatistics.m_sentPings - m_pingStatistics.m_receivedPings;
225 packetLossRate /= m_pingStatistics.m_sentPings;
226 std::cout << ", Packet Loss=" << packetLossRate * 100.0 << "%";
227 if (m_pingStatistics.m_sentPings != m_pingStatistics.m_receivedPings)
228 m_hasError = true;
229 std::cout << ", Total Time=" << m_pingStatistics.m_averageRoundTripTimeData << " ms\n";
230 if (m_pingStatistics.m_receivedPings > 0) {
231 double averageRoundTripTime =
232 m_pingStatistics.m_averageRoundTripTimeData / m_pingStatistics.m_receivedPings;
233 double standardDeviationRoundTripTime =
234 m_pingStatistics.m_standardDeviationRoundTripTimeData / m_pingStatistics.m_receivedPings;
235 standardDeviationRoundTripTime -= averageRoundTripTime * averageRoundTripTime;
236 standardDeviationRoundTripTime = std::sqrt(standardDeviationRoundTripTime);
237 std::cout << "Round Trip Time (Min/Max/Avg/MDev) = (";
238 std::cout << m_pingStatistics.m_minimumRoundTripTime << "/";
239 std::cout << m_pingStatistics.m_maximumRoundTripTime << "/";
240 std::cout << averageRoundTripTime << "/";
241 std::cout << standardDeviationRoundTripTime << ") ms\n";
242 }
243 std::cout << std::endl;
244 }
245
246 void
247 performPing(boost::asio::deadline_timer* deadlineTimer)
248 {
249 if ((m_totalPings < 0) || (m_pingsSent < m_totalPings))
250 {
251 m_pingsSent++;
252 m_pingStatistics.m_sentPings++;
253
254 //Perform Ping
255 char pingNumberString[20];
256 Name pingPacketName(m_prefix);
257 pingPacketName.append("ping");
258 if(m_clientIdentifier != 0)
259 pingPacketName.append(m_clientIdentifier);
260 std::memset(pingNumberString, 0, 20);
261 if (m_startPingNumber < 0)
262 m_startPingNumber = std::rand();
263 sprintf(pingNumberString, "%lld", static_cast<long long int>(m_startPingNumber));
264 pingPacketName.append(pingNumberString);
265 ndn::Interest interest(pingPacketName);
266 if (m_isAllowCachingSet)
267 interest.setMustBeFresh(false);
268 else
269 interest.setMustBeFresh(true);
270 interest.setInterestLifetime(m_pingTimeoutThreshold);
271 interest.setNonce(m_startPingNumber);
272 m_startPingNumber++;
273 try {
274 m_face.expressInterest(interest,
275 std::bind(&NdnTlvPing::onData, this, _1, _2,
276 time::steady_clock::now()),
277 std::bind(&NdnTlvPing::onTimeout, this, _1));
278 deadlineTimer->expires_at(deadlineTimer->expires_at() +
279 boost::posix_time::millisec(m_pingInterval.count()));
280 deadlineTimer->async_wait(bind(&NdnTlvPing::performPing,
281 this,
282 deadlineTimer));
283 }
284 catch (std::exception& e) {
285 std::cerr << "ERROR: " << e.what() << std::endl;
286 }
287 ++m_outstanding;
288 }
289 else {
290 this->finish();
291 }
292 }
293
294 void
295 finish()
296 {
297 if (--m_outstanding >= 0) {
298 return;
299 }
300 m_face.shutdown();
301 printPingStatistics();
302 m_ioService.stop();
303 }
304
305 void
306 signalHandler()
307 {
308 m_face.shutdown();
309 printPingStatistics();
310 exit(1);
311 }
312
313 void
314 run()
315 {
316 std::cout << "\n=== Pinging " << m_prefix << " ===\n" <<std::endl;
317
318 boost::asio::signal_set signalSet(m_ioService, SIGINT, SIGTERM);
319 signalSet.async_wait(bind(&NdnTlvPing::signalHandler, this));
320
321 boost::asio::deadline_timer deadlineTimer(m_ioService,
322 boost::posix_time::millisec(0));
323
324 deadlineTimer.async_wait(bind(&NdnTlvPing::performPing,
325 this,
326 &deadlineTimer));
327 try {
328 m_face.processEvents();
329 }
330 catch (std::exception& e) {
331 std::cerr << "ERROR: " << e.what() << std::endl;
332 m_hasError = true;
333 m_ioService.stop();
334 }
335 }
336
337private:
338 char* m_programName;
339 bool m_isAllowCachingSet;
340 bool m_isPrintTimestampSet;
341 bool m_hasError;
342 int m_totalPings;
343 int64_t m_startPingNumber;
344 int m_pingsSent;
345 int m_pingsReceived;
346 time::milliseconds m_pingInterval;
347 char* m_clientIdentifier;
348 time::milliseconds m_pingTimeoutThreshold;
349 char* m_prefix;
350 PingStatistics m_pingStatistics;
351 ssize_t m_outstanding;
352
353 boost::asio::io_service m_ioService;
354 Face m_face;
355};
356
357}
358
359
360int
361main(int argc, char* argv[])
362{
363 std::srand(::time(0));
364 int res;
365
366 ndn::NdnTlvPing ndnTlvPing(argv[0]);
367 while ((res = getopt(argc, argv, "htai:c:n:p:")) != -1)
368 {
369 switch (res) {
370 case 'a':
371 ndnTlvPing.setAllowCaching();
372 break;
373 case 'c':
374 ndnTlvPing.setTotalPings(atoi(optarg));
375 break;
376 case 'h':
377 ndnTlvPing.usage();
378 break;
379 case 'i':
380 ndnTlvPing.setPingInterval(atoi(optarg));
381 break;
382 case 'n':
383 try {
384 ndnTlvPing.setStartPingNumber(boost::lexical_cast<int64_t>(optarg));
385 }
386 catch (boost::bad_lexical_cast&) {
387 ndnTlvPing.usage();
388 }
389 break;
390 case 'p':
391 ndnTlvPing.setClientIdentifier(optarg);
392 break;
393 case 't':
394 ndnTlvPing.setPrintTimestamp();
395 break;
396 default:
397 ndnTlvPing.usage();
398 break;
399 }
400 }
401
402 argc -= optind;
403 argv += optind;
404
405 if (argv[0] == 0)
406 ndnTlvPing.usage();
407
408 ndnTlvPing.setPrefix(argv[0]);
409 ndnTlvPing.run();
410
411 std::cout << std::endl;
412
413 if (ndnTlvPing.hasError())
414 return 1;
415 else
416 return 0;
417}