Patch addresses bug 227.
authorcraigdo@craig-dowells-imac.local
Thu, 19 Jun 2008 14:58:46 -0700
changeset 3294 2ade6552db36
parent 3293 97c8fa9fa536
child 3295 133490cf0210
child 3296 0695f4b2a2bd
Patch addresses bug 227.
examples/tcp-large-transfer.cc
--- a/examples/tcp-large-transfer.cc	Thu Jun 19 14:53:53 2008 -0700
+++ b/examples/tcp-large-transfer.cc	Thu Jun 19 14:58:46 2008 -0700
@@ -25,12 +25,10 @@
 // - Tracing of queues and packet receptions to file 
 //   "tcp-large-transfer.tr"
 // - pcap traces also generated in the following files
-//   "tcp-large-transfer.pcap-$n-$i" where n and i represent node and interface numbers respectively
+//   "tcp-large-transfer-$n-$i.pcap" where n and i represent node and interface
+// numbers respectively
 //  Usage (e.g.): ./waf --run tcp-large-transfer
 
-//XXX this isn't working as described right now
-//it is just blasting away for 10 seconds, with no fixed amount of data
-//being sent
 
 #include <ctype.h>
 #include <iostream>
@@ -48,37 +46,15 @@
 
 NS_LOG_COMPONENT_DEFINE ("TcpLargeTransfer");
 
-void 
-ApplicationTraceSink (Ptr<const Packet> packet,
-                      const Address &addr)
-{
-// g_log is not declared in optimized builds
-// should convert this to use of some other flag than the logging system
-#ifdef NS3_LOG_ENABLE
-  if (!g_log.IsNoneEnabled ()) {
-    if (InetSocketAddress::IsMatchingType (addr) )
-      {
-      InetSocketAddress address = InetSocketAddress::ConvertFrom (addr);
-        std::cout << "PacketSink received size " << 
-        packet->GetSize () << " at time " << 
-        Simulator::Now ().GetSeconds () << " from address: " << 
-        address.GetIpv4 () << std::endl;
-        char buf[2000]; 
-        memcpy(buf, packet->PeekData (), packet->GetSize ());
-        for (uint32_t i=0; i < packet->GetSize (); i++)
-          {
-            std::cout << buf[i];
-            if (i && i % 60 == 0) 
-              std::cout << std::endl; 
-          }
-        std::cout << std::endl << std::endl;
-    }
-  }
-#endif
-}
+// The number of bytes to send in this simulation.
+static uint32_t txBytes = 2000000;
 
-void CloseConnection (Ptr<Socket> localSocket);
-void StartFlow(Ptr<Socket>, uint32_t, Ipv4Address, uint16_t);
+// These are for starting the writing process, and handling the sending 
+// socket's notification upcalls (events).  These two together more or less
+// implement a sending "Application", although not a proper ns3::Application
+// subclass.
+
+void StartFlow(Ptr<Socket>, Ipv4Address, uint16_t);
 void WriteUntilBufferFull (Ptr<Socket>, uint32_t);
 
 int main (int argc, char *argv[])
@@ -101,27 +77,34 @@
   CommandLine cmd;
   cmd.Parse (argc, argv);
 
-  // Here, we will explicitly create three nodes. 
-  NodeContainer c0;
-  c0.Create (2);
+  // Here, we will explicitly create three nodes.  The first container contains
+  // nodes 0 and 1 from the diagram above, and the second one contains nodes
+  // 1 and 2.  This reflects the channel connectivity, and will be used to
+  // install the network interfaces and connect them with a channel.
+  NodeContainer n0n1;
+  n0n1.Create (2);
 
-  NodeContainer c1;
-  c1.Add (c0.Get (1));
-  c1.Create (1);
+  NodeContainer n1n2;
+  n1n2.Add (n0n1.Get (1));
+  n1n2.Create (1);
 
   // We create the channels first without any IP addressing information
+  // First make and configure the helper, so that it will put the appropriate
+  // parameters on the network interfaces and channels we are about to install.
   PointToPointHelper p2p;
   p2p.SetDeviceParameter ("DataRate", DataRateValue (DataRate(10000000)));
   p2p.SetChannelParameter ("Delay", TimeValue (MilliSeconds(10)));
-  NetDeviceContainer dev0 = p2p.Install (c0);
-  NetDeviceContainer dev1 = p2p.Install (c1);
+
+  // And then install devices and channels connecting our topology.
+  NetDeviceContainer dev0 = p2p.Install (n0n1);
+  NetDeviceContainer dev1 = p2p.Install (n1n2);
 
-  // add ip/tcp stack to nodes.
-  NodeContainer c = NodeContainer (c0, c1.Get (1));
+  // Now add ip/tcp stack to all nodes.
+  NodeContainer allNodes = NodeContainer (n0n1, n1n2.Get (1));
   InternetStackHelper internet;
-  internet.Install (c);
+  internet.Install (allNodes);
 
-  // Later, we add IP addresses.  
+  // Later, we add IP addresses.
   Ipv4AddressHelper ipv4;
   ipv4.SetBase ("10.1.3.0", "255.255.255.0");
   ipv4.Assign (dev0);
@@ -133,82 +116,105 @@
 
   ///////////////////////////////////////////////////////////////////////////
   // Simulation 1
-  // 
+  //
   // Send 2000000 bytes over a connection to server port 50000 at time 0
