Nagle's algorithm in TCP (closes bug 1039)

This commit is contained in:
Adrian S Tam
2011-12-07 11:22:10 -05:00
parent 4c7b5db42b
commit 0ca076a06a
9 changed files with 256 additions and 1 deletions

View File

@@ -85,6 +85,7 @@ NscTcpSocketImpl::NscTcpSocketImpl(const NscTcpSocketImpl& sock)
: 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 @@ NscTcpSocketImpl::GetDelAckMaxCount (void) const
return m_delAckMaxCount;
}
void
NscTcpSocketImpl::SetTcpNoDelay (bool noDelay)
{
m_noDelay = noDelay;
}
bool
NscTcpSocketImpl::GetTcpNoDelay (void) const
{
return m_noDelay;
}
void
NscTcpSocketImpl::SetPersistTimeout (Time timeout)
{

View File

@@ -125,12 +125,15 @@ private:
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;

View File

@@ -54,7 +54,7 @@ RttEstimator::GetTypeId (void)
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 ())

View File

@@ -120,6 +120,7 @@ TcpSocketBase::TcpSocketBase (const TcpSocketBase& sock)
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 @@ TcpSocketBase::SendPendingData (bool withAck)
{
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
@@ -1835,6 +1844,18 @@ TcpSocketBase::GetDelAckMaxCount (void) const
return m_delAckMaxCount;
}
void
TcpSocketBase::SetTcpNoDelay (bool noDelay)
{
m_noDelay = noDelay;
}
bool
TcpSocketBase::GetTcpNoDelay (void) const
{
return m_noDelay;
}
void
TcpSocketBase::SetPersistTimeout (Time timeout)
{

View File

@@ -116,6 +116,8 @@ protected:
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 @@ protected:
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

View File

@@ -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 @@ TcpSocket::GetTypeId (void)
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)),

View File

@@ -90,6 +90,8 @@ private:
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;

View File

@@ -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;

View File

@@ -28,6 +28,7 @@ def build(bld):
'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']: