Fourth.cc 예제 파일 설명

Fourth example scenario

네번째 시나리오는 지금까지의 시나리오와는 다르게, 토폴로지를 만들고 시뮬레이션을 진행하는 것이 아니라 시뮬레이션을 돌리며 로그를 찍는 방법을 소개하고 있습니다. 시뮬레이션을 돌리다 보면 TCP throughput이나 error rate, SNR 등 다양한 시뮬레이션 결과를 알고싶을 때가 있는 데 그때 지금 소개하는 방법을 이용하여 로그로 남길 수 있습니다. 네트워크 토폴로지가 없으므로 바로 진행해보도록 하겠습니다.

###Source Code

/* -*- 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
 */

/*
시나리오에 필요한 헤더들을 포함시킨다.
포함되는 헤더들은 ns-3의 src 폴더 포함되어 있다.
*/
#include "ns3/object.h"
#include "ns3/uinteger.h"
#include "ns3/traced-value.h"
#include "ns3/trace-source-accessor.h"

#include <iostream>

using namespace ns3;

/*
객체를 1개 만들고 객체에 TraceSource 를 추가한다.
TraceSource는 객체의 로그를 작성하기 위해 사용한다.
TraceSource에 이름을 지정해주고, 어떤 property에 대해 Trace 할것인지 (로그를 남길 것인지 설정해준다.)
만약 2개 이상의 property에 대해 Trace를 하고 싶다면 TraceSource를 서로 다른 이름으로 2개 만들어 준다.
 */
class MyObject : public Object
{
public:
  /**
   * Register this type.
   * \return The TypeId.
   * 사용자가 직접 정의한 MyObject라는 객체에 TypeId를 설정한다.
   */
  static TypeId GetTypeId (void)
  {
    static TypeId tid = TypeId ("MyObject")
      .SetParent<Object> ()
      .SetGroupName ("Tutorial")
      .AddConstructor<MyObject> ()
      .AddTraceSource ("MyInteger",
                       "An integer value to trace.",
                       MakeTraceSourceAccessor (&MyObject::m_myInt),
                       "ns3::TracedValueCallback::Int32")
    ;
    return tid;
  }

  MyObject () {} // 사용자가 정의한 객체의 생성자이다.
  TracedValue<int32_t> m_myInt; // 이번에 MyInteger라는 TraceSource를 이용하여 Trace할 property 이다.
};

/*
Trace가 되고 난뒤 호출될 CallBack 함수이다.
이전 값과 변경된 값을 출력해준다.
*/
void
IntTrace (int32_t oldValue, int32_t newValue)
{
  std::cout << "Traced " << oldValue << " to " << newValue << std::endl;
}

int
main (int argc, char *argv[])
{
  Ptr<MyObject> myObject = CreateObject<MyObject> (); // 사용자가 정의해 놓은 MyObject 객체를 생성한다.
  myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback (&IntTrace)); // MyObject 객체의 instance에 MyInteger라는 TraceSource에 Tracing을 할 것이라고 설정한다. 이때 IntTrace라는 함수를 CallBack 함수로 사용해서 Tracing을 진행한다.

  myObject->m_myInt = 1234; // MyInteger로 Tracing 할 수 있는 m_myInt 값을 변경시켜보고 실행 결과를 살펴 본다.
}

실행결과

Traced 0 to 1234

실행결과는 간단하게 처음 m_myInt 값과 변경된 m_myInt 값을 IntTrace라는 CallBack 함수를 이용하여 출력해줍니다. 값이 변경됐을 때 CallBack 함수를 호출하기 위해 MyInteger 라는 Tracing Source를 사용했습니다. 이번에는 main 함수에 m_myInt 값을 1번만 바꾸는 것이 아니라 3번 (0 -> 1234, 1234 -> 2018, 2018 -> 2019) 바꿔 보도록 하겠습니다.

위의 내용으로 변경하기 위해서 main 함수에 myObject->m_myInt = 1234를 아래와 같이 바꿔주시면 됩니다.

myObject->m_myInt = 1234;
myObject->m_myInt = 2018;
myObject->m_myInt = 2019;

위와 같이 바꿔주시고 시나리오를 실행시켜 주시면 다음과 같은 결과를 볼수 있습니다.

Traced 0 to 1234
Traced 1234 to 2018
Traced 2018 to 2019

이번에는 m_myInt를 3번 변경시켰기 때문에 CallBack method가 3번 호출되었다는 것을 알 수 있습니다. 결과를 보면 Tracing하고 있는 property가 변경될 때마다 CallBack 함수가 호출된다는 것을 알 수 있습니다. 이런 Tracing의 기능을 이용하면 시뮬레이션이 진행됨에 따라 계속적으로 변화하는 throughput, error rate, SNR의 로그를 남기는데 사용할 수 있습니다. 지금은 CallBack method로 변화하는 property를 출력하도록 했지만 property를 log 파일로 남기면 손쉽게 로그를 남길 수 있습니다. 이러한 이유 때문에 ns-3에서 Tracing은 로그를 남기기 위한 목적으로 많이 사용됩니다.

그렇다면 어떻게 해서 Tracing을 이용해 로그를 남길 수 있는지 살펴보도록 하겠습니다. 먼저 fourth.cc 에서 Tracing을 하기 위해 어떤 과정을 거쳤는지 정리해보도록 하겠습니다.

  1. 객체의 getTypeId 함수에 AddTraceSource로 TraceSource를 추가한다.
  2. Tracing 할 때 사용할 CallBack 함수를 정의한다.
  3. main 함수에서 MyObject 객체의 instance에 MyInteger라는 Trace Source에 앞서 정의한 CallBack 함수를 이용하여 Tracing을 설정한다.

위의 3가지 과정중 1번째는 이미 ns-3에서 작성해 놓았습니다. 따라서 ns-3에서 미리 정의된 객체는 몇가지 TracingSource를 포함하고 있습니다. 미리 정의된 TracingSource는 여기에서 확인할 수 있습니다. 미리 정의된 TracingSource를 이용하여 첫번째 예제 시나리오였던 first.cc에 Tracing을 추가해보도록 하겠습니다. 혹시 첫번째 예제 파일에 대한 설명을 못보셨다면 여기서 보실수 있습니다.

첫번째 시나리오는 UdpEchoClient와 UdpEchoServer를 어플리케이션으로 사용하였습니다. ns-3에서는 UdpEchoClient와 관련하여 아래와 같은 Trace Source를 제공하고 있습니다. - Tx: A new packet is created and is sent - Rx: A packet has been received - TxWithAddresses: A new packet is created and is sent - RxWithAddresses: A packet has been received

이중 저희는 클라이언트가 서버로 보낸 패킷을 나타내는 Tx를 Tracing 해보도록 하겠습니다. 이미 Trace Source는 추가되어 있기 때문에 CallBack 함수를 정의하고 main 함수에서 Trace Source와 CallBack 함수를 연결해주면 됩니다.

void TxTrace (Ptr<Packet const> new_packet) {
  std::cout << "Packet UID: " << new_packet->GetUid() << ", Packet Size: " << new_packet->GetSize() << std::endl;
}

위와 같이 CallBack 함수를 만들어 새로 전송되는 패킷의 UID와 크기를 출력할수 있도록 하고 아래 코드를 이용해서 CallBack 함수와 TraceSource를 연결해줍니다.

clientApps.Get(0)->TraceConnectWithoutContext ("Tx", MakeCallback (&TxTrace));

위의 2코드를 포함한 first.cc는 아래와 같습니다.

/* -*- 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/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"
#include "ns3/traced-value.h"
#include "ns3/trace-source-accessor.h"

using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

void TxTrace (Ptr<Packet const> new_packet) {
  std::cout << "Packet UID: " << new_packet->GetUid() << ", Packet Size: " << new_packet->GetSize() << std::endl;
}

int
main (int argc, char *argv[])
{
  CommandLine cmd;
  cmd.Parse (argc, argv);

  Time::SetResolution (Time::NS);
//  LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
//  LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);

  NodeContainer nodes;
  nodes.Create (2);

  PointToPointHelper pointToPoint;
  pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
  pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));

  NetDeviceContainer devices;
  devices = pointToPoint.Install (nodes);

  InternetStackHelper stack;
  stack.Install (nodes);

  Ipv4AddressHelper address;
  address.SetBase ("10.1.1.0", "255.255.255.0");

  Ipv4InterfaceContainer interfaces = address.Assign (devices);

  UdpEchoServerHelper echoServer (9);

  ApplicationContainer serverApps = echoServer.Install (nodes.Get (1));
  serverApps.Start (Seconds (1.0));
  serverApps.Stop (Seconds (10.0));

  UdpEchoClientHelper echoClient (interfaces.GetAddress (1), 9);
  echoClient.SetAttribute ("MaxPackets", UintegerValue (7));
  echoClient.SetAttribute ("Interval", TimeValue (Seconds (0.5)));
  echoClient.SetAttribute ("PacketSize", UintegerValue (1024));

  ApplicationContainer clientApps = echoClient.Install (nodes.Get (0));
  clientApps.Get(0)->TraceConnectWithoutContext ("Tx", MakeCallback (&TxTrace));
  clientApps.Start (Seconds (2.0));
  clientApps.Stop (Seconds (10.0));

  Simulator::Run ();
  Simulator::Destroy ();
  return 0;
}

2개의 코드를 추가한 것 이외에도 필요한 헤더를 추가하고 결과를 잘 볼 수 있도록 Log는 주석처리 하였습니다. 위 시나리오를 실행하면 다음과 같은 결과를 얻을 수 있습니다

Packet UID: 0, Packet Size: 1024
Packet UID: 5, Packet Size: 1024
Packet UID: 10, Packet Size: 1024
Packet UID: 15, Packet Size: 1024
Packet UID: 20, Packet Size: 1024
Packet UID: 25, Packet Size: 1024
Packet UID: 30, Packet Size: 1024

지금은 단순하게 새로운 패킷의 UID와 크기를 확인하기 위해서만 Tracing을 사용하였지만 throughput을 기록한다거나 UE의 움직임을 기록하기 위해서도 Tracing을 사용할 수 있습니다. 또 기본적으로 제공하는 Tracing Source 이외에 더 필요한 Source가 있으면 직접 추가할 수 있습니다. 지금까지 ns-3의 Tracing에 대해 살펴보았습니다. ns-3에서는 LogComponent, Pcap, Tracing 3가지를 이용해서 시나리오의 시뮬레이션 결과를 확인할 수 있습니다. 특히 Tracing을 사용하면 사용자가 필요한 데이터를 필요한 형태로 기록할 수 있기 때문에 결과를 남기기 좋은 방법이라고 할 수 있습니다. 다음 포스팅에서는 직접 어플리케이션을 만들어보고, 오늘 포스팅에서 다룬 Tracing을 이용하여 Congestion Window를 살펴보도록 하겠습니다.

카테고리:

업데이트:

댓글남기기