Nagle's algorithm in TCP (closes bug 1039)
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ())
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
206
src/test/ns3tcp/ns3tcp-no-delay-test-suite.cc
Normal file
206
src/test/ns3tcp/ns3tcp-no-delay-test-suite.cc
Normal 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;
|
||||
@@ -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']:
|
||||
|
||||
Reference in New Issue
Block a user