--- a/src/internet/model/nsc-tcp-socket-impl.cc Tue Dec 06 08:42:44 2011 -0500
+++ b/src/internet/model/nsc-tcp-socket-impl.cc Wed Dec 07 11:22:10 2011 -0500
@@ -85,6 +85,7 @@
: TcpSocket (sock), //copy the base class callbacks
m_delAckMaxCount (sock.m_delAckMaxCount),
m_delAckTimeout (sock.m_delAckTimeout),
+ m_noDelay (sock.m_noDelay),
m_endPoint (0),
m_node (sock.m_node),
m_tcp (sock.m_tcp),
@@ -797,6 +798,18 @@
return m_delAckMaxCount;
}
+void
+NscTcpSocketImpl::SetTcpNoDelay (bool noDelay)
+{
+ m_noDelay = noDelay;
+}
+
+bool
+NscTcpSocketImpl::GetTcpNoDelay (void) const
+{
+ return m_noDelay;
+}
+
void
NscTcpSocketImpl::SetPersistTimeout (Time timeout)
{
--- a/src/internet/model/nsc-tcp-socket-impl.h Tue Dec 06 08:42:44 2011 -0500
+++ b/src/internet/model/nsc-tcp-socket-impl.h Wed Dec 07 11:22:10 2011 -0500
@@ -125,12 +125,15 @@
virtual Time GetDelAckTimeout (void) const;
virtual void SetDelAckMaxCount (uint32_t count);
virtual uint32_t GetDelAckMaxCount (void) const;
+ virtual void SetTcpNoDelay (bool noDelay);
+ virtual bool GetTcpNoDelay (void) const;
virtual void SetPersistTimeout (Time timeout);
virtual Time GetPersistTimeout (void) const;
enum Socket::SocketErrno GetNativeNs3Errno (int err) const;
uint32_t m_delAckMaxCount;
Time m_delAckTimeout;
+ bool m_noDelay;
Ipv4EndPoint *m_endPoint;
Ptr<Node> m_node;
--- a/src/internet/model/rtt-estimator.cc Tue Dec 06 08:42:44 2011 -0500
+++ b/src/internet/model/rtt-estimator.cc Wed Dec 07 11:22:10 2011 -0500
@@ -54,7 +54,7 @@
MakeTimeChecker ())
.AddAttribute ("MinRTO",
"Minimum retransmit timeout value",
- TimeValue (Seconds (0.2)),
+ TimeValue (Seconds (0.2)), // RFC2988 says min RTO=1 sec, but Linux uses 200ms. See http://www.postel.org/pipermail/end2end-interest/2004-November/004402.html
MakeTimeAccessor (&RttEstimator::SetMinRto,
&RttEstimator::GetMinRto),
MakeTimeChecker ())
--- a/src/internet/model/tcp-socket-base.cc Tue Dec 06 08:42:44 2011 -0500
+++ b/src/internet/model/tcp-socket-base.cc Wed Dec 07 11:22:10 2011 -0500
@@ -120,6 +120,7 @@
m_dupAckCount (sock.m_dupAckCount),
m_delAckCount (0),
m_delAckMaxCount (sock.m_delAckMaxCount),
+ m_noDelay (sock.m_noDelay),
m_cnRetries (sock.m_cnRetries),
m_delAckTimeout (sock.m_delAckTimeout),
m_persistTimeout (sock.m_persistTimeout),
@@ -1456,6 +1457,14 @@
{
break; // No more
}
+ // Nagle's algorithm (RFC896): Hold off sending if there is unacked data
+ // in the buffer and the amount of data to send is less than one segment
+ if (!m_noDelay && UnAckDataCount () > 0 &&
+ m_txBuffer.SizeFromSequence (m_nextTxSequence) < m_segmentSize)
+ {
+ NS_LOG_LOGIC ("Invoking Nagle's algorithm. Wait to send.");
+ break;
+ }
uint32_t s = std::min (w, m_segmentSize); // Send no more than window
uint32_t sz = SendDataPacket (m_nextTxSequence, s, withAck);
nPacketsSent++; // Count sent this loop
@@ -1836,6 +1845,18 @@
}
void
+TcpSocketBase::SetTcpNoDelay (bool noDelay)
+{
+ m_noDelay = noDelay;
+}
+
+bool
+TcpSocketBase::GetTcpNoDelay (void) const
+{
+ return m_noDelay;
+}
+
+void
TcpSocketBase::SetPersistTimeout (Time timeout)
{
m_persistTimeout = timeout;
--- a/src/internet/model/tcp-socket-base.h Tue Dec 06 08:42:44 2011 -0500
+++ b/src/internet/model/tcp-socket-base.h Wed Dec 07 11:22:10 2011 -0500
@@ -116,6 +116,8 @@
virtual Time GetDelAckTimeout (void) const;
virtual void SetDelAckMaxCount (uint32_t count);
virtual uint32_t GetDelAckMaxCount (void) const;
+ virtual void SetTcpNoDelay (bool noDelay);
+ virtual bool GetTcpNoDelay (void) const;
virtual void SetPersistTimeout (Time timeout);
virtual Time GetPersistTimeout (void) const;
virtual bool SetAllowBroadcast (bool allowBroadcast);
@@ -189,6 +191,7 @@
uint32_t m_dupAckCount; //< Dupack counter
uint32_t m_delAckCount; //< Delayed ACK counter
uint32_t m_delAckMaxCount; //< Number of packet to fire an ACK before delay timeout
+ bool m_noDelay; //< Set to true to disable Nagle's algorithm
uint32_t m_cnCount; //< Count of remaining connection retries
uint32_t m_cnRetries; //< Number of connection retries before giving up
TracedValue<Time> m_rto; //< Retransmit timeout
--- a/src/internet/model/tcp-socket.cc Tue Dec 06 08:42:44 2011 -0500
+++ b/src/internet/model/tcp-socket.cc Wed Dec 07 11:22:10 2011 -0500
@@ -22,6 +22,7 @@
#include "ns3/log.h"
#include "ns3/uinteger.h"
#include "ns3/double.h"
+#include "ns3/boolean.h"
#include "ns3/trace-source-accessor.h"
#include "ns3/nstime.h"
#include "tcp-socket.h"
@@ -93,6 +94,11 @@
MakeUintegerAccessor (&TcpSocket::GetDelAckMaxCount,
&TcpSocket::SetDelAckMaxCount),
MakeUintegerChecker<uint32_t> ())
+ .AddAttribute ("TcpNoDelay", "Set to true to disable Nagle's algorithm",
+ BooleanValue (true),
+ MakeBooleanAccessor (&TcpSocket::GetTcpNoDelay,
+ &TcpSocket::SetTcpNoDelay),
+ MakeBooleanChecker ())
.AddAttribute ("PersistTimeout",
"Persist timeout to probe for rx window",
TimeValue (Seconds (6)),
--- a/src/internet/model/tcp-socket.h Tue Dec 06 08:42:44 2011 -0500
+++ b/src/internet/model/tcp-socket.h Wed Dec 07 11:22:10 2011 -0500
@@ -90,6 +90,8 @@
virtual Time GetDelAckTimeout (void) const = 0;
virtual void SetDelAckMaxCount (uint32_t count) = 0;
virtual uint32_t GetDelAckMaxCount (void) const = 0;
+ virtual void SetTcpNoDelay (bool noDelay) = 0;
+ virtual bool GetTcpNoDelay (void) const = 0;
virtual void SetPersistTimeout (Time timeout) = 0;
virtual Time GetPersistTimeout (void) const = 0;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/test/ns3tcp/ns3tcp-no-delay-test-suite.cc Wed Dec 07 11:22:10 2011 -0500
@@ -0,0 +1,206 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * 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/log.h"
+#include "ns3/abort.h"
+#include "ns3/test.h"
+#include "ns3/pcap-file.h"
+#include "ns3/config.h"
+#include "ns3/string.h"
+#include "ns3/uinteger.h"
+#include "ns3/boolean.h"
+#include "ns3/data-rate.h"
+#include "ns3/inet-socket-address.h"
+#include "ns3/point-to-point-helper.h"
+#include "ns3/csma-helper.h"
+#include "ns3/internet-stack-helper.h"
+#include "ns3/ipv4-global-routing-helper.h"
+#include "ns3/ipv4-address-helper.h"
+#include "ns3/packet-sink-helper.h"
+#include "ns3/tcp-socket-factory.h"
+#include "ns3/node-container.h"
+#include "ns3/simulator.h"
+#include "ns3tcp-socket-writer.h"
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE ("Ns3TcpNoDelayTest");
+
+// ===========================================================================
+// Tests of Nagle's algorithm and the TCP no delay option
+// ===========================================================================
+//
+//
+class Ns3TcpNoDelayTestCase : public TestCase
+{
+public:
+ Ns3TcpNoDelayTestCase (bool noDelay);
+ virtual ~Ns3TcpNoDelayTestCase () {}
+
+private:
+ virtual void DoRun (void);
+ bool m_noDelay;
+ bool m_writeResults;
+
+ void SinkRx (std::string path, Ptr<const Packet> p, const Address &address);
+
+ TestVectors<uint32_t> m_inputs;
+ TestVectors<uint32_t> m_responses;
+};
+
+Ns3TcpNoDelayTestCase::Ns3TcpNoDelayTestCase (bool noDelay)
+ : TestCase ("Check that ns-3 TCP Nagle's algorithm works correctly and that we can turn it off."),
+ m_noDelay (noDelay),
+ m_writeResults (false)
+{
+}
+
+void
+Ns3TcpNoDelayTestCase::SinkRx (std::string path, Ptr<const Packet> p, const Address &address)
+{
+ m_responses.Add (p->GetSize ());
+}
+
+void
+Ns3TcpNoDelayTestCase::DoRun (void)
+{
+ uint16_t sinkPort = 50000;
+ double sinkStopTime = 8; // sec; will trigger Socket::Close
+ double writerStopTime = 5; // sec; will trigger Socket::Close
+ double simStopTime = 10; // sec
+ Time sinkStopTimeObj = Seconds (sinkStopTime);
+ Time writerStopTimeObj = Seconds (writerStopTime);
+ Time simStopTimeObj= Seconds (simStopTime);
+
+ Ptr<Node> n0 = CreateObject<Node> ();
+ Ptr<Node> n1 = CreateObject<Node> ();
+
+ PointToPointHelper pointToPoint;
+ pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
+ pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
+
+ NetDeviceContainer devices;
+ devices = pointToPoint.Install (n0, n1);
+
+ InternetStackHelper internet;
+ internet.InstallAll ();
+
+ Ipv4AddressHelper address;
+ address.SetBase ("10.1.1.0", "255.255.255.252");
+ Ipv4InterfaceContainer ifContainer = address.Assign (devices);
+
+ Ptr<SocketWriter> socketWriter = CreateObject<SocketWriter> ();
+ Address sinkAddress (InetSocketAddress (ifContainer.GetAddress (1), sinkPort));
+ socketWriter->Setup (n0, sinkAddress);
+ n0->AddApplication (socketWriter);
+ socketWriter->SetStartTime (Seconds (0.));
+ socketWriter->SetStopTime (writerStopTimeObj);
+
+ PacketSinkHelper sink ("ns3::TcpSocketFactory",
+ InetSocketAddress (Ipv4Address::GetAny (), sinkPort));
+ ApplicationContainer apps = sink.Install (n1);
+ // Start the sink application at time zero, and stop it at sinkStopTime
+ apps.Start (Seconds (0.0));
+ apps.Stop (sinkStopTimeObj);
+
+ Config::Connect ("/NodeList/*/ApplicationList/*/$ns3::PacketSink/Rx",
+ MakeCallback (&Ns3TcpNoDelayTestCase::SinkRx, this));
+
+ // Enable or disable TCP no delay option
+ Config::SetDefault ("ns3::TcpSocket::TcpNoDelay", BooleanValue (m_noDelay));
+
+ // Connect the socket writer
+ Simulator::Schedule (Seconds (1), &SocketWriter::Connect, socketWriter);
+
+ // Write 5 packets to get some bytes in flight and some acks going
+ Simulator::Schedule (Seconds (2), &SocketWriter::Write, socketWriter, 2680);
+ m_inputs.Add (536);
+ m_inputs.Add (536);
+ m_inputs.Add (536);
+ m_inputs.Add (536);
+ m_inputs.Add (536);
+
+ // Write one byte after 10 ms to ensure that some data is outstanding
+ // and the window is big enough
+ Simulator::Schedule (Seconds (2.010), &SocketWriter::Write, socketWriter, 1);
+
+ // If Nagle is not enabled, i.e. no delay is on, add an input for a 1-byte
+ // packet to be received
+ if (m_noDelay)
+ {
+ m_inputs.Add (1);
+ }
+
+ // One ms later, write 535 bytes, i.e. one segment size - 1
+ Simulator::Schedule (Seconds (2.012), &SocketWriter::Write, socketWriter, 535);
+
+ // If Nagle is not enabled, add an input for a 535 byte packet,
+ // otherwise, we should get a single "full" packet of 536 bytes
+ if (m_noDelay)
+ {
+ m_inputs.Add (535);
+ }
+ else
+ {
+ m_inputs.Add (536);
+ }
+
+ // Close down the socket
+ Simulator::Schedule (writerStopTimeObj, &SocketWriter::Close, socketWriter);
+
+ if (m_writeResults)
+ {
+ std::ostringstream oss;
+ if (m_noDelay)
+ {
+ oss << "tcp-no-delay-on-test-case";
+ pointToPoint.EnablePcapAll (oss.str ());
+ }
+ else
+ {
+ oss << "tcp-no-delay-off-test-case";
+ pointToPoint.EnablePcapAll (oss.str ());
+ }
+ }
+
+ Simulator::Stop (simStopTimeObj);
+ Simulator::Run ();
+ Simulator::Destroy ();
+
+ // Compare inputs and outputs
+ NS_TEST_ASSERT_MSG_EQ (m_inputs.GetN (), m_responses.GetN (), "Incorrect number of expected receive events");
+ for (uint32_t i = 0; i < m_responses.GetN (); i++)
+ {
+ uint32_t in = m_inputs.Get (i);
+ uint32_t out = m_responses.Get (i);
+ NS_TEST_ASSERT_MSG_EQ (in, out, "Mismatch: expected " << in << " bytes, got " << out << " bytes");
+ }
+}
+
+class Ns3TcpNoDelayTestSuite : public TestSuite
+{
+public:
+ Ns3TcpNoDelayTestSuite ();
+};
+
+Ns3TcpNoDelayTestSuite::Ns3TcpNoDelayTestSuite ()
+ : TestSuite ("ns3-tcp-no-delay", SYSTEM)
+{
+ AddTestCase (new Ns3TcpNoDelayTestCase (true));
+ AddTestCase (new Ns3TcpNoDelayTestCase (false));
+}
+
+static Ns3TcpNoDelayTestSuite ns3TcpNoDelayTestSuite;
--- a/src/test/ns3tcp/wscript Tue Dec 06 08:42:44 2011 -0500
+++ b/src/test/ns3tcp/wscript Wed Dec 07 11:22:10 2011 -0500
@@ -28,6 +28,7 @@
'ns3tcp-socket-test-suite.cc',
'ns3tcp-loss-test-suite.cc',
'ns3tcp-state-test-suite.cc',
+ 'ns3tcp-no-delay-test-suite.cc',
]
if bld.env['NSC_ENABLED']: