--- a/examples/tcp/tcp-echo-example.cc +++ a/examples/tcp/tcp-echo-example.cc @@ -0,0 +1,173 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2012 + * + * 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 + * + * Authors: Frank Helbert , + * Luiz Arthur Feitosa dos Santos and + * Rodrigo Campiolo + */ + +#include +#include + +#include "ns3/core-module.h" +#include "ns3/network-module.h" +#include "ns3/internet-module.h" +#include "ns3/point-to-point-module.h" +#include "ns3/applications-module.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("TcpEchoExample"); + +// =========================================================================== +// +// node 0 node 1 +// +----------------+ +----------------+ +// | ns-3 TCP | | ns-3 TCP | +// +----------------+ +----------------+ +// | 10.1.1.1 | | 10.1.1.2 | +// +----------------+ +----------------+ +// | point-to-point | | point-to-point | +// +----------------+ +----------------+ +// | | +// +---------------------+ +// 100 Mbps, 1 ms +// +// This is a simple test of TCP connection. We create two nodes, +// one will be a TCP client echo and other the TCP server echo, +// the nodes transmit data in the simple point-to-point channel. +// In this scenario the node 0 (client) send one packet TCP to +// the node 1 (server), then server receive this packet, return +// this back to the node 0 (client) and finish TCP connection. +// +// =========================================================================== +// + +int +main (int argc, char *argv[]) +{ + + // + // The three lines below enable debugging mode. Comment these three lines for disable. + // + LogComponentEnable ("TcpEchoExample", LOG_LEVEL_INFO); + LogComponentEnable ("TcpEchoClientApplication", LOG_LEVEL_ALL); + LogComponentEnable ("TcpEchoServerApplication", LOG_LEVEL_ALL); + + // + // Allow the user to override any of the defaults and the above Bind() at + // run-time, via command-line arguments + // + bool useV6 = false; + Address serverAddress; + + CommandLine cmd; + cmd.AddValue ("useIpv6", "Use Ipv6", useV6); + cmd.Parse (argc, argv); + + // + // Create two nodes required by the topology (point-to-point). + // + NS_LOG_INFO ("Create nodes."); + NodeContainer nodes; + nodes.Create (2); + + // + // Create and configure channel for the communication. + // + NS_LOG_INFO("Create and configuring channels."); + PointToPointHelper pointToPoint; + pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("100Mbps")); + pointToPoint.SetChannelAttribute ("Delay", StringValue ("1ms")); + + NetDeviceContainer devices; + devices = pointToPoint.Install (nodes); + + InternetStackHelper stack; + stack.Install (nodes); + + // + // Now, add IP address in the nodes. + // + + NS_LOG_INFO("Assign IP Address."); + if (useV6 == false) + { + Ipv4AddressHelper ipv4; + ipv4.SetBase ("10.1.1.0", "255.255.255.0"); + Ipv4InterfaceContainer i4 = ipv4.Assign (devices); + // for IPv4 GetAddress (1) is equivalent to GetAddress (1,0) + serverAddress = Address(i4.GetAddress (1)); + } + else + { + Ipv6AddressHelper ipv6; + ipv6.NewNetwork ("2001:0000:f00d:cafe::", 64); + Ipv6InterfaceContainer i6 = ipv6.Assign (devices); + // Notice the difference between IPv4 and IPv6. + // In both cases GetAddress (0,0) is the localhost (127.0.0.1 or ::1) + // However in IPv6 GetAddress (1,0) is the Link-Local address + // and GetAddress (1,1) is the Global address. + serverAddress = Address(i6.GetAddress (1,1)); + } + + // + // Create a TcpEchoServer on node 1. + // + NS_LOG_INFO("Create Server Application."); + uint16_t port = 7; // well-known echo port number. + TcpEchoServerHelper echoServer (port); + ApplicationContainer serverApps = echoServer.Install (nodes.Get (1)); + serverApps.Start (Seconds (1.0)); + serverApps.Stop (Seconds (11.0)); + + // Create a TcpEchoClient application to send TCP packet to server. + NS_LOG_INFO("Create Client Application."); + TcpEchoClientHelper echoClient (serverAddress, port); + echoClient.SetAttribute ("MaxPackets", UintegerValue (10)); + echoClient.SetAttribute ("Interval", TimeValue (Seconds (1.0))); + echoClient.SetAttribute ("PacketSize", UintegerValue (183)); + ApplicationContainer clientApps = echoClient.Install (nodes.Get (0)); + clientApps.Start (Seconds (2.0)); + clientApps.Stop (Seconds (10.0)); + +#if 0 +// +// Users may find it convenient to initialize echo packets with actual data; +// the below lines suggest how to do this +// + echoClient.SetFill (clientApps.Get (0), "Hello World"); + + echoClient.SetFill (clientApps.Get (0), 0xa5, 1024); + + uint8_t fill[] = { 0, 1, 2, 3, 4, 5, 6}; + echoClient.SetFill (clientApps.Get (0), fill, sizeof(fill), 1024); +#endif + + // + // Enable packet trace in pcap format and save on file tcp_echo_example.pcap. + // Comment the two lines below to disable this. + AsciiTraceHelper ascii; + pointToPoint.EnablePcapAll("tcp_echo_example"); + + // Start the simulation. + NS_LOG_INFO("Start Simulation."); + Simulator::Run (); + Simulator::Destroy (); + NS_LOG_INFO("Simulation finished."); + return 0; +} --- a/examples/tcp/wscript +++ a/examples/tcp/wscript @@ -24,3 +24,7 @@ obj = bld.create_ns3_program('tcp-bulk-send', ['point-to-point', 'applications', 'internet']) obj.source = 'tcp-bulk-send.cc' + + obj = bld.create_ns3_program('tcp-echo-example', + ['point-to-point','applications']) + obj.source = 'tcp-echo-example.cc' --- a/src/applications/helper/tcp-echo-helper.cc +++ a/src/applications/helper/tcp-echo-helper.cc @@ -0,0 +1,158 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2012 + * + * 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 + * + * Authors: Frank Helbert , + * Luiz Arthur Feitosa dos Santos and + * Rodrigo Campiolo + */ + +#include "tcp-echo-helper.h" +#include "ns3/tcp-echo-server.h" +#include "ns3/tcp-echo-client.h" +#include "ns3/uinteger.h" +#include "ns3/names.h" + +namespace ns3 { + + +TcpEchoServerHelper::TcpEchoServerHelper (uint16_t port) +{ + m_factory.SetTypeId (TcpEchoServer::GetTypeId ()); + SetAttribute ("Port", UintegerValue (port)); +} + +void +TcpEchoServerHelper::SetAttribute ( + std::string name, + const AttributeValue &value) +{ + m_factory.Set (name, value); +} + +ApplicationContainer +TcpEchoServerHelper::Install (Ptr node) const +{ + return ApplicationContainer (InstallPriv (node)); +} + +ApplicationContainer +TcpEchoServerHelper::Install (std::string nodeName) const +{ + Ptr node = Names::Find (nodeName); + return ApplicationContainer (InstallPriv (node)); +} + +ApplicationContainer +TcpEchoServerHelper::Install (NodeContainer c) const +{ + ApplicationContainer apps; + for (NodeContainer::Iterator i = c.Begin (); i != c.End (); ++i) + { + apps.Add (InstallPriv (*i)); + } + + return apps; +} + +Ptr +TcpEchoServerHelper::InstallPriv (Ptr node) const +{ + Ptr app = m_factory.Create (); + node->AddApplication (app); + + return app; +} + +TcpEchoClientHelper::TcpEchoClientHelper (Address address, uint16_t port) +{ + m_factory.SetTypeId (TcpEchoClient::GetTypeId ()); + SetAttribute ("RemoteAddress", AddressValue (address)); + SetAttribute ("RemotePort", UintegerValue (port)); +} + +void +TcpEchoClientHelper::SetAttribute ( + std::string name, + const AttributeValue &value) +{ + m_factory.Set (name, value); +} + +void +TcpEchoClientHelper::SetFill (Ptr app, std::string fill) +{ + app->GetObject()->SetFill (fill); +} + +void +TcpEchoClientHelper::SetFill (Ptr app, uint8_t fill, uint32_t dataLength) +{ + app->GetObject()->SetFill (fill, dataLength); +} + +void +TcpEchoClientHelper::SetFill (Ptr app, uint8_t *fill, uint32_t fillLength, uint32_t dataLength) +{ + app->GetObject()->SetFill (fill, fillLength, dataLength); +} + +ApplicationContainer +TcpEchoClientHelper::Install (Ptr node) +{ + return ApplicationContainer (InstallPriv (node)); +} + +ApplicationContainer +TcpEchoClientHelper::Install (std::string nodeName) +{ + Ptr node = Names::Find (nodeName); + return ApplicationContainer (InstallPriv (node)); +} + +ApplicationContainer +TcpEchoClientHelper::Install (NodeContainer c) +{ + ApplicationContainer apps; + for (NodeContainer::Iterator i = c.Begin (); i != c.End (); ++i) + { + Ptr node = *i; + + m_client = m_factory.Create (); + node->AddApplication (m_client); + apps.Add (m_client); + + } + + return apps; +} + +Ptr +TcpEchoClientHelper::InstallPriv (Ptr node) +{ + m_client = m_factory.Create (); + node->AddApplication (m_client); + + return m_client; +} + +Ptr +TcpEchoClientHelper::GetClient (void) +{ + return m_client; +} + +} // namespace ns3 --- a/src/applications/helper/tcp-echo-helper.h +++ a/src/applications/helper/tcp-echo-helper.h @@ -0,0 +1,217 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2012 + * + * 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 + * + * Authors: Frank Helbert , + * Luiz Arthur Feitosa dos Santos and + * Rodrigo Campiolo + */ + +#ifndef TCP_ECHO_HELPER +#define TCP_ECHO_HELPER + +#include +#include "ns3/application-container.h" +#include "ns3/node-container.h" +#include "ns3/object-factory.h" +#include "ns3/ipv4-address.h" + +namespace ns3 { + +class TcpEchoClient; + +/** + * \brief Create a server application which waits for TCP connections. + * This server receive TCP packets and send back to the client. + * + */ + +class TcpEchoServerHelper +{ +public: + /** + * Create TcpEchoServerHelper which will make life easier for people trying + * to set up simulations with TCP echos. + * + * \param port The port the server will wait on for incoming packets + */ + TcpEchoServerHelper (uint16_t port); + + /** + * Record an attribute to be set in each Application after it is is created. + * + * \param name the name of the attribute to set + * \param value the value of the attribute to set + */ + void SetAttribute (std::string name, const AttributeValue &value); + + /** + * Create a TcpEchoServerApplication on the specified Node. + * + * \param node The node on which to create the Application. The node is + * specified by a Ptr. + * + * \returns An ApplicationContainer holding the Application created, + */ + ApplicationContainer Install (Ptr node) const; + + /** + * Create a TcpEchoServerApplication on specified node + * + * \param nodeName The node on which to create the application. The node + * is specified by a node name previously registered with + * the Object Name Service. + * + * \returns An ApplicationContainer holding the Application created. + */ + ApplicationContainer Install (std::string nodeName) const; + + /** + * \param c The nodes on which to create the Applications. The nodes + * are specified by a NodeContainer. + * + * Create one TCP echo server application on each of the Nodes in the + * NodeContainer. + * + * \returns The applications created, one Application per Node in the + * NodeContainer. + */ + ApplicationContainer Install (NodeContainer c) const; + +private: + /** + * \internal + */ + Ptr InstallPriv (Ptr node) const; + + ObjectFactory m_factory; +}; + +/** + * \brief create an application which sends a TCP packet and waits for an echo of this packet + */ +class TcpEchoClientHelper +{ +public: + /** + * Create TcpEchoClientHelper which will make life easier for people trying + * to set up simulations with TCP echos. + * + * \param ip The IP address of the remote TCP echo server + * \param port The port number of the remote TCP echo server + */ + TcpEchoClientHelper (Address ip, uint16_t port); + + /** + * Record an attribute to be set in each Application after it is created. + * + * \param name the name of the attribute to set + * \param value the value of the attribute to set + */ + void SetAttribute (std::string name, const AttributeValue &value); + + /** + * Given a pointer to a TcpEchoClient application, set the data fill of the TCP + * packet (what is sent as data to the server) to the contents of the fill + * string (including the trailing zero terminator). + * + * \warning The size of resulting echo packets will be automatically adjusted + * to reflect the size of the fill string -- this means that the PacketSize + * attribute may be changed as a result of this call. + * + * \param app Smart pointer to the application (real type must be TcpEchoClient). + * \param fill The string to use as the actual echo data bytes. + */ + void SetFill (Ptr app, std::string fill); + + /** + * Given a pointer to a TcpEchoClient application, set the data fill of the + * packet (what is sent as data to the server) to the contents of the fill + * byte. + * + * The fill byte will be used to initialize the contents of the data packet. + * + * \warning The size of resulting echo packets will be automatically adjusted + * to reflect the dataLength parameter -- this means that the PacketSize + * attribute may be changed as a result of this call. + * + * \param app Smart pointer to the application (real type must be TcpEchoClient). + * \param fill The byte to be repeated in constructing the packet data. + * \param dataLength The desired length of the resulting echo packet data. + */ + void SetFill (Ptr app, uint8_t fill, uint32_t dataLength); + + /** + * Given a pointer to a TcpEchoClient application, set the data fill of the + * packet (what is sent as data to the server) to the contents of the fill + * buffer, repeated as many times as is required. + * + * Initializing the fill to the contents of a single buffer is accomplished + * by providing a complete buffer with fillLength set to your desired + * dataLength + * + * \warning The size of resulting echo packets will be automatically adjusted + * to reflect the dataLength parameter -- this means that the PacketSize + * attribute of the Application may be changed as a result of this call. + * + * \param app Smart pointer to the application (real type must be TcpEchoClient). + * \param fill The fill pattern to use when constructing packets. + * \param fillLength The number of bytes in the provided fill pattern. + * \param dataLength The desired length of the final echo data. + */ + void SetFill (Ptr app, uint8_t *fill, uint32_t fillLength, uint32_t dataLength); + + /** + * Create a TCP echo client application on the specified node. The Node + * is provided as a Ptr. + * + * \param node The Ptr on which to create the TcpEchoClientApplication. + * + * \returns An ApplicationContainer that holds a Ptr to the + * application created + */ + ApplicationContainer Install (Ptr node) ; + + /** + * Create a TCP echo client application on the specified node. The Node + * is provided as a string name of a Node that has been previously + * associated using the Object Name Service. + * + * \param nodeName The name of the node on which to create the TcpEchoClientApplication + * + * \returns An ApplicationContainer that holds a Ptr to the + * application created + */ + ApplicationContainer Install (std::string nodeName) ; + + /** + * \param c the nodes + * + * Create one TCP echo client application on each of the input nodes + * + * \returns the applications created, one application per input node. + */ + ApplicationContainer Install (NodeContainer c); + Ptr GetClient (void); +private: + Ptr InstallPriv (Ptr node); + ObjectFactory m_factory; + Ptr m_client; +}; + +} // namespace ns3 + +#endif /* TCP_ECHO_HELPER */ --- a/src/applications/model/tcp-echo-client.cc +++ a/src/applications/model/tcp-echo-client.cc @@ -0,0 +1,354 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2012 + * + * 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 + * + * Authors: Frank Helbert , + * Luiz Arthur Feitosa dos Santos and + * Rodrigo Campiolo + */ + +#include "ns3/log.h" +#include "ns3/ipv4-address.h" +#include "ns3/ipv6-address.h" +#include "ns3/address-utils.h" +#include "ns3/nstime.h" +#include "ns3/inet-socket-address.h" +#include "ns3/inet6-socket-address.h" +#include "ns3/socket.h" +#include "ns3/simulator.h" +#include "ns3/socket-factory.h" +#include "ns3/packet.h" +#include "ns3/uinteger.h" +#include "ns3/trace-source-accessor.h" +#include "tcp-echo-client.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("TcpEchoClientApplication"); +NS_OBJECT_ENSURE_REGISTERED (TcpEchoClient); + +TypeId +TcpEchoClient::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpEchoClient") + .SetParent () + .AddConstructor () + .AddAttribute ("MaxPackets", + "The maximum number of packets the application will send", + UintegerValue (100), + MakeUintegerAccessor (&TcpEchoClient::m_count), + MakeUintegerChecker ()) + .AddAttribute ("Interval", + "The time to wait between packets", + TimeValue (Seconds (1.0)), + MakeTimeAccessor (&TcpEchoClient::m_interval), + MakeTimeChecker ()) + .AddAttribute ("RemoteAddress", + "The destination address of the outbound packets", + AddressValue (), + MakeAddressAccessor (&TcpEchoClient::m_peerAddress), + MakeAddressChecker ()) + .AddAttribute ("RemotePort", + "The destination port of the outbound packets", + UintegerValue (0), + MakeUintegerAccessor (&TcpEchoClient::m_peerPort), + MakeUintegerChecker ()) + .AddAttribute ("PacketSize", "Size of echo data in outbound packets", + UintegerValue (100), + MakeUintegerAccessor (&TcpEchoClient::SetDataSize, + &TcpEchoClient::GetDataSize), + MakeUintegerChecker ()) + .AddTraceSource ("Tx", "A new packet is created and is sent", + MakeTraceSourceAccessor (&TcpEchoClient::m_txTrace)) + ; + return tid; +} + +TcpEchoClient::TcpEchoClient () +{ + NS_LOG_FUNCTION_NOARGS (); + m_sent = 0; + m_bytesSent = 0; + m_recvBack = 0; + m_bytesRecvBack = 0; + m_socket = 0; + m_sendEvent = EventId (); + m_data = 0; + m_dataSize = 0; +} + +TcpEchoClient::~TcpEchoClient() +{ + NS_LOG_FUNCTION_NOARGS (); + m_socket = 0; + + delete [] m_data; + m_data = 0; + m_dataSize = 0; +} + +void +TcpEchoClient::SetRemote (Address ip, uint16_t port) +{ + m_peerAddress = ip; + m_peerPort = port; +} + +void +TcpEchoClient::DoDispose (void) +{ + NS_LOG_FUNCTION_NOARGS (); + Application::DoDispose (); +} + +void +TcpEchoClient::StartApplication (void) +{ + NS_LOG_FUNCTION_NOARGS (); + + if (m_socket == 0) + { + TypeId tid = TypeId::LookupByName ("ns3::TcpSocketFactory"); + m_socket = Socket::CreateSocket (GetNode (), tid); + if (Ipv4Address::IsMatchingType(m_peerAddress) == true) + { + m_socket->Bind(); + m_socket->Connect (InetSocketAddress (Ipv4Address::ConvertFrom(m_peerAddress), m_peerPort)); + } + else if (Ipv6Address::IsMatchingType(m_peerAddress) == true) + { + m_socket->Bind6(); + m_socket->Connect (Inet6SocketAddress (Ipv6Address::ConvertFrom(m_peerAddress), m_peerPort)); + } + } + + m_socket->SetRecvCallback (MakeCallback (&TcpEchoClient::ReceivePacket, this)); + + ScheduleTransmit (Seconds (0.)); +} + +void +TcpEchoClient::StopApplication () +{ + NS_LOG_FUNCTION_NOARGS (); + + if (m_socket != 0) + { + m_socket->Close (); + m_socket->SetRecvCallback (MakeNullCallback > ()); + m_socket = 0; + } + + Simulator::Cancel (m_sendEvent); +} + +void +TcpEchoClient::SetDataSize (uint32_t dataSize) +{ + NS_LOG_FUNCTION (dataSize); + + // + // If the client is setting the echo packet data size this way, we infer + // that she doesn't care about the contents of the packet at all, so + // neither will we. + // + delete [] m_data; + m_data = 0; + m_dataSize = 0; + m_size = dataSize; +} + +uint32_t +TcpEchoClient::GetDataSize (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_size; +} + +void +TcpEchoClient::SetFill (std::string fill) +{ + NS_LOG_FUNCTION (fill); + + uint32_t dataSize = fill.size () + 1; + + if (dataSize != m_dataSize) + { + delete [] m_data; + m_data = new uint8_t [dataSize]; + m_dataSize = dataSize; + } + + memcpy (m_data, fill.c_str (), dataSize); + + // + // Overwrite packet size attribute. + // + m_size = dataSize; +} + +void +TcpEchoClient::SetFill (uint8_t fill, uint32_t dataSize) +{ + if (dataSize != m_dataSize) + { + delete [] m_data; + m_data = new uint8_t [dataSize]; + m_dataSize = dataSize; + } + + memset (m_data, fill, dataSize); + + // + // Overwrite packet size attribute. + // + m_size = dataSize; +} + +void +TcpEchoClient::SetFill (uint8_t *fill, uint32_t fillSize, uint32_t dataSize) +{ + if (dataSize != m_dataSize) + { + delete [] m_data; + m_data = new uint8_t [dataSize]; + m_dataSize = dataSize; + } + + if (fillSize >= dataSize) + { + memcpy (m_data, fill, dataSize); + return; + } + + // + // Do all but the final fill. + // + uint32_t filled = 0; + while (filled + fillSize < dataSize) + { + memcpy (&m_data[filled], fill, fillSize); + filled += fillSize; + } + + // + // Last fill may be partial + // + memcpy (&m_data[filled], fill, dataSize - filled); + + // + // Overwrite packet size attribute. + // + m_size = dataSize; +} + +void +TcpEchoClient::ScheduleTransmit (Time dt) +{ + NS_LOG_FUNCTION_NOARGS (); + m_sendEvent = Simulator::Schedule (dt, &TcpEchoClient::Send, this); +} + +void +TcpEchoClient::Send (void) +{ + NS_LOG_FUNCTION_NOARGS (); + + NS_ASSERT (m_sendEvent.IsExpired ()); + + Ptr p; + if (m_dataSize) + { + // + // If m_dataSize is non-zero, we have a data buffer of the same size that we + // are expected to copy and send. This state of affairs is created if one of + // the Fill functions is called. In this case, m_size must have been set + // to agree with m_dataSize + // + NS_ASSERT_MSG (m_dataSize == m_size, "TcpEchoClient::Send(): m_size and m_dataSize inconsistent"); + NS_ASSERT_MSG (m_data, "TcpEchoClient::Send(): m_dataSize but no m_data"); + p = Create (m_data, m_dataSize); + m_bytesSent += m_dataSize; + } + else + { + // + // If m_dataSize is zero, the client has indicated that she doesn't care + // about the data itself either by specifying the data size by setting + // the corresponding atribute or by not calling a SetFill function. In + // this case, we don't worry about it either. But we do allow m_size + // to have a value different from the (zero) m_dataSize. + // + p = Create (m_size); + m_bytesSent += m_size; + } + // call to the trace sinks before the packet is actually sent, + // so that tags added to the packet can be sent as well + m_txTrace (p); + m_socket->Send (p); + + ++m_sent; + + if (InetSocketAddress::IsMatchingType (m_peerAddress)) + { + NS_LOG_INFO ("Sent " << m_size << " bytes to " << + InetSocketAddress::ConvertFrom (m_peerAddress)); + } + else if (Inet6SocketAddress::IsMatchingType (m_peerAddress)) + { + NS_LOG_INFO ("Sent " << m_size << " bytes to " << + Inet6SocketAddress::ConvertFrom (m_peerAddress)); + } + + if (m_sent < m_count) + { + ScheduleTransmit (m_interval); + } +} + +void +TcpEchoClient::ReceivePacket (Ptr socket) +{ + NS_LOG_FUNCTION (this << socket); + Ptr packet; + Address from; + while ((packet = socket->RecvFrom (from))) + { + if (InetSocketAddress::IsMatchingType (from)) + { + NS_LOG_INFO ("Received " << packet->GetSize () << " bytes from " << + InetSocketAddress::ConvertFrom (from).GetIpv4 ()); + } + else if (Inet6SocketAddress::IsMatchingType (from)) + { + NS_LOG_INFO ("Received " << packet->GetSize () << " bytes from " << + Inet6SocketAddress::ConvertFrom (from).GetIpv6 ()); + } + + // don't check if data returned is the same data sent earlier + m_recvBack++; + m_bytesRecvBack += packet->GetSize (); + } + + if (m_count == m_recvBack) + { + socket->Close(); + m_socket->SetRecvCallback (MakeNullCallback > ()); + socket = 0; + } +} + +} // Namespace ns3 --- a/src/applications/model/tcp-echo-client.h +++ a/src/applications/model/tcp-echo-client.h @@ -0,0 +1,170 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2012 + * + * 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 + * + * Authors: Frank Helbert , + * Luiz Arthur Feitosa dos Santos and + * Rodrigo Campiolo + */ + +#ifndef TCP_ECHO_CLIENT +#define TCP_ECHO_CLIENT + + +#include "ns3/application.h" +#include "ns3/event-id.h" +#include "ns3/ptr.h" +#include "ns3/ipv4-address.h" +#include "ns3/ipv6-address.h" +#include "ns3/traced-callback.h" + +namespace ns3 { + +class Socket; +class Packet; + +/** + * \ingroup tcpecho + * \brief A Tcp Echo client + * + * Every packet sent should be returned by the server and received here. + */ +class TcpEchoClient : public Application +{ +public: + static TypeId GetTypeId (void); + + TcpEchoClient (); + + virtual ~TcpEchoClient (); + + /** + * \param ip destination address + * \param port destination port + */ + void SetRemote (Address ip, uint16_t port); + + /** + * Set the data size of the packet (the number of bytes that are sent as data + * to the server). The contents of the data are set to unspecified (don't + * care) by this call. + * + * \warning If you have set the fill data for the echo client using one of the + * SetFill calls, this will undo those effects. + * + * \param dataSize The size of the echo data you want to sent. + */ + void SetDataSize (uint32_t dataSize); + + /** + * Get the number of data bytes that will be sent to the server. + * + * \warning The number of bytes may be modified by calling any one of the + * SetFill methods. If you have called SetFill, then the number of + * data bytes will correspond to the size of an initialized data buffer. + * If you have not called a SetFill method, the number of data bytes will + * correspond to the number of don't care bytes that will be sent. + * + * \returns The number of data bytes. + */ + uint32_t GetDataSize (void) const; + + /** + * Set the data fill of the packet (what is sent as data to the server) to + * the zero-terminated contents of the fill string string. + * + * \warning The size of resulting echo packets will be automatically adjusted + * to reflect the size of the fill string -- this means that the PacketSize + * attribute may be changed as a result of this call. + * + * \param fill The string to use as the actual echo data bytes. + */ + void SetFill (std::string fill); + + /** + * Set the data fill of the packet (what is sent as data to the server) to + * the repeated contents of the fill byte. i.e., the fill byte will be + * used to initialize the contents of the data packet. + * + * \warning The size of resulting echo packets will be automatically adjusted + * to reflect the dataSize parameter -- this means that the PacketSize + * attribute may be changed as a result of this call. + * + * \param fill The byte to be repeated in constructing the packet data.. + * \param dataSize The desired size of the resulting echo packet data. + */ + void SetFill (uint8_t fill, uint32_t dataSize); + + /** + * Set the data fill of the packet (what is sent as data to the server) to + * the contents of the fill buffer, repeated as many times as is required. + * + * Initializing the packet to the contents of a provided single buffer is + * accomplished by setting the fillSize set to your desired dataSize + * (and providing an appropriate buffer). + * + * \warning The size of resulting echo packets will be automatically adjusted + * to reflect the dataSize parameter -- this means that the PacketSize + * attribute of the Application may be changed as a result of this call. + * + * \param fill The fill pattern to use when constructing packets. + * \param fillSize The number of bytes in the provided fill pattern. + * \param dataSize The desired size of the final echo data. + */ + void SetFill (uint8_t *fill, uint32_t fillSize, uint32_t dataSize); + + // mainly to the automated test + uint32_t GetPacketsSent (void) { return m_sent; }; + uint64_t GetBytesSent (void) { return m_bytesSent; }; + uint32_t GetPacketsReceivedBack (void) { return m_recvBack; }; + uint64_t GetBytesReceivedBack (void) { return m_bytesRecvBack; }; + +protected: + virtual void DoDispose (void); + +private: + + virtual void StartApplication (void); + virtual void StopApplication (void); + + void ScheduleTransmit (Time dt); + void Send (void); + + void ReceivePacket (Ptr socket); + + uint32_t m_count; + Time m_interval; + uint32_t m_size; + + uint32_t m_dataSize; + uint8_t *m_data; + + uint32_t m_sent; + uint64_t m_bytesSent; + uint32_t m_recvBack; + uint64_t m_bytesRecvBack; + Ptr m_socket; + Address m_peerAddress; + uint16_t m_peerPort; + EventId m_sendEvent; + /// Callbacks for tracing the packet Tx events + TracedCallback > m_txTrace; +}; + +} // namespace ns3 + + +#endif /* TCP_ECHO_CLIENT */ --- a/src/applications/model/tcp-echo-server.cc +++ a/src/applications/model/tcp-echo-server.cc @@ -0,0 +1,183 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2012 + * + * 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 + * + * Authors: Frank Helbert , + * Luiz Arthur Feitosa dos Santos and + * Rodrigo Campiolo + */ + +#include "ns3/log.h" +#include "ns3/ipv4-address.h" +#include "ns3/ipv6-address.h" +#include "ns3/address-utils.h" +#include "ns3/nstime.h" +#include "ns3/inet-socket-address.h" +#include "ns3/inet6-socket-address.h" +#include "ns3/socket.h" +#include "ns3/simulator.h" +#include "ns3/socket-factory.h" +#include "ns3/packet.h" +#include "ns3/uinteger.h" + +#include "tcp-echo-server.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("TcpEchoServerApplication"); +NS_OBJECT_ENSURE_REGISTERED (TcpEchoServer); + +TypeId +TcpEchoServer::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpEchoServer") + .SetParent () + .AddConstructor () + .AddAttribute ("Port", "Port on which we listen for incoming connections.", + UintegerValue (7), + MakeUintegerAccessor (&TcpEchoServer::m_port), + MakeUintegerChecker ()) + ; + return tid; +} + +TcpEchoServer::TcpEchoServer () +{ + NS_LOG_FUNCTION_NOARGS (); + m_socket = 0; + m_socket6 = 0; + m_running = false; +} + +TcpEchoServer::~TcpEchoServer() +{ + NS_LOG_FUNCTION_NOARGS (); + m_socket = 0; + m_socket6 = 0; +} + +void +TcpEchoServer::DoDispose (void) +{ + NS_LOG_FUNCTION_NOARGS (); + Application::DoDispose (); +} + +void +TcpEchoServer::StartApplication (void) +{ + NS_LOG_FUNCTION_NOARGS (); + + m_running = true; + + if (m_socket == 0) + { + TypeId tid = TypeId::LookupByName ("ns3::TcpSocketFactory"); + m_socket = Socket::CreateSocket (GetNode (), tid); + InetSocketAddress listenAddress = InetSocketAddress (Ipv4Address::GetAny (), m_port); + m_socket->Bind (listenAddress); + m_socket->Listen(); + } + if (m_socket6 == 0) + { + TypeId tid = TypeId::LookupByName ("ns3::TcpSocketFactory"); + m_socket6 = Socket::CreateSocket (GetNode (), tid); + Inet6SocketAddress listenAddress = Inet6SocketAddress (Ipv6Address::GetAny (), m_port); + m_socket6->Bind (listenAddress); + m_socket6->Listen(); + } + + m_socket->SetAcceptCallback ( + MakeNullCallback, const Address &> (), + MakeCallback (&TcpEchoServer::HandleAccept, this)); + m_socket6->SetAcceptCallback ( + MakeNullCallback, const Address &> (), + MakeCallback (&TcpEchoServer::HandleAccept, this)); +} + +void +TcpEchoServer::StopApplication () +{ + NS_LOG_FUNCTION_NOARGS (); + + m_running = false; + + if (m_socket != 0) + { + m_socket->Close (); + m_socket->SetAcceptCallback ( + MakeNullCallback, const Address &> (), + MakeNullCallback, const Address &> () ); + } + if (m_socket6 != 0) + { + m_socket6->Close (); + m_socket6->SetAcceptCallback ( + MakeNullCallback, const Address &> (), + MakeNullCallback, const Address &> () ); + } +} + +void +TcpEchoServer::ReceivePacket (Ptr s) +{ + NS_LOG_FUNCTION (this << s); + + Ptr packet; + Address from; + while ((packet = s->RecvFrom (from))) + { + if (packet->GetSize () > 0) + { + if (InetSocketAddress::IsMatchingType (from)) + { + NS_LOG_INFO ("Server Received " << packet->GetSize () << " bytes from " << + InetSocketAddress::ConvertFrom (from).GetIpv4 ()); + } + else if (Inet6SocketAddress::IsMatchingType (from)) + { + NS_LOG_INFO ("Server Received " << packet->GetSize () << " bytes from " << + Inet6SocketAddress::ConvertFrom (from).GetIpv6 ()); + } + + packet->RemoveAllPacketTags (); + packet->RemoveAllByteTags (); + + NS_LOG_LOGIC ("Echoing packet"); + s->Send (packet); + } + } +} + +void TcpEchoServer::HandleAccept (Ptr s, const Address& from) +{ + NS_LOG_FUNCTION (this << s << from); + s->SetRecvCallback (MakeCallback (&TcpEchoServer::ReceivePacket, this)); + s->SetCloseCallbacks(MakeCallback (&TcpEchoServer::HandleSuccessClose, this), + MakeNullCallback > () ); +} + +void TcpEchoServer::HandleSuccessClose(Ptr s) +{ + NS_LOG_FUNCTION (this << s); + NS_LOG_LOGIC ("Client close received"); + s->Close(); + s->SetRecvCallback (MakeNullCallback > () ); + s->SetCloseCallbacks(MakeNullCallback > (), + MakeNullCallback > () ); +} + +} // Namespace ns3 --- a/src/applications/model/tcp-echo-server.h +++ a/src/applications/model/tcp-echo-server.h @@ -0,0 +1,101 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2012 + * + * 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 + * + * Authors: Frank Helbert , + * Luiz Arthur Feitosa dos Santos and + * Rodrigo Campiolo + */ + +#ifndef TCP_ECHO_SERVER +#define TCP_ECHO_SERVER + +#include "ns3/application.h" +#include "ns3/event-id.h" +#include "ns3/ptr.h" +#include "ns3/address.h" + +namespace ns3 { + +class Socket; +class Packet; + +/** + * \ingroup applications + * \defgroup TcpEcho + */ + +/** + * \ingroup tcpecho + * \brief A Tcp Echo server + * + * Every packet received is sent back to the client. + */ +class TcpEchoServer : public Application +{ +public: + static TypeId GetTypeId (void); + TcpEchoServer (); + virtual ~TcpEchoServer (); + + /** + * + * Receive the packet from client echo on socket level (layer 4), + * handle the packet and return to the client. + * + * \param socket TCP socket. + * + */ + void ReceivePacket(Ptr socket); + + /** + * + * Handle packet from accept connections. + * + * \parm s TCP socket. + * \parm from Address from client echo. + */ + void HandleAccept (Ptr s, const Address& from); + + /** + * + * Handle successful closing connections. + * + * \parm s TCP socket. + * + */ + void HandleSuccessClose(Ptr s); + +protected: + virtual void DoDispose (void); + +private: + + virtual void StartApplication (void); + virtual void StopApplication (void); + + void HandleRead (Ptr socket); + + Ptr m_socket; + Ptr m_socket6; + uint16_t m_port; + bool m_running; +}; + +} // namespace ns3 + +#endif /* TCP_ECHO_SERVER */ + --- a/src/applications/test/tcp-echo-test-suite.cc +++ a/src/applications/test/tcp-echo-test-suite.cc @@ -0,0 +1,204 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2012 + * + * 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 + * + * Authors: Frank Helbert , + * Luiz Arthur Feitosa dos Santos and + * Rodrigo Campiolo + */ + +#include +#include "ns3/log.h" +#include "ns3/abort.h" +#include "ns3/config.h" +#include "ns3/string.h" +#include "ns3/uinteger.h" +#include "ns3/address.h" +#include "ns3/inet-socket-address.h" +#include "ns3/inet6-socket-address.h" +#include "ns3/internet-stack-helper.h" +#include "ns3/ipv4-address-helper.h" +#include "ns3/ipv6-address-helper.h" +#include "ns3/tcp-echo-server.h" +#include "ns3/tcp-echo-client.h" +#include "ns3/tcp-echo-helper.h" +#include "ns3/simple-net-device.h" +#include "ns3/simple-channel.h" +#include "ns3/test.h" +#include "ns3/simulator.h" + +using namespace ns3; + +/** + * Test if all tcp packets generated by a tcpEchoClient application are + * correctly received by an tcpEchoServer application. + * + */ +class TcpEchoTestCase : public TestCase +{ +public: + TcpEchoTestCase (); + virtual ~TcpEchoTestCase (); + +private: + virtual void DoRun (void); +}; + +TcpEchoTestCase::TcpEchoTestCase () + : TestCase ("Tcp-echo test if data sent by an client returns back to server") +{ +} + +TcpEchoTestCase::~TcpEchoTestCase () +{ +} + +void +TcpEchoTestCase::DoRun (void) +{ + NodeContainer n; + n.Create (2); + + InternetStackHelper internet; + internet.Install (n); + + // link the two nodes + Ptr txDev = CreateObject (); + Ptr rxDev = CreateObject (); + n.Get (0)->AddDevice (txDev); + n.Get (1)->AddDevice (rxDev); + Ptr channel1 = CreateObject (); + rxDev->SetChannel (channel1); + txDev->SetChannel (channel1); + NetDeviceContainer d; + d.Add (txDev); + d.Add (rxDev); + + Ipv4AddressHelper ipv4; + + ipv4.SetBase ("10.1.1.0", "255.255.255.0"); + Ipv4InterfaceContainer i = ipv4.Assign (d); + + uint16_t port = 4000; + TcpEchoServerHelper server (port); + ApplicationContainer apps = server.Install (n.Get (1)); + apps.Start (Seconds (1.0)); + apps.Stop (Seconds (10.0)); + + uint32_t MaxPacketSize = 183; + Time interPacketInterval = Seconds (1.); + uint32_t maxPacketCount = 5; + TcpEchoClientHelper client (i.GetAddress (1), port); + client.SetAttribute ("MaxPackets", UintegerValue (maxPacketCount)); + client.SetAttribute ("Interval", TimeValue (interPacketInterval)); + client.SetAttribute ("PacketSize", UintegerValue (MaxPacketSize)); + apps = client.Install (n.Get (0)); + apps.Start (Seconds (2.0)); + apps.Stop (Seconds (10.0)); + + Simulator::Run (); + Simulator::Destroy (); + + NS_TEST_ASSERT_MSG_EQ (client.GetClient ()->GetBytesSent (), client.GetClient ()->GetBytesReceivedBack (), "Bytes received differ from bytes sent !"); +} + +/** + * Test if all tcp packets generated by a tcpEchoClient application are + * correctly received by an tcpEchoServer apllication - IPv6 version. + * + */ +class TcpEchoTestCase6 : public TestCase +{ +public: + TcpEchoTestCase6 (); + virtual ~TcpEchoTestCase6 (); + +private: + virtual void DoRun (void); +}; + +TcpEchoTestCase6::TcpEchoTestCase6 () + : TestCase ("Tcp-echo test if data sent by an client returns back to server - IPv6") +{ +} + +TcpEchoTestCase6::~TcpEchoTestCase6 () +{ +} + +void +TcpEchoTestCase6::DoRun (void) +{ + NodeContainer n; + n.Create (2); + + InternetStackHelper internet; + internet.Install (n); + + // link the two nodes + Ptr txDev = CreateObject (); + Ptr rxDev = CreateObject (); + n.Get (0)->AddDevice (txDev); + n.Get (1)->AddDevice (rxDev); + Ptr channel1 = CreateObject (); + rxDev->SetChannel (channel1); + txDev->SetChannel (channel1); + NetDeviceContainer d; + d.Add (txDev); + d.Add (rxDev); + + Ipv6AddressHelper ipv6; + ipv6.NewNetwork ("2001:0000:f00d:cafe::", 64); + Ipv6InterfaceContainer i6 = ipv6.Assign (d); + + uint16_t port = 4000; + TcpEchoServerHelper server (port); + ApplicationContainer apps = server.Install (n.Get (1)); + apps.Start (Seconds (1.0)); + apps.Stop (Seconds (11.0)); + + uint32_t MaxPacketSize = 183; + Time interPacketInterval = Seconds (1.); + uint32_t maxPacketCount = 5; + TcpEchoClientHelper client (i6.GetAddress (1,1), port); + client.SetAttribute ("MaxPackets", UintegerValue (maxPacketCount)); + client.SetAttribute ("Interval", TimeValue (interPacketInterval)); + client.SetAttribute ("PacketSize", UintegerValue (MaxPacketSize)); + apps = client.Install (n.Get (0)); + apps.Start (Seconds (2.0)); + apps.Stop (Seconds (10.0)); + + Simulator::Run (); + Simulator::Destroy (); + + NS_TEST_ASSERT_MSG_EQ (client.GetClient ()->GetBytesSent (), client.GetClient ()->GetBytesReceivedBack (), "Bytes received differ from bytes sent !"); +} + +class TcpEchoTestSuite : public TestSuite +{ +public: + TcpEchoTestSuite (); +}; + +TcpEchoTestSuite::TcpEchoTestSuite () + : TestSuite ("tcp-echo", UNIT) +{ + AddTestCase (new TcpEchoTestCase); + AddTestCase (new TcpEchoTestCase6); +} + +static TcpEchoTestSuite tcpEchoTestSuite; + --- a/src/applications/wscript +++ a/src/applications/wscript @@ -18,6 +18,8 @@ 'model/udp-echo-client.cc', 'model/udp-echo-server.cc', 'model/v4ping.cc', + 'model/tcp-echo-client.cc', + 'model/tcp-echo-server.cc', 'helper/bulk-send-helper.cc', 'helper/on-off-helper.cc', 'helper/packet-sink-helper.cc', @@ -25,11 +27,13 @@ 'helper/udp-client-server-helper.cc', 'helper/udp-echo-helper.cc', 'helper/v4ping-helper.cc', + 'helper/tcp-echo-helper.cc', ] applications_test = bld.create_ns3_module_test_library('applications') applications_test.source = [ 'test/udp-client-server-test.cc', + 'test/tcp-echo-test-suite.cc', ] headers = bld.new_task_gen(features=['ns3header']) @@ -50,6 +54,8 @@ 'model/udp-echo-client.h', 'model/udp-echo-server.h', 'model/v4ping.h', + 'model/tcp-echo-client.h', + 'model/tcp-echo-server.h', 'helper/bulk-send-helper.h', 'helper/on-off-helper.h', 'helper/packet-sink-helper.h', @@ -57,6 +63,7 @@ 'helper/udp-client-server-helper.h', 'helper/udp-echo-helper.h', 'helper/v4ping-helper.h', + 'helper/tcp-echo-helper.h', ] bld.ns3_python_bindings()