본문 바로가기

개인공부

[NS-3] 1. first.cc 해체 분석

NS-3을 성공적으로 설치했으니, 이제 기본적인 NS-3에서의 코드 동작을 알아보도록 하겠다.

 

우선 지난시간에 성공적으로 컴파일한 examples/tutorial/first.cc파일을 확인해보자.

 

first.cc 한눈에 보기

 

~/ns-allinone-3.40/ns-3.40/ns-3-dev/examples/tutorial/first.cc

#include "ns3/applications-module.h"
#include "ns3/core-module.h"
#include "ns3/internet-module.h"
#include "ns3/network-module.h"
#include "ns3/point-to-point-module.h"

// Default Network Topology
//
//       10.1.1.0
// n0 -------------- n1
//    point-to-point
//

using namespace ns3;

NS_LOG_COMPONENT_DEFINE("FirstScriptExample");

int
main(int argc, char* argv[])
{
    CommandLine cmd(__FILE__);
    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(1));
    echoClient.SetAttribute("Interval", TimeValue(Seconds(1.0)));
    echoClient.SetAttribute("PacketSize", UintegerValue(1024));

    ApplicationContainer clientApps = echoClient.Install(nodes.Get(0));
    clientApps.Start(Seconds(2.0));
    clientApps.Stop(Seconds(10.0));

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

 

정체모를 객체들과 메서드들이 무수하게 사용되고있다.

이럴 땐 대개 코드의 내용을 해석하기전에, 코드 실행의 결과물부터 보면 이해가 편하다.

 

first.cc 실행결과

 

 

실행 결과를 보니, 프로세스 시작 - 클라이언트 전송 - 서버 수신 - 서버 전송 - 클라이언트 수신 - 프로세스 종료의 과정이 드러난다.

first, 즉 튜토리얼의 처음 코드이기때문에 전송 - 수신 - 전송 -수신의 꽤나 단순한 토폴로지를 구현한 듯하다.

 


코드 해석 준비 (주석 읽기)

// Default Network Topology
//
//       10.1.1.0
// n0 -------------- n1
//    point-to-point
//

헤더보다 먼저 눈에 들어오는 이 주석 부분은, first.cc가 구현하고있는 토폴로지를 그림으로 나타낸 것이다.

앞으로는 계속 이 주석부터 읽고 코드의 동작을 유추할 것이다.

그림으로 알아낼 수 있는 사실은 다음과 같다.

 

1. 10.1.1.0 네트워크에서의 통신이다.

2. n0과 n1 두 노드 간의 통신이다.

3. p2p(point-to-point) 프로토콜을 사용한 통신이다.

 

위 내용을 염두하고, 지금부터는 이 내용들이 어떻게 코드로 표현되는지 살펴보면 되겠다.

 


코드 해체 분석 - 매크로 및 전역 scope

#include "ns3/applications-module.h"
#include "ns3/core-module.h"
#include "ns3/internet-module.h"
#include "ns3/network-module.h"
#include "ns3/point-to-point-module.h"

 

헤더 매크로 부분이다. 통신에 basic하게 필요해보이는 모듈들이 많다.  

모듈을 벌써부터 전부 이해하려면 너무 어지러우니 눈에만 익혀두자.

그 와중에 point-to-point 모듈은 눈에 띈다.

 

using namespace ns3;

NS_LOG_COMPONENT_DEFINE("FirstScriptExample");

 

ns3의 네임스페이스 안의 필드명을 사용하고

FirstScriptExample이라는 로그 컴포넌트를 생성? 초기화? 한 듯 하다.

 


코드 해체 분석 - main문

 

main문을 보자.

    CommandLine cmd(__FILE__);
    cmd.Parse(argc, argv);

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

 

cmd입력에 대한 내용을 argc, argv로 파싱한다. (exec을 위해)

시뮬레이션의 Time Resolution(시간 해상도) 단위를 나노밀리초로 설정하고,

로그에 UDP 호스트. 서버 응용이라고 기록한다.

따라서 구현하는 토폴로지는 UDP프로토콜일 예정이다.

    NodeContainer nodes;
    nodes.Create(2);

 

NodeContainer라는 객체를 불러와서, 노드를 2개 만들었다.

통신의 기본이되는 줄기세포? 쯤 된다고 생각하면 편하다.

 

이 노드에 무엇을 install하느냐에 따라 host, router, switch, csma.... 등등 다 될 수 있다.

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

 

P2P 프로토콜 구현을 도와주는 PointToPointHelper 객체를 초기화하고,

P2P 링크의 dataRate와 지연시간을 설정해준다.

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

 

그리고 (가상)네트워크 장치 객체를 생성하여, P2P 링크를 가지는 노드를 장치에 설치한다.

    InternetStackHelper stack;
    stack.Install(nodes);

 

노드가 기본적인 네트워크 프로토콜을 사용할 수 있도록, InternetStackHelper 객체를 노드에 설치해준다.

이 stack을 설치하지 않으면, 기본적으로 어떤 프로토콜도 지원되지 않는다.

    Ipv4AddressHelper address;
    address.SetBase("10.1.1.0", "255.255.255.0");
    
    Ipv4InterfaceContainer interfaces = address.Assign(devices);

 

우리가 익히 아는 네트워크주소, IPv4주소를 설정한다. 10.1.1.0이므로, 서브넷마스크는 255.255.255.0이다.

이후 네트워크 장치에 주소를 할당한다.

*주의할 점 : IP주소는 사용할 프로토콜이 모두 install된 device에 할당되어야한다.

    UdpEchoServerHelper echoServer(9);

 

처음에 로그에서 우리가 UDP EchoServer를 사용한다는것을 스포일러 당했기 때문에,

놀라지 않고 port 9번의 EchoServer객체를 생성한다(서버를 연다)는 것을 확인할 수 있다.

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

 

이제 ApplicationContainer객체를 echoServer로 초기화하여 애플리케이션을 만들게되는데,

이때 우리가 만들었던 노드 중 n1을 서버 애플리케이션으로 만든다.

이후 애플리케이션이 1초에 시작, 10초에 종료되도록 설정한다.

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

 

클라이언트 객체는 설정할 것이 좀 더 많은데,

n1에 대한 ip주소와 포트번호 (9)를 이용하여 초기화하고,

서버에게 보낼 패킷에 대한 설정이 필요하다.

지금 설정해놓은 상태는 아래와 같다.

 

최대 전송 패킷 갯수 : 1

패킷 간 전송 간격 : 1초

패킷 사이즈 : 1024바이트

    ApplicationContainer clientApps = echoClient.Install(nodes.Get(0));
    clientApps.Start(Seconds(2.0));
    clientApps.Stop(Seconds(10.0));

 

이후 클라이언트 앱을 n1노드에 설치하고, 

클라이언트는 2초에 시작, 10초에 종료하도록 설정한다.

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

 

모든 토폴로지 설정이 완료되었으므로, 시뮬레이터를 실행하고, Destory()한다.

 

정리하자면,

코드 구현은 TCP/IP 프로토콜의 Layer구조와 비슷하게 진행되는 것을 볼 수 있었다.

 

1. node를 생성

2. P2P링크 생성 및 설정 (Physical & Data Link Layer)

3. IPv4주소 생성 및 할당 (Network Layer)

4. Port번호 설정 (Transfort Layer)

5. Application 생성 및 실행 (Application Layer)


다시 확인하는 실행 결과

first.cc 실행결과

우리가 미리 봤던 실행결과를 이제 다시 한번 곱씹어보자.

1번 라인 : 서버는 1s에 이미 실행되었고, 클라이언트는 2초에 실행되어, 실행되자마자 1024바이트의 패킷을 서버에 전송했다.

2,3번 라인 : 서버는 Time Slice(프로세스 스케쥴링)가 돌아 온 후 패킷을 받고, echo reply를 보낸다.

4번 라인 : 클라이언트가 reply를 받고, 시뮬레이션이 종료된다.

 

각 함수의 인자값을 변경하며, 그 결과의 차이를 직접 확인해 볼 수 있다.