From c8f12671273f75285fbdade206b41bc3c115c008 Mon Sep 17 00:00:00 2001 From: Tom Henderson Date: Tue, 10 Mar 2020 10:56:25 -0700 Subject: [PATCH] applications: Align E2eStatsHeader with other application headers (!211) * E2eStatsHeader renamed to SeqTsSizeHeader, for module naming consistency * BulkSendApplication::EnableE2EStats attribute renamed to EnableSeqTsSizeHeader * BulkSendApplication::TxE2EStat trace source renamed to TxWithSeqTsSize * OnOffApplication::EnableE2EStats attribute renamed to EnableSeqTsSizeHeader * OnOffApplication::TxE2EStat trace source renamed to TxWithSeqTsSize * PacketSink::EnableE2EStats attribute renamed to EnableSeqTsSizeHeader * PacketSink::RxE2EStat trace source renamed to RxWithSeqTsSize * Cache outgoing packet based on Socket::Send () failure (due to full buffer) * Add test suite for BulkSendApplication, checking sequence number operation * Various other review comments addressed, and doxygen alignments --- CHANGES.html | 1 + .../model/bulk-send-application.cc | 47 ++-- .../model/bulk-send-application.h | 22 +- src/applications/model/onoff-application.cc | 95 ++++--- src/applications/model/onoff-application.h | 22 +- src/applications/model/packet-sink.cc | 25 +- src/applications/model/packet-sink.h | 26 +- src/applications/model/seq-ts-echo-header.cc | 7 +- src/applications/model/seq-ts-echo-header.h | 3 +- src/applications/model/seq-ts-header.h | 17 +- ...-stats-header.cc => seq-ts-size-header.cc} | 39 ++- ...2e-stats-header.h => seq-ts-size-header.h} | 51 ++-- .../test/bulk-send-application-test-suite.cc | 233 ++++++++++++++++++ src/applications/wscript | 1 + 14 files changed, 435 insertions(+), 154 deletions(-) rename src/applications/model/{e2e-stats-header.cc => seq-ts-size-header.cc} (66%) rename src/applications/model/{e2e-stats-header.h => seq-ts-size-header.h} (62%) create mode 100644 src/applications/test/bulk-send-application-test-suite.cc diff --git a/CHANGES.html b/CHANGES.html index be0f827b6..c9078c015 100644 --- a/CHANGES.html +++ b/CHANGES.html @@ -74,6 +74,7 @@ allows to choose between Block Ack policy and Implicit Block Ack Request policy allows to request an acknowledgment after a configurable number of MPDUs have been transmitted.
  • The MaxSize attribute is removed from the QueueBase base class and moved to subclasses. A new MaxSize attribute is therefore added to the DropTailQueue class, while the MaxQueueSize attribute of the WifiMacQueue class is renamed as MaxSize for API consistency.
  • +
  • TCP-based applications (OnOffApplication, BulkSendApplication, and PacketSink) support a new header to convey sequence number, timestamp, and size data. Use is controlled by the "EnableSeqTsSizeHeader" attribute.
  • Added a new trace source PhyRxPayloadBegin in WifiPhy for tracing begin of PSDU reception.
  • A new sequence and timestamp header variant for applications has been added. The SeqTsEchoHeader contains an additional timestamp field for use in echoing a timestamp back to a sender.
  • diff --git a/src/applications/model/bulk-send-application.cc b/src/applications/model/bulk-send-application.cc index 44a463427..f7dbc2e4b 100644 --- a/src/applications/model/bulk-send-application.cc +++ b/src/applications/model/bulk-send-application.cc @@ -70,17 +70,17 @@ BulkSendApplication::GetTypeId (void) TypeIdValue (TcpSocketFactory::GetTypeId ()), MakeTypeIdAccessor (&BulkSendApplication::m_tid), MakeTypeIdChecker ()) - .AddAttribute ("EnableE2EStats", - "Enable E2E statistics (sequences, timestamps)", + .AddAttribute ("EnableSeqTsSizeHeader", + "Add SeqTsSizeHeader to each packet", BooleanValue (false), - MakeBooleanAccessor (&BulkSendApplication::m_enableE2EStats), + MakeBooleanAccessor (&BulkSendApplication::m_enableSeqTsSizeHeader), MakeBooleanChecker ()) - .AddTraceSource ("Tx", "A new packet is created and is sent", + .AddTraceSource ("Tx", "A new packet is sent", MakeTraceSourceAccessor (&BulkSendApplication::m_txTrace), "ns3::Packet::TracedCallback") - .AddTraceSource ("TxE2EStat", "Statistic sent with the packet", - MakeTraceSourceAccessor (&BulkSendApplication::m_txTraceWithStats), - "ns3::PacketSink::E2EStatCallback") + .AddTraceSource ("TxWithSeqTsSize", "A new packet is created with SeqTsSizeHeader", + MakeTraceSourceAccessor (&BulkSendApplication::m_txTraceWithSeqTsSize), + "ns3::PacketSink::SeqTsSizeCallback") ; return tid; } @@ -89,7 +89,8 @@ BulkSendApplication::GetTypeId (void) BulkSendApplication::BulkSendApplication () : m_socket (0), m_connected (false), - m_totBytes (0) + m_totBytes (0), + m_unsentPacket (0) { NS_LOG_FUNCTION (this); } @@ -119,6 +120,7 @@ BulkSendApplication::DoDispose (void) NS_LOG_FUNCTION (this); m_socket = 0; + m_unsentPacket = 0; // chain up Application::DoDispose (); } @@ -221,16 +223,20 @@ void BulkSendApplication::SendData (const Address &from, const Address &to) NS_LOG_LOGIC ("sending packet at " << Simulator::Now ()); Ptr packet; - if (m_enableE2EStats) + if (m_unsentPacket) { - // Should we add a trace for the sent tx and timestamp? - E2eStatsHeader header; + packet = m_unsentPacket; + } + else if (m_enableSeqTsSizeHeader) + { + SeqTsSizeHeader header; header.SetSeq (m_seq++); header.SetSize (toSend); NS_ABORT_IF (toSend < header.GetSerializedSize ()); packet = Create (toSend - header.GetSerializedSize ()); + // Trace before adding header, for consistency with PacketSink + m_txTraceWithSeqTsSize (packet, from, to, header); packet->AddHeader (header); - m_txTraceWithStats (packet, from, to, header); } else { @@ -238,18 +244,25 @@ void BulkSendApplication::SendData (const Address &from, const Address &to) } int actual = m_socket->Send (packet); - if (actual > 0) + if ((unsigned) actual == toSend) { m_totBytes += actual; m_txTrace (packet); + m_unsentPacket = 0; } - // We exit this loop when actual < toSend as the send side - // buffer is full. The "DataSent" callback will pop when - // some buffer space has freed up. - if ((unsigned)actual != toSend) + else if (actual == -1) { + // We exit this loop when actual < toSend as the send side + // buffer is full. The "DataSent" callback will pop when + // some buffer space has freed up. + NS_LOG_DEBUG ("Unable to send packet; caching for later attempt"); + m_unsentPacket = packet; break; } + else + { + NS_FATAL_ERROR ("Unexpected return value from m_socket->Send ()"); + } } // Check if time to close (all sent) if (m_totBytes == m_maxBytes && m_connected) diff --git a/src/applications/model/bulk-send-application.h b/src/applications/model/bulk-send-application.h index 544ef8414..4d8aa5c96 100644 --- a/src/applications/model/bulk-send-application.h +++ b/src/applications/model/bulk-send-application.h @@ -26,7 +26,7 @@ #include "ns3/event-id.h" #include "ns3/ptr.h" #include "ns3/traced-callback.h" -#include "ns3/e2e-stats-header.h" +#include "ns3/seq-ts-size-header.h" namespace ns3 { @@ -65,12 +65,12 @@ class Socket; * For example, TCP sockets can be used, but * UDP sockets can not be used. * - * If the attribute "EnableE2EStats" is enabled, the application will use - * some byte of the payload to store an header with a sequence number, - * a timestamp, and the size of the packet sent. To get these statistics, - * please use PacketSink (and enable its "EnableE2EStats" attribute) or extract - * the header yourself in your application (you can see how PacketSink is working - * with such headers). + * If the attribute "EnableSeqTsSizeHeader" is enabled, the application will + * use some bytes of the payload to store an header with a sequence number, + * a timestamp, and the size of the packet sent. Support for extracting + * statistics from this header have been added to \c ns3::PacketSink + * (enable its "EnableSeqTsSizeHeader" attribute), or users may extract + * the header via trace sources. */ class BulkSendApplication : public Application { @@ -129,14 +129,14 @@ private: uint64_t m_totBytes; //!< Total bytes sent so far TypeId m_tid; //!< The type of protocol to use. uint32_t m_seq {0}; //!< Sequence - bool m_enableE2EStats {false}; //!< Enable or disable the e2e statistic generation - + Ptr m_unsentPacket; //!< Variable to cache unsent packet + bool m_enableSeqTsSizeHeader {false}; //!< Enable or disable the SeqTsSizeHeader /// Traced Callback: sent packets TracedCallback > m_txTrace; - /// Callback for tracing the packet Tx events, includes source, destination, and the packet sent - TracedCallback, const Address &, const Address &, const E2eStatsHeader &> m_txTraceWithStats; + /// Callback for tracing the packet Tx events, includes source, destination, the packet sent, and header + TracedCallback, const Address &, const Address &, const SeqTsSizeHeader &> m_txTraceWithSeqTsSize; private: /** diff --git a/src/applications/model/onoff-application.cc b/src/applications/model/onoff-application.cc index 166612e61..d4d546308 100644 --- a/src/applications/model/onoff-application.cc +++ b/src/applications/model/onoff-application.cc @@ -94,10 +94,10 @@ OnOffApplication::GetTypeId (void) MakeTypeIdAccessor (&OnOffApplication::m_tid), // This should check for SocketFactory as a parent MakeTypeIdChecker ()) - .AddAttribute ("EnableE2EStats", - "Enable E2E statistics (sequences, timestamps)", + .AddAttribute ("EnableSeqTsSizeHeader", + "Enable use of SeqTsSizeHeader for sequence number and timestamp", BooleanValue (false), - MakeBooleanAccessor (&OnOffApplication::m_enableE2EStats), + MakeBooleanAccessor (&OnOffApplication::m_enableSeqTsSizeHeader), MakeBooleanChecker ()) .AddTraceSource ("Tx", "A new packet is created and is sent", MakeTraceSourceAccessor (&OnOffApplication::m_txTrace), @@ -105,9 +105,9 @@ OnOffApplication::GetTypeId (void) .AddTraceSource ("TxWithAddresses", "A new packet is created and is sent", MakeTraceSourceAccessor (&OnOffApplication::m_txTraceWithAddresses), "ns3::Packet::TwoAddressTracedCallback") - .AddTraceSource ("TxE2EStat", "Statistic sent with the packet", - MakeTraceSourceAccessor (&OnOffApplication::m_txTraceWithStats), - "ns3::PacketSink::E2EStatCallback") + .AddTraceSource ("TxWithSeqTsSize", "A new packet is created with SeqTsSizeHeader", + MakeTraceSourceAccessor (&OnOffApplication::m_txTraceWithSeqTsSize), + "ns3::PacketSink::SeqTsSizeCallback") ; return tid; } @@ -118,7 +118,8 @@ OnOffApplication::OnOffApplication () m_connected (false), m_residualBits (0), m_lastStartTime (Seconds (0)), - m_totBytes (0) + m_totBytes (0), + m_unsentPacket (0) { NS_LOG_FUNCTION (this); } @@ -158,6 +159,7 @@ OnOffApplication::DoDispose (void) CancelEvents (); m_socket = 0; + m_unsentPacket = 0; // chain up Application::DoDispose (); } @@ -245,6 +247,13 @@ void OnOffApplication::CancelEvents () m_cbrRateFailSafe = m_cbrRate; Simulator::Cancel (m_sendEvent); Simulator::Cancel (m_startStopEvent); + // Canceling events may cause discontinuity in sequence number if the + // SeqTsSizeHeader is header, and m_unsentPacket is true + if (m_unsentPacket) + { + NS_LOG_DEBUG ("Discarding cached packet upon CancelEvents ()"); + } + m_unsentPacket = 0; } // Event handlers @@ -271,17 +280,19 @@ void OnOffApplication::ScheduleNextTx () if (m_maxBytes == 0 || m_totBytes < m_maxBytes) { + NS_ABORT_MSG_IF (m_residualBits > m_pktSize * 8, "Calculation to compute next send time will overflow"); uint32_t bits = m_pktSize * 8 - m_residualBits; NS_LOG_LOGIC ("bits = " << bits); Time nextTime (Seconds (bits / static_cast(m_cbrRate.GetBitRate ()))); // Time till next packet - NS_LOG_LOGIC ("nextTime = " << nextTime); + NS_LOG_LOGIC ("nextTime = " << nextTime.As (Time::S)); m_sendEvent = Simulator::Schedule (nextTime, &OnOffApplication::SendPacket, this); } else { // All done, cancel any pending events StopApplication (); + exit(1); } } @@ -290,7 +301,7 @@ void OnOffApplication::ScheduleStartEvent () NS_LOG_FUNCTION (this); Time offInterval = Seconds (m_offTime->GetValue ()); - NS_LOG_LOGIC ("start at " << offInterval); + NS_LOG_LOGIC ("start at " << offInterval.As (Time::S)); m_startStopEvent = Simulator::Schedule (offInterval, &OnOffApplication::StartSending, this); } @@ -299,7 +310,7 @@ void OnOffApplication::ScheduleStopEvent () NS_LOG_FUNCTION (this); Time onInterval = Seconds (m_onTime->GetValue ()); - NS_LOG_LOGIC ("stop at " << onInterval); + NS_LOG_LOGIC ("stop at " << onInterval.As (Time::S)); m_startStopEvent = Simulator::Schedule (onInterval, &OnOffApplication::StopSending, this); } @@ -311,51 +322,65 @@ void OnOffApplication::SendPacket () NS_ASSERT (m_sendEvent.IsExpired ()); Ptr packet; - if (m_enableE2EStats) + if (m_unsentPacket) + { + packet = m_unsentPacket; + } + else if (m_enableSeqTsSizeHeader) { Address from, to; m_socket->GetSockName (from); m_socket->GetPeerName (to); - E2eStatsHeader header; + SeqTsSizeHeader header; header.SetSeq (m_seq++); header.SetSize (m_pktSize); NS_ABORT_IF (m_pktSize < header.GetSerializedSize ()); packet = Create (m_pktSize - header.GetSerializedSize ()); + // Trace before adding header, for consistency with PacketSink + m_txTraceWithSeqTsSize (packet, from, to, header); packet->AddHeader (header); - m_txTraceWithStats (packet, from, to, header); } else { packet = Create (m_pktSize); } - m_txTrace (packet); - m_socket->Send (packet); - m_totBytes += m_pktSize; - Address localAddress; - m_socket->GetSockName (localAddress); - if (InetSocketAddress::IsMatchingType (m_peer)) + int actual = m_socket->Send (packet); + if ((unsigned) actual == m_pktSize) { - NS_LOG_INFO ("At time " << Simulator::Now ().GetSeconds () - << "s on-off application sent " - << packet->GetSize () << " bytes to " - << InetSocketAddress::ConvertFrom(m_peer).GetIpv4 () - << " port " << InetSocketAddress::ConvertFrom (m_peer).GetPort () - << " total Tx " << m_totBytes << " bytes"); - m_txTraceWithAddresses (packet, localAddress, InetSocketAddress::ConvertFrom (m_peer)); + m_txTrace (packet); + m_totBytes += m_pktSize; + m_unsentPacket = 0; + Address localAddress; + m_socket->GetSockName (localAddress); + if (InetSocketAddress::IsMatchingType (m_peer)) + { + NS_LOG_INFO ("At time " << Simulator::Now ().GetSeconds () + << "s on-off application sent " + << packet->GetSize () << " bytes to " + << InetSocketAddress::ConvertFrom(m_peer).GetIpv4 () + << " port " << InetSocketAddress::ConvertFrom (m_peer).GetPort () + << " total Tx " << m_totBytes << " bytes"); + m_txTraceWithAddresses (packet, localAddress, InetSocketAddress::ConvertFrom (m_peer)); + } + else if (Inet6SocketAddress::IsMatchingType (m_peer)) + { + NS_LOG_INFO ("At time " << Simulator::Now ().GetSeconds () + << "s on-off application sent " + << packet->GetSize () << " bytes to " + << Inet6SocketAddress::ConvertFrom(m_peer).GetIpv6 () + << " port " << Inet6SocketAddress::ConvertFrom (m_peer).GetPort () + << " total Tx " << m_totBytes << " bytes"); + m_txTraceWithAddresses (packet, localAddress, Inet6SocketAddress::ConvertFrom(m_peer)); + } } - else if (Inet6SocketAddress::IsMatchingType (m_peer)) + else { - NS_LOG_INFO ("At time " << Simulator::Now ().GetSeconds () - << "s on-off application sent " - << packet->GetSize () << " bytes to " - << Inet6SocketAddress::ConvertFrom(m_peer).GetIpv6 () - << " port " << Inet6SocketAddress::ConvertFrom (m_peer).GetPort () - << " total Tx " << m_totBytes << " bytes"); - m_txTraceWithAddresses (packet, localAddress, Inet6SocketAddress::ConvertFrom(m_peer)); + NS_LOG_DEBUG ("Unable to send packet; actual " << actual << " size " << m_pktSize << "; caching for later attempt"); + m_unsentPacket = packet; } - m_lastStartTime = Simulator::Now (); m_residualBits = 0; + m_lastStartTime = Simulator::Now (); ScheduleNextTx (); } diff --git a/src/applications/model/onoff-application.h b/src/applications/model/onoff-application.h index 90711c1a0..3aba54514 100644 --- a/src/applications/model/onoff-application.h +++ b/src/applications/model/onoff-application.h @@ -31,7 +31,7 @@ #include "ns3/ptr.h" #include "ns3/data-rate.h" #include "ns3/traced-callback.h" -#include "ns3/e2e-stats-header.h" +#include "ns3/seq-ts-size-header.h" namespace ns3 { @@ -84,12 +84,13 @@ class Socket; * If the underlying socket type supports broadcast, this application * will automatically enable the SetAllowBroadcast(true) socket option. * -* If the attribute "EnableE2EStats" is enabled, the application will use -* some byte of the payload to store an header with a sequence number, -* a timestamp, and the size of the packet sent. To get these statistics, -* please use PacketSink (and enable its "EnableE2EStats" attribute) or extract -* the header yourself in your application (you can see how PacketSink is working -* with such headers). + * If the attribute "EnableSeqTsSizeHeader" is enabled, the application will + * use some bytes of the payload to store an header with a sequence number, + * a timestamp, and the size of the packet sent. Support for extracting + * statistics from this header have been added to \c ns3::PacketSink + * (enable its "EnableSeqTsSizeHeader" attribute), or users may extract + * the header via trace sources. Note that the continuity of the sequence + * number may be disrupted across On/Off cycles. */ class OnOffApplication : public Application { @@ -173,7 +174,8 @@ private: EventId m_sendEvent; //!< Event id of pending "send packet" event TypeId m_tid; //!< Type of the socket used uint32_t m_seq {0}; //!< Sequence - bool m_enableE2EStats {false}; //!< Enable or disable the e2e statistic generation + Ptr m_unsentPacket; //!< Unsent packet cached for future attempt + bool m_enableSeqTsSizeHeader {false}; //!< Enable or disable the use of SeqTsSizeHeader /// Traced Callback: transmitted packets. @@ -182,8 +184,8 @@ private: /// Callbacks for tracing the packet Tx events, includes source and destination addresses TracedCallback, const Address &, const Address &> m_txTraceWithAddresses; - /// Callback for tracing the packet Tx events, includes source, destination, and the packet sent - TracedCallback, const Address &, const Address &, const E2eStatsHeader &> m_txTraceWithStats; + /// Callback for tracing the packet Tx events, includes source, destination, the packet sent, and header + TracedCallback, const Address &, const Address &, const SeqTsSizeHeader &> m_txTraceWithSeqTsSize; private: /** diff --git a/src/applications/model/packet-sink.cc b/src/applications/model/packet-sink.cc index 16c539834..a1c6a7cb4 100644 --- a/src/applications/model/packet-sink.cc +++ b/src/applications/model/packet-sink.cc @@ -56,10 +56,10 @@ PacketSink::GetTypeId (void) TypeIdValue (UdpSocketFactory::GetTypeId ()), MakeTypeIdAccessor (&PacketSink::m_tid), MakeTypeIdChecker ()) - .AddAttribute ("EnableE2EStats", - "Enable E2E statistics (sequences, timestamps)", + .AddAttribute ("EnableSeqTsSizeHeader", + "Enable optional header tracing of SeqTsSizeHeader", BooleanValue (false), - MakeBooleanAccessor (&PacketSink::m_enableE2EStats), + MakeBooleanAccessor (&PacketSink::m_enableSeqTsSizeHeader), MakeBooleanChecker ()) .AddTraceSource ("Rx", "A packet has been received", @@ -68,10 +68,10 @@ PacketSink::GetTypeId (void) .AddTraceSource ("RxWithAddresses", "A packet has been received", MakeTraceSourceAccessor (&PacketSink::m_rxTraceWithAddresses), "ns3::Packet::TwoAddressTracedCallback") - .AddTraceSource ("RxE2EStat", - "A sequence number and a timestamp have been received", - MakeTraceSourceAccessor (&PacketSink::m_rxTraceWithAddressesAndSeqTs), - "ns3::PacketSink::E2EStatCallback") + .AddTraceSource ("RxWithSeqTsSize", + "A packet with SeqTsSize header has been received", + MakeTraceSourceAccessor (&PacketSink::m_rxTraceWithSeqTsSize), + "ns3::PacketSink::SeqTsSizeCallback") ; return tid; } @@ -208,7 +208,7 @@ void PacketSink::HandleRead (Ptr socket) m_rxTrace (packet, from); m_rxTraceWithAddresses (packet, from, localAddress); - if (m_enableE2EStats) + if (m_enableSeqTsSizeHeader) { PacketReceived (packet, from, localAddress); } @@ -219,7 +219,7 @@ void PacketSink::PacketReceived (const Ptr &p, const Address &from, const Address &localAddress) { - E2eStatsHeader header; + SeqTsSizeHeader header; Ptr buffer; auto itBuffer = m_buffer.find (from); @@ -236,14 +236,15 @@ PacketSink::PacketReceived (const Ptr &p, const Address &from, while (buffer->GetSize () >= header.GetSize ()) { + NS_LOG_DEBUG ("Removing packet of size " << header.GetSize () << " from buffer of size " << buffer->GetSize ()); Ptr complete = buffer->CreateFragment (0, static_cast (header.GetSize ())); buffer->RemoveAtStart (static_cast (header.GetSize ())); complete->RemoveHeader (header); - m_rxTraceWithAddressesAndSeqTs (complete, from, localAddress, header); + m_rxTraceWithSeqTsSize (complete, from, localAddress, header); - if (buffer->GetSize () > 0) + if (buffer->GetSize () > header.GetSerializedSize ()) { buffer->PeekHeader (header); } @@ -254,7 +255,6 @@ PacketSink::PacketReceived (const Ptr &p, const Address &from, } } - void PacketSink::HandlePeerClose (Ptr socket) { NS_LOG_FUNCTION (this << socket); @@ -264,7 +264,6 @@ void PacketSink::HandlePeerError (Ptr socket) { NS_LOG_FUNCTION (this << socket); } - void PacketSink::HandleAccept (Ptr s, const Address& from) { diff --git a/src/applications/model/packet-sink.h b/src/applications/model/packet-sink.h index b583bcdaa..7f938a0f2 100644 --- a/src/applications/model/packet-sink.h +++ b/src/applications/model/packet-sink.h @@ -27,7 +27,7 @@ #include "ns3/traced-callback.h" #include "ns3/address.h" #include "ns3/inet-socket-address.h" -#include "ns3/e2e-stats-header.h" +#include "ns3/seq-ts-size-header.h" #include namespace ns3 { @@ -96,16 +96,15 @@ public: std::list > GetAcceptedSockets (void) const; /** - * TracedCallback signature for an E2E stat callback + * TracedCallback signature for a reception with addresses and SeqTsSizeHeader * - * \param p The packet received + * \param p The packet received (without the SeqTsSize header) * \param from From address * \param to Local address - * \param seq The sequence received - * \param time The delay the sequence has needed from the sender to the receiver + * \param header The SeqTsSize header */ - typedef void (* E2EStatCallback)(Ptr p, const Address &from, const Address & to, - const E2eStatsHeader &header); + typedef void (* SeqTsSizeCallback)(Ptr p, const Address &from, const Address & to, + const SeqTsSizeHeader &header); protected: virtual void DoDispose (void); @@ -137,14 +136,13 @@ private: void HandlePeerError (Ptr socket); /** - * \brief Packet received: calculation of the e2e statistics + * \brief Packet received: assemble byte stream to extract SeqTsSizeHeader * \param p received packet * \param from from address * \param localAddress local address * - * The method calculates e2e statistics about the received packet. If - * the transport protocol is stream-based, then reconstruct first the - * original packet, and then extract the statistic header from it. + * The method assembles a received byte stream and extracts SeqTsSizeHeader + * instances from the stream to export in a trace source. */ void PacketReceived (const Ptr &p, const Address &from, const Address &localAddress); @@ -182,14 +180,14 @@ private: uint64_t m_totalRx; //!< Total bytes received TypeId m_tid; //!< Protocol TypeId - bool m_enableE2EStats {false}; //!< Enable or disable the E2E statistics generation + bool m_enableSeqTsSizeHeader {false}; //!< Enable or disable the export of SeqTsSize header /// Traced Callback: received packets, source address. TracedCallback, const Address &> m_rxTrace; /// Callback for tracing the packet Rx events, includes source and destination addresses TracedCallback, const Address &, const Address &> m_rxTraceWithAddresses; - /// Callback for tracing the packet Rx events, includes source, destination addresses, sequence and timestamp - TracedCallback, const Address &, const Address &, const E2eStatsHeader&> m_rxTraceWithAddressesAndSeqTs; + /// Callbacks for tracing the packet Rx events, includes source, destination addresses, and headers + TracedCallback, const Address &, const Address &, const SeqTsSizeHeader&> m_rxTraceWithSeqTsSize; }; } // namespace ns3 diff --git a/src/applications/model/seq-ts-echo-header.cc b/src/applications/model/seq-ts-echo-header.cc index d211b9a8a..0a8e3188a 100644 --- a/src/applications/model/seq-ts-echo-header.cc +++ b/src/applications/model/seq-ts-echo-header.cc @@ -1,6 +1,7 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2016 Universita' di Firenze + * Copyright (c) 2009 INRIA + * Copyright (c) 2016 Universita' di Firenze (added echo fields) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -55,7 +56,7 @@ SeqTsEchoHeader::GetSeq (void) const void SeqTsEchoHeader::SetTsValue (Time ts) { - NS_LOG_FUNCTION (this); + NS_LOG_FUNCTION (this << ts); m_tsValue = ts; } @@ -69,7 +70,7 @@ SeqTsEchoHeader::GetTsValue (void) const void SeqTsEchoHeader::SetTsEchoReply (Time ts) { - NS_LOG_FUNCTION (this); + NS_LOG_FUNCTION (this << ts); m_tsEchoReply = ts; } diff --git a/src/applications/model/seq-ts-echo-header.h b/src/applications/model/seq-ts-echo-header.h index 662d60885..203c3281e 100644 --- a/src/applications/model/seq-ts-echo-header.h +++ b/src/applications/model/seq-ts-echo-header.h @@ -1,6 +1,7 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2016 Universita' di Firenze + * Copyright (c) 2009 INRIA + * Copyright (c) 2016 Universita' di Firenze (added echo fields) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/src/applications/model/seq-ts-header.h b/src/applications/model/seq-ts-header.h index add229d37..938f07bc8 100644 --- a/src/applications/model/seq-ts-header.h +++ b/src/applications/model/seq-ts-header.h @@ -26,13 +26,20 @@ namespace ns3 { /** - * \ingroup udpclientserver + * \ingroup applications * - * \brief Packet header for UDP client/server application. + * \brief Packet header to carry sequence number and timestamp * - * The header is made of a 32bits sequence number followed by - * a 64bits time stamp. If you need to use this header with a transport protocol - * such as TCP, please consider to use SizeHeader. + * The header is used as a payload in applications (typically UDP) to convey + * a 32 bit sequence number followed by a 64 bit timestamp (12 bytes total). + * + * The timestamp is not set explicitly but automatically set to the + * simulation time upon creation. + * + * If you need space for an application data unit size field (e.g. for + * stream-based protocols like TCP), use ns3::SeqTsSizeHeader. + * + * \sa ns3::SeqTsSizeHeader */ class SeqTsHeader : public Header { diff --git a/src/applications/model/e2e-stats-header.cc b/src/applications/model/seq-ts-size-header.cc similarity index 66% rename from src/applications/model/e2e-stats-header.cc rename to src/applications/model/seq-ts-size-header.cc index 753b84d54..a25955603 100644 --- a/src/applications/model/e2e-stats-header.cc +++ b/src/applications/model/seq-ts-size-header.cc @@ -1,6 +1,8 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* + * Copyright (c) 2009 INRIA * Copyright (c) 2018 Natale Patriciello + * (added timestamp and size fields) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -18,39 +20,51 @@ */ #include "ns3/log.h" -#include "e2e-stats-header.h" +#include "seq-ts-size-header.h" namespace ns3 { -NS_LOG_COMPONENT_DEFINE ("SizeHeader"); +NS_LOG_COMPONENT_DEFINE ("SeqTsSizeHeader"); -NS_OBJECT_ENSURE_REGISTERED (E2eStatsHeader); +NS_OBJECT_ENSURE_REGISTERED (SeqTsSizeHeader); -E2eStatsHeader::E2eStatsHeader () +SeqTsSizeHeader::SeqTsSizeHeader () : SeqTsHeader () { NS_LOG_FUNCTION (this); } TypeId -E2eStatsHeader::GetTypeId (void) +SeqTsSizeHeader::GetTypeId (void) { - static TypeId tid = TypeId ("ns3::SizeHeader") + static TypeId tid = TypeId ("ns3::SeqTsSizeHeader") .SetParent () .SetGroupName ("Applications") - .AddConstructor () + .AddConstructor () ; return tid; } TypeId -E2eStatsHeader::GetInstanceTypeId (void) const +SeqTsSizeHeader::GetInstanceTypeId (void) const { return GetTypeId (); } void -E2eStatsHeader::Print (std::ostream &os) const +SeqTsSizeHeader::SetSize (uint64_t size) +{ + m_size = size; +} + +uint64_t +SeqTsSizeHeader::GetSize (void) const +{ + return m_size; +} + +void +SeqTsSizeHeader::Print (std::ostream &os) const { NS_LOG_FUNCTION (this << &os); os << "(size=" << m_size << ") AND "; @@ -58,14 +72,13 @@ E2eStatsHeader::Print (std::ostream &os) const } uint32_t -E2eStatsHeader::GetSerializedSize (void) const +SeqTsSizeHeader::GetSerializedSize (void) const { - NS_LOG_FUNCTION (this); return SeqTsHeader::GetSerializedSize () + 8; } void -E2eStatsHeader::Serialize (Buffer::Iterator start) const +SeqTsSizeHeader::Serialize (Buffer::Iterator start) const { NS_LOG_FUNCTION (this << &start); Buffer::Iterator i = start; @@ -74,7 +87,7 @@ E2eStatsHeader::Serialize (Buffer::Iterator start) const } uint32_t -E2eStatsHeader::Deserialize (Buffer::Iterator start) +SeqTsSizeHeader::Deserialize (Buffer::Iterator start) { NS_LOG_FUNCTION (this << &start); Buffer::Iterator i = start; diff --git a/src/applications/model/e2e-stats-header.h b/src/applications/model/seq-ts-size-header.h similarity index 62% rename from src/applications/model/e2e-stats-header.h rename to src/applications/model/seq-ts-size-header.h index f3bdfeb6d..4b5fa49e5 100644 --- a/src/applications/model/e2e-stats-header.h +++ b/src/applications/model/seq-ts-size-header.h @@ -1,6 +1,8 @@ /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ /* + * Copyright (c) 2009 INRIA * Copyright (c) 2018 Natale Patriciello + * (added timestamp and size fields) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,7 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#pragma once +#ifndef SEQ_TS_SIZE_HEADER_H +#define SEQ_TS_SIZE_HEADER_H #include @@ -25,17 +28,13 @@ namespace ns3 { * \ingroup applications * \brief Header with a sequence, a timestamp, and a "size" attribute * - * Sometimes, you would need an header that not only track an application - * sequence number, or an application timestamp, but also track - * how big are these application packets. + * This header adds a size attribute to the sequence number and timestamp + * of class \c SeqTsHeader. The size attribute can be used to track + * application data units for stream-based sockets such as TCP. * - * This header extends SeqTsHeader, adding space to store the information - * about the size of these packets. - * - * When you will use a protocol like TCP, you will find the answer to the question - * "isn't SeqTsHeader enough?". + * \sa ns3::SeqTsHeader */ -class E2eStatsHeader : public SeqTsHeader +class SeqTsSizeHeader : public SeqTsHeader { public: /** @@ -45,35 +44,21 @@ public: static TypeId GetTypeId (void); /** - * @brief SizeHeader constructor + * \brief constructor */ - E2eStatsHeader (); + SeqTsSizeHeader (); /** - * \brief ~SizeHeader - * - * Nothing much to add here + * \brief Set the size information that the header will carry + * \param size the size */ - virtual ~E2eStatsHeader () override - { - } + void SetSize (uint64_t size); /** - * @brief Set the size information that the header will carry - * @param size the size + * \brief Get the size information that the header is carrying + * \return the size */ - void SetSize (uint64_t size) - { - m_size = size; - } - /** - * @brief Get the size information that the header is carrying - * @return the size - */ - uint64_t GetSize (void) const - { - return m_size; - } + uint64_t GetSize (void) const; // Inherited virtual TypeId GetInstanceTypeId (void) const override; @@ -87,3 +72,5 @@ private: }; } // namespace ns3 + +#endif /* SEQ_TS_SIZE_HEADER */ diff --git a/src/applications/test/bulk-send-application-test-suite.cc b/src/applications/test/bulk-send-application-test-suite.cc new file mode 100644 index 000000000..74a763bd7 --- /dev/null +++ b/src/applications/test/bulk-send-application-test-suite.cc @@ -0,0 +1,233 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2020 Tom Henderson (tomh@tomh.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "ns3/test.h" +#include "ns3/nstime.h" +#include "ns3/node.h" +#include "ns3/traced-callback.h" +#include "ns3/node-container.h" +#include "ns3/application-container.h" +#include "ns3/point-to-point-helper.h" +#include "ns3/string.h" +#include "ns3/uinteger.h" +#include "ns3/boolean.h" +#include "ns3/internet-stack-helper.h" +#include "ns3/ipv4-address.h" +#include "ns3/inet-socket-address.h" +#include "ns3/ipv4-address-helper.h" +#include "ns3/ipv4-interface-container.h" +#include "ns3/bulk-send-application.h" +#include "ns3/bulk-send-helper.h" +#include "ns3/packet-sink.h" +#include "ns3/packet-sink-helper.h" + +using namespace ns3; + +class BulkSendBasicTestCase : public TestCase +{ +public: + BulkSendBasicTestCase (); + virtual ~BulkSendBasicTestCase (); + +private: + virtual void DoRun (void); + void SendTx (Ptr p); + void ReceiveRx (Ptr p, const Address& addr); + uint64_t m_sent {0}; + uint64_t m_received {0}; +}; + +BulkSendBasicTestCase::BulkSendBasicTestCase () + : TestCase ("Check a basic 300KB transfer") +{ +} + +BulkSendBasicTestCase::~BulkSendBasicTestCase () +{ +} + +void +BulkSendBasicTestCase::SendTx (Ptr p) +{ + m_sent += p->GetSize (); +} + +void +BulkSendBasicTestCase::ReceiveRx (Ptr p, const Address& addr) +{ + m_received += p->GetSize (); +} + +void +BulkSendBasicTestCase::DoRun (void) +{ + Ptr sender = CreateObject (); + Ptr receiver = CreateObject (); + NodeContainer nodes; + nodes.Add (sender); + nodes.Add (receiver); + PointToPointHelper p2pHelper; + p2pHelper.SetDeviceAttribute ("DataRate", StringValue ("10Mbps")); + p2pHelper.SetChannelAttribute ("Delay", StringValue ("10ms")); + NetDeviceContainer devices; + devices = p2pHelper.Install (nodes); + InternetStackHelper internet; + internet.Install (nodes); + Ipv4AddressHelper ipv4; + ipv4.SetBase ("10.1.1.0", "255.255.255.0"); + Ipv4InterfaceContainer i = ipv4.Assign (devices); + uint16_t port = 9; + BulkSendHelper sourceHelper ("ns3::TcpSocketFactory", + InetSocketAddress (i.GetAddress (1), port)); + sourceHelper.SetAttribute ("MaxBytes", UintegerValue (300000)); + ApplicationContainer sourceApp = sourceHelper.Install (nodes.Get (0)); + sourceApp.Start (Seconds (0.0)); + sourceApp.Stop (Seconds (10.0)); + PacketSinkHelper sinkHelper ("ns3::TcpSocketFactory", + InetSocketAddress (Ipv4Address::GetAny (), port)); + ApplicationContainer sinkApp = sinkHelper.Install (nodes.Get (1)); + sinkApp.Start (Seconds (0.0)); + sinkApp.Stop (Seconds (10.0)); + + Ptr source = DynamicCast (sourceApp.Get (0)); + Ptr sink = DynamicCast (sinkApp.Get (0)); + + source->TraceConnectWithoutContext ("Tx", MakeCallback (&BulkSendBasicTestCase::SendTx, this)); + sink->TraceConnectWithoutContext ("Rx", MakeCallback (&BulkSendBasicTestCase::ReceiveRx, this)); + + Simulator::Run (); + Simulator::Destroy (); + + NS_TEST_ASSERT_MSG_EQ (m_sent, 300000, "Sent the full 300000 bytes"); + NS_TEST_ASSERT_MSG_EQ (m_received, 300000, "Received the full 300000 bytes"); +} + +// This test checks that the sequence number is sent and received in sequence +// despite the sending application having to pause and restart its sending +// due to a temporarily full transmit buffer. +class BulkSendSeqTsSizeTestCase : public TestCase +{ +public: + BulkSendSeqTsSizeTestCase (); + virtual ~BulkSendSeqTsSizeTestCase (); + +private: + virtual void DoRun (void); + void SendTx (Ptr p, const Address &from, const Address & to, const SeqTsSizeHeader &header); + void ReceiveRx (Ptr p, const Address &from, const Address & to, const SeqTsSizeHeader &header); + uint64_t m_sent {0}; + uint64_t m_received {0}; + uint64_t m_seqTxCounter {0}; + uint64_t m_seqRxCounter {0}; + Time m_lastTxTs {Seconds (0)}; + Time m_lastRxTs {Seconds (0)}; +}; + +BulkSendSeqTsSizeTestCase::BulkSendSeqTsSizeTestCase () + : TestCase ("Check a 300KB transfer with SeqTsSize header enabled") +{ +} + +BulkSendSeqTsSizeTestCase::~BulkSendSeqTsSizeTestCase () +{ +} + +void +BulkSendSeqTsSizeTestCase::SendTx (Ptr p, const Address &from, const Address & to, const SeqTsSizeHeader &header) +{ + // The header is not serialized onto the packet in this trace + m_sent += p->GetSize () + header.GetSerializedSize (); + NS_TEST_ASSERT_MSG_EQ (header.GetSeq (), m_seqTxCounter, "Missing sequence number"); + m_seqTxCounter++; + NS_TEST_ASSERT_MSG_GT_OR_EQ (header.GetTs (), m_lastTxTs, "Timestamp less than last time"); + m_lastTxTs = header.GetTs (); +} + +void +BulkSendSeqTsSizeTestCase::ReceiveRx (Ptr p, const Address &from, const Address & to, const SeqTsSizeHeader &header) +{ + // The header is not serialized onto the packet in this trace + m_received += p->GetSize () + header.GetSerializedSize (); + NS_TEST_ASSERT_MSG_EQ (header.GetSeq (), m_seqRxCounter, "Missing sequence number"); + m_seqRxCounter++; + NS_TEST_ASSERT_MSG_GT_OR_EQ (header.GetTs (), m_lastRxTs, "Timestamp less than last time"); + m_lastRxTs = header.GetTs (); +} + +void +BulkSendSeqTsSizeTestCase::DoRun (void) +{ + Ptr sender = CreateObject (); + Ptr receiver = CreateObject (); + NodeContainer nodes; + nodes.Add (sender); + nodes.Add (receiver); + PointToPointHelper p2pHelper; + p2pHelper.SetDeviceAttribute ("DataRate", StringValue ("10Mbps")); + p2pHelper.SetChannelAttribute ("Delay", StringValue ("10ms")); + NetDeviceContainer devices; + devices = p2pHelper.Install (nodes); + InternetStackHelper internet; + internet.Install (nodes); + Ipv4AddressHelper ipv4; + ipv4.SetBase ("10.1.1.0", "255.255.255.0"); + Ipv4InterfaceContainer i = ipv4.Assign (devices); + uint16_t port = 9; + BulkSendHelper sourceHelper ("ns3::TcpSocketFactory", + InetSocketAddress (i.GetAddress (1), port)); + sourceHelper.SetAttribute ("MaxBytes", UintegerValue (300000)); + sourceHelper.SetAttribute ("EnableSeqTsSizeHeader", BooleanValue (true)); + ApplicationContainer sourceApp = sourceHelper.Install (nodes.Get (0)); + sourceApp.Start (Seconds (0.0)); + sourceApp.Stop (Seconds (10.0)); + PacketSinkHelper sinkHelper ("ns3::TcpSocketFactory", + InetSocketAddress (Ipv4Address::GetAny (), port)); + sinkHelper.SetAttribute ("EnableSeqTsSizeHeader", BooleanValue (true)); + ApplicationContainer sinkApp = sinkHelper.Install (nodes.Get (1)); + sinkApp.Start (Seconds (0.0)); + sinkApp.Stop (Seconds (10.0)); + + Ptr source = DynamicCast (sourceApp.Get (0)); + Ptr sink = DynamicCast (sinkApp.Get (0)); + + source->TraceConnectWithoutContext ("TxWithSeqTsSize", MakeCallback (&BulkSendSeqTsSizeTestCase::SendTx, this)); + sink->TraceConnectWithoutContext ("RxWithSeqTsSize", MakeCallback (&BulkSendSeqTsSizeTestCase::ReceiveRx, this)); + + Simulator::Run (); + Simulator::Destroy (); + + NS_TEST_ASSERT_MSG_EQ (m_sent, 300000, "Sent the full 300000 bytes"); + NS_TEST_ASSERT_MSG_EQ (m_received, 300000, "Received the full 300000 bytes"); +} + +class BulkSendTestSuite : public TestSuite +{ +public: + BulkSendTestSuite (); +}; + +BulkSendTestSuite::BulkSendTestSuite () + : TestSuite ("bulk-send-application", UNIT) +{ + AddTestCase (new BulkSendBasicTestCase, TestCase::QUICK); + AddTestCase (new BulkSendSeqTsSizeTestCase, TestCase::QUICK); +} + +static BulkSendTestSuite g_bulkSendTestSuite; + diff --git a/src/applications/wscript b/src/applications/wscript index 64ebbc8c1..2ae4cf789 100644 --- a/src/applications/wscript +++ b/src/applications/wscript @@ -31,6 +31,7 @@ def build(bld): applications_test = bld.create_ns3_module_test_library('applications') applications_test.source = [ 'test/three-gpp-http-client-server-test.cc', + 'test/bulk-send-application-test-suite.cc', 'test/udp-client-server-test.cc' ]