Wed Dec 16 20:25:37 2015 +0100 b031302 (HEAD -> master) [BUG 2141] Fixing data sent callback on TCP  [Natale Patriciello] diff --git a/src/internet/model/tcp-socket-base.cc b/src/internet/model/tcp-socket-base.cc index e00574b..2ea76dc 100644 --- a/src/internet/model/tcp-socket-base.cc +++ b/src/internet/model/tcp-socket-base.cc @@ -2430,9 +2430,9 @@ TcpSocketBase::SendDataPacket (SequenceNumber32 seq, uint32_t maxSize, bool with } // Notify the application of the data being sent unless this is a retransmit - if (seq == m_highTxMark) + if (seq + sz > m_highTxMark) { - Simulator::ScheduleNow (&TcpSocketBase::NotifyDataSent, this, sz); + Simulator::ScheduleNow (&TcpSocketBase::NotifyDataSent, this, (seq + sz - m_highTxMark.Get ())); } // Update highTxMark m_highTxMark = std::max (seq + sz, m_highTxMark.Get ()); diff --git a/src/internet/test/tcp-datasentcb-test.cc b/src/internet/test/tcp-datasentcb-test.cc new file mode 100644 index 0000000..382ef29 --- /dev/null +++ b/src/internet/test/tcp-datasentcb-test.cc @@ -0,0 +1,151 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2015 Natale Patriciello + * + * 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 "tcp-general-test.h" +#include "ns3/node.h" +#include "ns3/log.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("TcpDatSentCbTest"); + +/** + * \brief Socket that the 50% of the times saves the entire packet in the buffer, + * while in the other 50% saves only half the packet. + */ +class TcpSocketHalfAck : public TcpSocketMsgBase +{ +public: + static TypeId GetTypeId (void); + + TcpSocketHalfAck () : TcpSocketMsgBase () + { + } + + TcpSocketHalfAck (const TcpSocketHalfAck &other) : TcpSocketMsgBase (other) + { + } +protected: + virtual Ptr Fork (); + virtual void ReceivedData (Ptr packet, const TcpHeader& tcpHeader); +}; + +NS_OBJECT_ENSURE_REGISTERED (TcpSocketHalfAck); + +TypeId +TcpSocketHalfAck::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpSocketHalfAck") + .SetParent () + .SetGroupName ("Internet") + .AddConstructor () + ; + return tid; +} + +Ptr +TcpSocketHalfAck::Fork (void) +{ + return CopyObject (this); +} + +void +TcpSocketHalfAck::ReceivedData(Ptr packet, const TcpHeader &tcpHeader) +{ + NS_LOG_FUNCTION (this << packet << tcpHeader); + static uint32_t times = 1; + + Ptr halved = packet->Copy (); + + if (times % 2 == 0) + halved->RemoveAtEnd (packet->GetSize() / 2); + + times++; + + TcpSocketMsgBase::ReceivedData (halved, tcpHeader); +} + + +/** + * \brief Data Sent callback test + * + * The rationale of this test is to check if the dataSent callback advertises + * to the application all the transmitted bytes. We know in advance how many + * bytes are being transmitted, and we check if the amount of data notified + * equals this value. + * + */ +class TcpDataSentCbTestCase : public TcpGeneralTest +{ +public: + TcpDataSentCbTestCase (const std::string &desc, uint32_t size, uint32_t packets) : + TcpGeneralTest (desc, size, packets), + m_notifiedData (0) + { } + +protected: + virtual Ptr CreateReceiverSocket (Ptr node); + + virtual void DataSent (uint32_t size, SocketWho who); + + virtual void FinalChecks (); + +private: + uint32_t m_notifiedData; +}; + +void +TcpDataSentCbTestCase::DataSent (uint32_t size, SocketWho who) +{ + NS_LOG_FUNCTION (this << who << size); + + m_notifiedData += size; +} + +void +TcpDataSentCbTestCase::FinalChecks() +{ + NS_TEST_ASSERT_MSG_EQ (m_notifiedData, GetPktSize () * GetPktCount () , + "Notified more data than application sent"); +} + +Ptr +TcpDataSentCbTestCase::CreateReceiverSocket (Ptr node) +{ + NS_LOG_FUNCTION (this); + + return CreateSocket (node, TcpSocketHalfAck::GetTypeId (), m_congControlTypeId); +} + +static class TcpDataSentCbTestSuite : public TestSuite +{ +public: + TcpDataSentCbTestSuite () + : TestSuite ("tcp-datasentcb", UNIT) + { + AddTestCase (new TcpDataSentCbTestCase ("Check the data sent callback", 500, 10), TestCase::QUICK); + AddTestCase (new TcpDataSentCbTestCase ("Check the data sent callback", 100, 100), TestCase::QUICK); + AddTestCase (new TcpDataSentCbTestCase ("Check the data sent callback", 1000, 50), TestCase::QUICK); + AddTestCase (new TcpDataSentCbTestCase ("Check the data sent callback", 855, 18), TestCase::QUICK); + AddTestCase (new TcpDataSentCbTestCase ("Check the data sent callback", 1243, 59), TestCase::QUICK); + } + +} g_tcpDataSentCbTestSuite; + +} // namespace ns3 diff --git a/src/internet/test/tcp-general-test.cc b/src/internet/test/tcp-general-test.cc index dd96363..60959c6 100644 --- a/src/internet/test/tcp-general-test.cc +++ b/src/internet/test/tcp-general-test.cc @@ -180,6 +180,7 @@ TcpGeneralTest::DoRun (void) m_senderSocket->SetRcvAckCb (MakeCallback (&TcpGeneralTest::RcvAckCb, this)); m_senderSocket->SetProcessedAckCb (MakeCallback (&TcpGeneralTest::ProcessedAckCb, this)); m_senderSocket->SetRetransmitCb (MakeCallback (&TcpGeneralTest::RtoExpiredCb, this)); + m_senderSocket->SetDataSentCallback(MakeCallback (&TcpGeneralTest::DataSentCb, this)); m_senderSocket->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&TcpGeneralTest::CWndTrace, this)); m_senderSocket->TraceConnectWithoutContext ("CongState", @@ -241,7 +242,7 @@ TcpGeneralTest::CreateSocket (Ptr node, TypeId socketType, socketFactory.SetTypeId (socketType); Ptr rtt = rttFactory.Create (); - Ptr socket = socketFactory.Create (); + Ptr socket = DynamicCast (socketFactory.Create ()); Ptr algo = congestionAlgorithmFactory.Create (); socket->SetNode (node); @@ -350,6 +351,23 @@ TcpGeneralTest::RtoExpiredCb (const Ptr tcb, } void +TcpGeneralTest::DataSentCb (Ptr socket, uint32_t size) +{ + if (socket->GetNode () == m_receiverSocket->GetNode ()) + { + DataSent (size, RECEIVER); + } + else if (socket->GetNode () == m_senderSocket->GetNode ()) + { + DataSent (size, SENDER); + } + else + { + NS_FATAL_ERROR ("Closed socket, but not recognized"); + } +} + +void TcpGeneralTest::ErrorCloseCb (Ptr socket) { if (socket->GetNode () == m_receiverSocket->GetNode ()) diff --git a/src/internet/test/tcp-general-test.h b/src/internet/test/tcp-general-test.h index e966ae4..fd7a307 100644 --- a/src/internet/test/tcp-general-test.h +++ b/src/internet/test/tcp-general-test.h @@ -569,6 +569,16 @@ protected: } /** + * \brief Notifying application for sent data + * + * \param size the amount of bytes transmitted + * \param who where the RTO has expired (SENDER or RECEIVER) + */ + virtual void DataSent (uint32_t size, SocketWho who) + { + } + + /** * \brief Performs the (eventual) final checks through test asserts * */ @@ -687,6 +697,7 @@ private: const Ptr tcp); void RtoExpiredCb (const Ptr tcb, const Ptr tcp); + void DataSentCb (Ptr socket, uint32_t size); void ForkCb (Ptr tcp); void HandleAccept (Ptr socket, const Address& from); diff --git a/src/internet/wscript b/src/internet/wscript index fafe9f8..36a3942 100644 --- a/src/internet/wscript +++ b/src/internet/wscript @@ -248,10 +248,11 @@ def build(bld): 'test/ipv6-fragmentation-test.cc', 'test/ipv6-forwarding-test.cc', 'test/ipv6-ripng-test.cc', - 'test/ipv6-address-helper-test-suite.cc', + 'test/ipv6-address-helper-test-suite.cc', 'test/rtt-test.cc', 'test/codel-queue-test-suite.cc', 'test/tcp-endpoint-bug2211.cc', + 'test/tcp-datasentcb-test.cc', ] privateheaders = bld(features='ns3privateheader') privateheaders.module = 'internet'