node 0 node 1
+----------------+ +----------------+
| ns-3 TCP | | ns-3 TCP |
+----------------+ +----------------+
| 10.1.1.1 | | 10.1.1.2 |
+----------------+ +----------------+
| point-to-point | | point-to-point |
+----------------+ +----------------+
| |
+---------------------+
5 Mbps, 2 ms
我们想要查看ns3 TCP的拥塞控制窗口的变化,我们需要加快一个流并且hook发送包的套接字的拥塞控制窗口的属性(Hook是Windows中提供的一种用以替换DOS下“中断”的系统机制,一旦hook某个事件之后,当该事件发生时,对该事件进行hook的程序就会收到系统的通知,这时程序就能在第一时间对该事件做出响应)。 通常会使用一个开关的应用程序去生成流,但是这样会导致问题 ,首先,这个开关应用程序的套接字编程直到应用程序启动才开始创建, 所以我们将不能hool配置时间内的socket;其次,即使我们在开始之后才安排一个包,因为socket并不公开所以我们并不能得到它。
class MyApp : public Application
{
public:
MyApp ();
virtual ~MyApp();
void Setup (Ptr<Socket> socket, Address address, uint32_t packetSize, uint32_t nPackets, DataRate dataRate);
private:
virtual void StartApplication (void);
virtual void StopApplication (void);
void ScheduleTx (void);
void SendPacket (void);
Ptr<Socket> m_socket;
Address m_peer;
uint32_t m_packetSize;
uint32_t m_nPackets;
DataRate m_dataRate;
EventId m_sendEvent;
bool m_running;
uint32_t m_packetsSent;
};
MyApp::MyApp ()
: m_socket (0),
m_peer (),
m_packetSize (0),
m_nPackets (0),
m_dataRate (0),
m_sendEvent (),
m_running (false),
m_packetsSent (0)
{
}
MyApp::~MyApp()
{
m_socket = 0;
}
void
MyApp::Setup (Ptr<Socket> socket, Address address, uint32_t packetSize, uint32_t nPackets, DataRate dataRate)
{
m_socket = socket;
m_peer = address;
m_packetSize = packetSize;
m_nPackets = nPackets;
m_dataRate = dataRate;
}
void
MyApp::StartApplication (void)
{
m_running = true;
m_packetsSent = 0;
m_socket->Bind ();
m_socket->Connect (m_peer);
SendPacket ();
}
//析构函数:检查当前是否有发包行为,如果有,则取消发包;随后关闭套接字链接
void
MyApp::StopApplication (void)
{
m_running = false;
if (m_sendEvent.IsRunning ())
{
Simulator::Cancel (m_sendEvent);
}
if (m_socket)
{
m_socket->Close ();
}
}
void
MyApp::SendPacket (void)
{
Ptr<Packet> packet = Create<Packet> (m_packetSize);
m_socket->Send (packet);
if (++m_packetsSent < m_nPackets)
{
ScheduleTx ();
}
}
void
MyApp::ScheduleTx (void)
{
//计算一个包传输的时间t,当时间t之后,发送一个包
if (m_running)
{
Time tNext (Seconds (m_packetSize * 8 / static_cast<double> (m_dataRate.GetBitRate ())));
m_sendEvent = Simulator::Schedule (tNext, &MyApp::SendPacket, this);
}
}
//当拥塞控制窗口大小发生变化的时候 日志打印旧的窗口
static void
CwndChange (uint32_t oldCwnd, uint32_t newCwnd)
{
NS_LOG_UNCOND (Simulator::Now ().GetSeconds () << "\t" << newCwnd);
}
static void
RxDrop (Ptr<const Packet> p)
{
NS_LOG_UNCOND ("RxDrop at " << Simulator::Now ().GetSeconds ());
}
uint16_t sinkPort = 8080;
Address sinkAddress (InetSocketAddress (interfaces.GetAddress (1), sinkPort));
PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), sinkPort));
ApplicationContainer sinkApps = packetSinkHelper.Install (nodes.Get (1));
sinkApps.Start (Seconds (0.));
sinkApps.Stop (Seconds (20.));
将node1视为接收节点,为它绑定一个端口号8080,packetSink是一个对于on-off applocation的补充类,它可以接收和消耗到某一个地址和端口的流量。
PacketSinkHelper::PacketSinkHelper ("ns3::TcpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), sinkPort)) 第一个参数选定用于创建套接字的factory类型,绑定接收端的IP地址和端口
其中,Ipv4Address::GetAny ()返回主机的所有IP地址。
- GetAny()返回0.0.0.0 最特殊的一个ip地址,代表的是本机所有ip地址,不管你有多少个网口,多少个ip,如果监听本机的0.0.0.0上的端口,就等于监听机器上的所有ip端口。
- 127.0.0.1代表本地回环地址
- 主机的IP地址 是公网ip地址,这些是在网络中代表本机的ip地址,可通过此ip地址远程访问或控制主机
将PacketSinkApplication安装到 node 1
Ptr<MyApp> app = CreateObject<MyApp> ();
app->Setup (ns3TcpSocket, sinkAddress, 1040, 1000, DataRate ("1Mbps"));
nodes.Get (0)->AddApplication (app);
app->SetStartTime (Seconds (1.));
app->SetStopTime (Seconds (20.));
将node 0视为发送端,将MyApp安装到node0上
Ptr<Socket> ns3TcpSocket = Socket::CreateSocket (nodes.Get (0), TcpSocketFactory::GetTypeId ());
ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndChange));
devices.Get (1)->TraceConnectWithoutContext ("PhyRxDrop", MakeCallback (&RxDrop));