-  // Should observe SYN exchange, a lot of data segments, and FIN exchange
+  // Should observe SYN exchange, a lot of data segments and ACKS, and FIN 
+  // exchange.  FIN exchange isn't quite compliant with TCP spec (see release
+  // notes for more info)
   //
   ///////////////////////////////////////////////////////////////////////////
 
-  int nBytes = 2000000;
   uint16_t servPort = 50000;
 
-  // Create a packet sink to receive these packets
+  // Create a packet sink to receive these packets on n2...
   PacketSinkHelper sink ("ns3::TcpSocketFactory",
                          InetSocketAddress (Ipv4Address::GetAny (), servPort));
 
-  ApplicationContainer apps = sink.Install (c1.Get (1));
+  ApplicationContainer apps = sink.Install (n1n2.Get (1));
   apps.Start (Seconds (0.0));
 
-  // and generate traffic to remote sink.
-  //TypeId tid = TypeId::LookupByName ("ns3::TcpSocketFactory");
-  Ptr<Socket> localSocket = Socket::CreateSocket (c0.Get (0), TcpSocketFactory::GetTypeId ());
+  // Create a source to send packets from n0.  Instead of a full Application
+  // and the helper APIs you might see in other example files, this example
+  // will use sockets directly and register some socket callbacks as a sending
+  // "Application".
+
+  // Create and bind the socket...
+  Ptr<Socket> localSocket =
+      Socket::CreateSocket (n0n1.Get (0), TcpSocketFactory::GetTypeId ());
   localSocket->Bind ();
-  Simulator::ScheduleNow (&StartFlow, localSocket, nBytes,
+
+  // ...and schedule the sending "Application"; This is similar to what an 
+  // ns3::Application subclass would do internally.
+  Simulator::ScheduleNow (&StartFlow, localSocket,
                           ipInterfs.GetAddress (1), servPort);
 
-  Config::ConnectWithoutContext ("/NodeList/*/ApplicationList/*/Rx", 
-                   MakeCallback (&ApplicationTraceSink));
+  // One can toggle the comment for the following line on or off to see the
+  // effects of finite send buffer modelling.  One can also change the size of
+  // said buffer.
 
+  //localSocket->SetAttribute("SndBufSize", UintegerValue(4096));
+
+  //Ask for ASCII and pcap traces of network traffic
   std::ofstream ascii;
   ascii.open ("tcp-large-transfer.tr");
   PointToPointHelper::EnableAsciiAll (ascii);
 
   PointToPointHelper::EnablePcapAll ("tcp-large-transfer");
 
+  // Finally, set up the simulator to run.  The 1000 second hard limit is a
+  // failsafe in case some change above causes the simulation to never end
   Simulator::Stop (Seconds(1000));
   Simulator::Run ();
   Simulator::Destroy ();
 }
 
-void CloseConnection (Ptr<Socket> localSocket)
-{
-  localSocket->Close ();
-}
 
-void StartFlow(Ptr<Socket> localSocket, uint32_t nBytes, 
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//begin implementation of sending "Application"
+void StartFlow(Ptr<Socket> localSocket,
                Ipv4Address servAddress,
                uint16_t servPort)
 {
- // NS_LOG_LOGIC("Starting flow at time " <<  Simulator::Now ().GetSeconds ());
+  NS_LOG_LOGIC("Starting flow at time " <<  Simulator::Now ().GetSeconds ());
   localSocket->Connect (InetSocketAddress (servAddress, servPort));//connect
-  localSocket->SetConnectCallback (MakeCallback (&CloseConnection),
-                                   Callback<void, Ptr<Socket> > ());
-  //we want to close as soon as the connection is established
-  //the tcp state machine and outgoing buffer will assure that
-  //all of the data is delivered
+
+  // tell the tcp implementation to call WriteUntilBufferFull again
+  // if we blocked and new tx buffer space becomes available
   localSocket->SetSendCallback (MakeCallback (&WriteUntilBufferFull));
-  WriteUntilBufferFull (localSocket, nBytes);
+  WriteUntilBufferFull (localSocket, txBytes);
 }
 
-void WriteUntilBufferFull (Ptr<Socket> localSocket, uint32_t nBytes)
+void WriteUntilBufferFull (Ptr<Socket> localSocket, uint32_t txSpace)
 {
   // Perform series of 1040 byte writes (this is a multiple of 26 since
   // we want to detect data splicing in the output stream)
   uint32_t writeSize = 1040;
   uint8_t data[writeSize];
-  while (nBytes > 0) {
-    uint32_t curSize= nBytes > writeSize ? writeSize : nBytes;
+
+  while (txBytes > 0) {
+    uint32_t curSize= txBytes > writeSize ? writeSize : txBytes;
+    if (curSize > txSpace)
+      curSize = txSpace;
     for(uint32_t i = 0; i < curSize; ++i)
     {
       char m = toascii (97 + i % 26);
       data[i] = m;
     }
-    uint32_t amountSent = localSocket->Send (data, curSize, 0);
-    if(amountSent < curSize)
+    int amountSent = localSocket->Send (data, curSize, 0);
+    if(amountSent < 0)
       {
-        std::cout << "Socket blocking, returning" << std::endl;
+        // we will be called again when new tx space becomes available.
+        std::cout << "Socket blocking, " << txBytes << " left to write, returning" << std::endl;
         return;
       }
-    nBytes -= curSize;
+    txBytes -= curSize;
+    if (amountSent != (int)curSize)
+      {
+        std::cout << "Short Write, returning" << std::endl;
+        return;
+      }
   }
+  localSocket->Close ();
 }