| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */ |
| /** |
| * Copyright (C) 2014 Arizona Board of Regents |
| * |
| * This file is part of ndn-tlv-ping (Ping Application for Named Data Networking). |
| * |
| * ndn-tlv-ping is a free software: you can redistribute it and/or modify it under |
| * the terms of the GNU General Public License as published by the Free Software |
| * Foundation, either version 3 of the License, or (at your option) any later version. |
| * |
| * ndn-tlv-ping is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
| * PARTICULAR PURPOSE. See the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along with |
| * ndn-tlv-ping, e.g., in LICENSE file. If not, see <http://www.gnu.org/licenses/>. |
| * |
| * @author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu> |
| */ |
| |
| #include <ndn-cxx/face.hpp> |
| #include <ndn-cxx/name.hpp> |
| |
| #include <boost/asio.hpp> |
| #include <boost/bind.hpp> |
| #include <boost/date_time/posix_time/posix_time.hpp> |
| #include <boost/noncopyable.hpp> |
| |
| namespace ndn { |
| namespace ping { |
| |
| class NdnPing : boost::noncopyable |
| { |
| public: |
| explicit |
| NdnPing(char* programName) |
| : m_programName(programName) |
| , m_isAllowCachingSet(false) |
| , m_isPrintTimestampSet(false) |
| , m_hasError(false) |
| , m_totalPings(-1) |
| , m_startPingNumber(-1) |
| , m_pingsSent(0) |
| , m_pingsReceived(0) |
| , m_pingInterval(getPingMinimumInterval()) |
| , m_clientIdentifier(0) |
| , m_pingTimeoutThreshold(getPingTimeoutThreshold()) |
| , m_outstanding(0) |
| , m_face(m_ioService) |
| { |
| } |
| |
| class PingStatistics : boost::noncopyable |
| { |
| public: |
| explicit |
| PingStatistics() |
| : m_sentPings(0) |
| , m_receivedPings(0) |
| , m_pingStartTime(time::steady_clock::now()) |
| , m_minimumRoundTripTime(std::numeric_limits<double>::max()) |
| , m_averageRoundTripTimeData(0) |
| , m_standardDeviationRoundTripTimeData(0) |
| { |
| } |
| |
| void |
| addToPingStatistics(time::nanoseconds roundTripNanoseconds) |
| { |
| double roundTripTime = roundTripNanoseconds.count() / 1000000.0; |
| if (roundTripTime < m_minimumRoundTripTime) |
| m_minimumRoundTripTime = roundTripTime; |
| if (roundTripTime > m_maximumRoundTripTime) |
| m_maximumRoundTripTime = roundTripTime; |
| |
| m_averageRoundTripTimeData += roundTripTime; |
| m_standardDeviationRoundTripTimeData += roundTripTime * roundTripTime; |
| } |
| |
| public: |
| int m_sentPings; |
| int m_receivedPings; |
| time::steady_clock::TimePoint m_pingStartTime; |
| double m_minimumRoundTripTime; |
| double m_maximumRoundTripTime; |
| double m_averageRoundTripTimeData; |
| double m_standardDeviationRoundTripTimeData; |
| |
| }; |
| |
| void |
| usage() |
| { |
| std::cout << "\n Usage:\n " << m_programName << " ndn:/name/prefix [options]\n" |
| " Ping a NDN name prefix using Interests with name" |
| " ndn:/name/prefix/ping/number.\n" |
| " The numbers in the Interests are randomly generated unless specified.\n" |
| " [-i interval] - set ping interval in seconds (minimum " |
| << getPingMinimumInterval().count() << " milliseconds)\n" |
| " [-c count] - set total number of pings\n" |
| " [-n number] - set the starting number, the number is incremented by 1" |
| " after each Interest\n" |
| " [-p identifier] - add identifier to the Interest names before the" |
| " numbers to avoid conflict\n" |
| " [-a] - allow routers to return stale Data from cache\n" |
| " [-t] - print timestamp with messages\n" |
| " [-h] - print this message and exit\n\n"; |
| exit(1); |
| } |
| |
| time::milliseconds |
| getPingMinimumInterval() |
| { |
| return time::milliseconds(1000); |
| } |
| |
| time::milliseconds |
| getPingTimeoutThreshold() |
| { |
| return time::milliseconds(4000); |
| } |
| |
| void |
| setTotalPings(int totalPings) |
| { |
| if (totalPings <= 0) |
| usage(); |
| |
| m_totalPings = totalPings; |
| } |
| |
| void |
| setPingInterval(int pingInterval) |
| { |
| if (pingInterval < getPingMinimumInterval().count()) |
| usage(); |
| |
| m_pingInterval = time::milliseconds(pingInterval); |
| } |
| |
| void |
| setStartPingNumber(int64_t startPingNumber) |
| { |
| if (startPingNumber < 0) |
| usage(); |
| |
| m_startPingNumber = startPingNumber; |
| } |
| |
| void |
| setAllowCaching() |
| { |
| m_isAllowCachingSet = true; |
| } |
| |
| void |
| setPrintTimestamp() |
| { |
| m_isPrintTimestampSet = true; |
| } |
| |
| void |
| setClientIdentifier(char* clientIdentifier) |
| { |
| m_clientIdentifier = clientIdentifier; |
| |
| if (strlen(clientIdentifier) == 0) |
| usage(); |
| |
| while (*clientIdentifier != '\0') { |
| if (isalnum(*clientIdentifier) == 0) |
| usage(); |
| clientIdentifier++; |
| } |
| } |
| |
| void |
| setPrefix(char* prefix) |
| { |
| m_prefix = prefix; |
| } |
| |
| bool |
| hasError() const |
| { |
| return m_hasError; |
| } |
| |
| |
| void |
| onData(const ndn::Interest& interest, |
| Data& data, |
| time::steady_clock::TimePoint timePoint) |
| { |
| std::string pingReference = interest.getName().toUri(); |
| m_pingsReceived++; |
| m_pingStatistics.m_receivedPings++; |
| time::nanoseconds roundTripTime = time::steady_clock::now() - timePoint; |
| |
| if (m_isPrintTimestampSet) |
| std::cout << time::toIsoString(time::system_clock::now()) << " - "; |
| std::cout << "Content From " << m_prefix; |
| std::cout << " - Ping Reference = " << |
| interest.getName().getSubName(interest.getName().size()-1).toUri().substr(1); |
| std::cout << " \t- Round Trip Time = " << |
| roundTripTime.count() / 1000000.0 << " ms" << std::endl; |
| |
| m_pingStatistics.addToPingStatistics(roundTripTime); |
| this->finish(); |
| } |
| |
| void |
| onTimeout(const ndn::Interest& interest) |
| { |
| if (m_isPrintTimestampSet) |
| std::cout << time::toIsoString(time::system_clock::now()) << " - "; |
| std::cout << "Timeout From " << m_prefix; |
| std::cout << " - Ping Reference = " << |
| interest.getName().getSubName(interest.getName().size()-1).toUri().substr(1); |
| std::cout << std::endl; |
| |
| this->finish(); |
| } |
| |
| void |
| printPingStatistics() |
| { |
| std::cout << "\n\n=== " << " Ping Statistics For "<< m_prefix <<" ===" << std::endl; |
| std::cout << "Sent=" << m_pingStatistics.m_sentPings; |
| std::cout << ", Received=" << m_pingStatistics.m_receivedPings; |
| double packetLossRate = m_pingStatistics.m_sentPings - m_pingStatistics.m_receivedPings; |
| packetLossRate /= m_pingStatistics.m_sentPings; |
| std::cout << ", Packet Loss=" << packetLossRate * 100.0 << "%"; |
| if (m_pingStatistics.m_sentPings != m_pingStatistics.m_receivedPings) |
| m_hasError = true; |
| std::cout << ", Total Time=" << m_pingStatistics.m_averageRoundTripTimeData << " ms\n"; |
| if (m_pingStatistics.m_receivedPings > 0) { |
| double averageRoundTripTime = |
| m_pingStatistics.m_averageRoundTripTimeData / m_pingStatistics.m_receivedPings; |
| double standardDeviationRoundTripTime = |
| m_pingStatistics.m_standardDeviationRoundTripTimeData / m_pingStatistics.m_receivedPings; |
| standardDeviationRoundTripTime -= averageRoundTripTime * averageRoundTripTime; |
| standardDeviationRoundTripTime = std::sqrt(standardDeviationRoundTripTime); |
| std::cout << "Round Trip Time (Min/Max/Avg/MDev) = ("; |
| std::cout << m_pingStatistics.m_minimumRoundTripTime << "/"; |
| std::cout << m_pingStatistics.m_maximumRoundTripTime << "/"; |
| std::cout << averageRoundTripTime << "/"; |
| std::cout << standardDeviationRoundTripTime << ") ms\n"; |
| } |
| std::cout << std::endl; |
| } |
| |
| void |
| performPing(boost::asio::deadline_timer* deadlineTimer) |
| { |
| if ((m_totalPings < 0) || (m_pingsSent < m_totalPings)) { |
| m_pingsSent++; |
| m_pingStatistics.m_sentPings++; |
| |
| //Perform Ping |
| char pingNumberString[20]; |
| Name pingPacketName(m_prefix); |
| pingPacketName.append("ping"); |
| if (m_clientIdentifier != 0) |
| pingPacketName.append(m_clientIdentifier); |
| std::memset(pingNumberString, 0, 20); |
| if (m_startPingNumber < 0) |
| m_startPingNumber = std::rand(); |
| sprintf(pingNumberString, "%lld", static_cast<long long int>(m_startPingNumber)); |
| pingPacketName.append(pingNumberString); |
| |
| ndn::Interest interest(pingPacketName); |
| |
| if (m_isAllowCachingSet) |
| interest.setMustBeFresh(false); |
| else |
| interest.setMustBeFresh(true); |
| |
| interest.setInterestLifetime(m_pingTimeoutThreshold); |
| interest.setNonce(m_startPingNumber); |
| |
| m_startPingNumber++; |
| |
| try { |
| m_face.expressInterest(interest, |
| std::bind(&NdnPing::onData, this, _1, _2, |
| time::steady_clock::now()), |
| std::bind(&NdnPing::onTimeout, this, _1)); |
| deadlineTimer->expires_at(deadlineTimer->expires_at() + |
| boost::posix_time::millisec(m_pingInterval.count())); |
| deadlineTimer->async_wait(bind(&NdnPing::performPing, |
| this, |
| deadlineTimer)); |
| } |
| catch (std::exception& e) { |
| std::cerr << "ERROR: " << e.what() << std::endl; |
| } |
| ++m_outstanding; |
| } |
| else { |
| this->finish(); |
| } |
| } |
| |
| void |
| finish() |
| { |
| if (--m_outstanding >= 0) { |
| return; |
| } |
| m_face.shutdown(); |
| printPingStatistics(); |
| m_ioService.stop(); |
| } |
| |
| void |
| signalHandler() |
| { |
| m_face.shutdown(); |
| printPingStatistics(); |
| exit(1); |
| } |
| |
| void |
| run() |
| { |
| std::cout << "\n=== Pinging " << m_prefix << " ===\n" <<std::endl; |
| |
| boost::asio::signal_set signalSet(m_ioService, SIGINT, SIGTERM); |
| signalSet.async_wait(bind(&NdnPing::signalHandler, this)); |
| |
| boost::asio::deadline_timer deadlineTimer(m_ioService, |
| boost::posix_time::millisec(0)); |
| |
| deadlineTimer.async_wait(bind(&NdnPing::performPing, |
| this, |
| &deadlineTimer)); |
| try { |
| m_face.processEvents(); |
| } |
| catch (std::exception& e) { |
| std::cerr << "ERROR: " << e.what() << std::endl; |
| m_hasError = true; |
| m_ioService.stop(); |
| } |
| } |
| |
| private: |
| char* m_programName; |
| bool m_isAllowCachingSet; |
| bool m_isPrintTimestampSet; |
| bool m_hasError; |
| int m_totalPings; |
| int64_t m_startPingNumber; |
| int m_pingsSent; |
| int m_pingsReceived; |
| time::milliseconds m_pingInterval; |
| char* m_clientIdentifier; |
| time::milliseconds m_pingTimeoutThreshold; |
| char* m_prefix; |
| PingStatistics m_pingStatistics; |
| ssize_t m_outstanding; |
| |
| boost::asio::io_service m_ioService; |
| Face m_face; |
| }; |
| |
| int |
| main(int argc, char* argv[]) |
| { |
| std::srand(::time(0)); |
| int res; |
| |
| NdnPing program(argv[0]); |
| while ((res = getopt(argc, argv, "htai:c:n:p:")) != -1) |
| { |
| switch (res) { |
| case 'a': |
| program.setAllowCaching(); |
| break; |
| case 'c': |
| program.setTotalPings(atoi(optarg)); |
| break; |
| case 'h': |
| program.usage(); |
| break; |
| case 'i': |
| program.setPingInterval(atoi(optarg)); |
| break; |
| case 'n': |
| try { |
| program.setStartPingNumber(boost::lexical_cast<int64_t>(optarg)); |
| } |
| catch (boost::bad_lexical_cast&) { |
| program.usage(); |
| } |
| break; |
| case 'p': |
| program.setClientIdentifier(optarg); |
| break; |
| case 't': |
| program.setPrintTimestamp(); |
| break; |
| default: |
| program.usage(); |
| break; |
| } |
| } |
| |
| argc -= optind; |
| argv += optind; |
| |
| if (argv[0] == 0) |
| program.usage(); |
| |
| program.setPrefix(argv[0]); |
| program.run(); |
| |
| std::cout << std::endl; |
| |
| if (program.hasError()) |
| return 1; |
| else |
| return 0; |
| } |
| |
| } // namespace ping |
| } // namespace ndn |
| |
| int |
| main(int argc, char** argv) |
| { |
| return ndn::ping::main(argc, argv); |
| } |