Tutorial do simulador de rede ns-3. Capítulo 4

Tutorial do simulador de rede ns-3. Capítulo 4
capítulo 1,2
capitulo 3

4 Visión xeral do concepto
4.1 Abstraccións clave
4.1.1 Nodo
4.1.2 Aplicación
4.1.3 Canle
4.1.4 Dispositivo de rede
4.1.5 Asistentes topolóxicos
4.2 Primeiro script ns-3
4.2.1 Código estándar
4.2.2 Complementos
4.2.3 espazo de nomes ns3
4.2.4 Rexistro
4.2.5 Función principal
4.2.6 Utilización de asistentes de topoloxía
4.2.7 Uso da aplicación
4.2.8 Simulador
4.2.9 Construír o teu guión
4.3 ns-3 Código fonte

Capítulo 4

Visión xeral do concepto

O primeiro que debemos facer antes de comezar a aprender ou escribir código ns-3 é explicar algúns conceptos básicos e abstraccións do sistema. Moito disto pode parecer obvio para algúns, pero recomendamos que se tome o tempo para ler esta sección para asegurarse de que está empezando cunha base sólida.

4.1 Abstraccións clave

Nesta sección, analizaremos algúns termos que se usan habitualmente na web pero que teñen un significado específico en ns-3.

4.1.1 Nodo

Na xerga de Internet, un dispositivo informático que se conecta a unha rede chámase host ou, ás veces, sistema final. Dado que ns-3 é un simulador de rede e non un simulador de Internet, non usamos deliberadamente o termo host, xa que está moi relacionado con Internet e os seus protocolos. En cambio, usamos un termo máis xeral, tamén usado por outros simuladores, que se orixina na teoría de grafos: nodo (nodo).

En ns-3, a abstracción subxacente dun dispositivo informático chámase nodo. Esta abstracción está representada en C++ pola clase Node. Clase NodeNode (nodo) proporciona métodos para manipular representacións de dispositivos informáticos en simulacións.

Debes entender Nodo como un ordenador ao que lle engades funcionalidades. Engadirás cousas como aplicacións, pilas de protocolos e tarxetas de periféricos con controladores que permiten que o ordenador faga un traballo útil. Usamos o mesmo modelo básico en ns-3.

4.1.2 Aplicación

Xeralmente, o software informático divídese en dúas grandes clases. O software do sistema organiza diversos recursos informáticos como memoria, ciclos do procesador, disco, rede, etc. segundo algún modelo informático. O software do sistema normalmente non utiliza estes recursos para realizar tarefas que benefician directamente ao usuario. Un usuario normalmente executa unha aplicación para acadar un obxectivo específico, que obtén e utiliza recursos controlados polo software do sistema.

Moitas veces, a liña de separación entre o sistema e o software de aplicación debúxase nos cambios de nivel de privilexios que se producen nas trampas do sistema operativo. ns-3 non ten ningún concepto real de sistema operativo e, polo tanto, ningún concepto de niveis de privilexios ou chamadas ao sistema. Non obstante, temos unha idea para unha aplicación. Do mesmo xeito que no "mundo real" as aplicacións de software execútanse en ordenadores para realizar tarefas, as aplicacións ns-3 execútanse en nodos ns-3 para controlar simulacións no mundo simulado.

En ns-3, a abstracción básica para un programa de usuario que xera algunha actividade para modelar é unha aplicación. Esta abstracción está representada en C++ pola clase Application. A clase de aplicación ofrece métodos para manipular as vistas da nosa versión de aplicacións a nivel de usuario nas simulacións. Espérase que os desenvolvedores especialicen a clase Aplicación nun sentido de programación orientada a obxectos para crear novas aplicacións. Neste tutorial, usaremos especializacións da clase de aplicación chamada Aplicación UdpEchoClient и Aplicación UdpEchoServer. Como podería esperar, estas aplicacións constitúen un conxunto de aplicacións cliente/servidor utilizadas para xerar e facer eco de paquetes de rede.

4.1.3 Canle

No mundo real, podes conectar un ordenador a unha rede. A miúdo os medios polos que se transmiten os datos nestas redes chámanse canles. Cando conectas un cable Ethernet a unha toma de parede, estás conectando o teu ordenador a unha conexión Ethernet. No mundo simulado ns-3, un nodo está conectado a un obxecto que representa unha canle de comunicación. Aquí, a abstracción básica da subrede de comunicación chámase canle e está representada en C++ pola clase Channel.

Clase CanleCanle ofrece métodos para xestionar a interacción de obxectos de subrede e conectar hosts a eles. As canles tamén poden ser especializadas polos desenvolvedores nun sentido de programación orientada a obxectos. A especialización da canle pode modelar algo tan sinxelo como un cable. Unha canle dedicada tamén pode modelar cousas complexas como un gran interruptor Ethernet ou un espazo tridimensional cheo de obstáculos no caso das redes sen fíos.

Usaremos versións especializadas da canle neste tutorial chamado CsmaChannelCsmaChannel, PointToPointChannelPointToPointChannel и WifiChannelWifiChannel. CsmaChannel, por exemplo, modela unha versión dunha subrede de comunicacións que implementa un ambiente de comunicacións de acceso múltiple con sentido do operador. Isto ofrécenos unha funcionalidade similar a Ethernet.

4.1.4 Dispositivo de rede

Antes era que se quería conectar un ordenador a unha rede, había que mercar un cable de rede específico e un dispositivo de hardware chamado (en terminoloxía de PC) tarxeta de periféricos que había que instalar no ordenador. Se unha tarxeta periférica implementaba algunhas funcións de rede, chamábanse tarxetas de interface de rede ou tarxetas de rede. Hoxe, a maioría dos ordenadores veñen con hardware de interface de rede integrado e os usuarios non os ven como dispositivos separados.

Unha tarxeta de rede non funcionará sen un controlador de software que controle o seu hardware. En Unix (ou Linux), unha peza de equipo periférico clasifícase como un dispositivo. Os dispositivos xestionanse mediante controladores de dispositivo e os dispositivos de rede (NIC) son xestionados mediante controladores de dispositivos de rede (controladores de dispositivos de rede) e denomínanse colectivamente dispositivos de rede (dispositivos de rede). En Unix e Linux, refírese aos dispositivos de rede por nomes como eth0.

En ns-3, a abstracción do dispositivo de rede abarca tanto o software do controlador como o hardware que se está a modelar. Na simulación, un dispositivo de rede é "instalado" nun nodo para permitir que se comunique con outros nodos a través de canles. Do mesmo xeito que un ordenador real, un nodo pódese conectar a varias canles a través de varios dispositivos NetDevices.

A abstracción de rede dun dispositivo está representada en C++ pola clase NetDevice. Clase NetDevice proporciona métodos para xestionar conexións a obxectos Node e Channel; e pode ser especializado polos desenvolvedores no sentido de programación orientada a obxectos. Neste tutorial usaremos varias versións especializadas de NetDevice chamadas CsmaNetDevice, PointToPointNetDevice и Dispositivo WifiNet. Do mesmo xeito que un adaptador de rede Ethernet está deseñado para funcionar cunha rede Ethernet, CsmaNetDevice deseñado para traballar CsmaChannel, PointToPointNetDevice deseñado para traballar PointToPointChannelE Dispositivo WifiNet - Deseñado para traballar WifiChannel.

4.1.5 Asistentes topolóxicos

Nunha rede real, atoparás ordenadores host con tarxetas de rede engadidas (ou incorporadas). En ns-3 diríamos que verás nodos con NetDevices conectados. Nunha rede simulada grande, terás que organizar conexións entre moitos obxectos Nodo, NetDevice и Canle.

Desde a conexión de NetDevices a nodos, NetDevices a ligazóns, a asignación de enderezos IP, etc. en ns-3 son unha tarefa común, para facelo o máis sinxelo posible, fornecemos os chamados axudantes de topoloxía. Por exemplo, para crear un NetDevice, cómpre realizar moitas operacións do núcleo ns-3, engadir un enderezo MAC, instalar o dispositivo de rede en Node, configurar a pila de protocolos do nodo e, a continuación, conectar o NetDevice á Canle. Será necesario traballar aínda máis para conectar varios dispositivos a ligazóns multipunto e, a continuación, conectar as redes individuais a unha rede de Internetworks. Ofrecemos obxectos auxiliares de topoloxía que combinan estas moitas operacións nun modelo fácil de usar para a súa comodidade.

4.2 Primeiro script ns-3

Se instalou o sistema como se suxeriu anteriormente, terá a versión ns-3 nun directorio chamado repos no seu directorio persoal. Ir ao directorio liberar

Se non tes un directorio deste tipo, significa que non especificou o directorio de saída ao crear a versión de lanzamento de ns-3, constrúeo así:
$ ./waf configure —build-profile=release —out=build/release,
$ ./waf compilación

alí deberías ver unha estrutura de directorios semellante á seguinte:

AUTHORS       examples      scratch       utils       waf.bat*
bindings      LICENSE       src           utils.py    waf-tools
build         ns3           test.py*      utils.pyc   wscript
CHANGES.html  README        testpy-output VERSION     wutils.py
doc           RELEASE_NOTES testpy.supp   waf*        wutils.pyc

Ir ao directorio exemplos/titorial. Deberías ver un ficheiro alí chamado primeiro.cc. Este é un script que creará unha conexión simple punto a punto entre dous nodos e transmitirá un paquete entre os nodos. Vexamos este script liña por liña; para facelo, abre first.cc no teu editor favorito.

4.2.1 Código estándar
A primeira liña do ficheiro é a liña do modo editor emacs. Infórmalle a emacs as convencións de formato (estilo de codificación) que usamos no noso código fonte.

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */

Este é sempre un tema bastante polémico, polo que hai que poñer o récord para sacalo de inmediato. O proxecto ns-3, como a maioría dos grandes proxectos, adoptou un estilo de codificación ao que debe axustarse todo o código aportado. Se queres aportar o teu código ao proxecto, terás que cumprir co estándar de codificación ns-3, tal e como se describe no ficheiro doc/codingstd.txt ou aparece na páxina web do proxecto: https://www.nsnam.org/develop/contributing-code/coding-style/.

Recomendámosche que te acostumes ao aspecto do código ns-3 e que apliques este estándar sempre que traballes co noso código. Todo o equipo de desenvolvemento e os colaboradores estiveron de acordo con isto despois de quexarse. A liña do modo emacs anterior facilita o formato correcto se está a usar o editor emacs.

O simulador ns-3 ten licenza de uso GNU General Public License. Verá a cabeceira legal de GNU adecuada en cada ficheiro de distribución ns-3. Moitas veces verás un aviso de copyright dunha das institucións participantes no proxecto ns-3 enriba do texto GPL e do autor, que se mostra a continuación.

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

4.2.2 Complementos

O código en si comeza cunha serie de instrucións de inclusión (incluír).

#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"

Para axudar aos nosos usuarios de scripts de alto nivel a facer fronte ao gran número de ficheiros de cabeceira presentes no sistema, agrupámolos segundo o seu uso en módulos grandes. Ofrecemos un único ficheiro de cabeceira que cargará de forma recursiva todos os ficheiros de cabeceira utilizados nun módulo determinado. En lugar de ter que buscar exactamente o encabezado que necesitas e posiblemente obter a lista correcta de dependencias, ofrecémosche a posibilidade de descargar un grupo de ficheiros con gran granularidade. Non é o enfoque máis eficiente, pero sen dúbida facilita moito a escritura de guións.

Cada un dos ficheiros de inclusión ns-3 colócase nun directorio chamado ns3 (subdirectorio de compilación) para evitar conflitos de nomes de ficheiro durante o proceso de compilación. Arquivo ns3/core-module.h corresponde ao módulo ns-3, que atoparás no directorio src/core na versión que instalou. Na lista deste directorio atoparás un gran número de ficheiros de cabeceira. Cando fas a montaxe, Waf coloca ficheiros de cabeceira públicos no directorio ns3 nun subdirectorio construír/depurar

Se non tes un directorio deste tipo, significa que non especificou o directorio de saída ao crear a versión de lanzamento de ns-3, constrúeo así:
$ ./waf configure --build-profile=depurar --out=construír/depurar
$ ./waf compilación
ou
$ ./waf configure --build-profile=optimizado --out=construír/optimizar
$ ./waf compilación

ou construír/optimizar, dependendo da súa configuración. Waf tamén xerará automaticamente un ficheiro de inclusión do módulo para cargar todos os ficheiros de cabeceira públicos. Xa que, por suposto, estás seguindo esta guía relixiosamente, xa o fixeches

$ ./waf -d debug --enable-examples --enable-tests configure

para configurar o proxecto para executar compilacións de depuración que inclúan exemplos e probas. Ti tamén o fixeches

$ ./waf

para montar o proxecto. Entón agora cando miras no directorio ../../build/debug/ns3, entón alí atoparás, entre outros, os ficheiros de cabeceira dos catro módulos mostrados anteriormente. Podes consultar o contido destes ficheiros e comprobar que inclúen todos os ficheiros públicos utilizados polos módulos correspondentes.

4.2.3 espazo de nomes ns3

Seguinte liña no guión primeiro.cc é unha declaración de espazo de nomes.

using namespace ns3;

O proxecto ns-3 está implementado nun espazo de nomes C++ chamado ns3. Isto agrupa todas as declaracións relacionadas con ns-3 nun ámbito fóra do espazo de nomes global, o que se espera que axude coa integración con outro código. Usando o operador C++ introdúcese o espazo de nomes ns-3 na rexión declarativa (global) actual. Esta é unha forma elegante de dicir que despois desta declaración, non terás que escribir o operador de permisos ns3::scope antes de todo o teu código ns-3 para usalo. Se non estás familiarizado cos espazos de nomes, consulta case calquera libro de texto de C++ e compara o espazo de nomes ns3 usando o espazo de nomes e a declaración std using namespace std; en exemplos de traballo co operador de saída custo e regatos.

4.2.4 Rexistro

A seguinte liña do guión é,

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

Usaremos esta declaración como un lugar cómodo para discutir o noso sistema de documentación Osíxeno. Se miras o sitio web do proxecto ns-3, atoparás unha ligazón de documentación na barra de navegación. Se fai clic nesta ligazón dirixirase á nosa páxina de documentación. Hai unha ligazón "Última versión" que o levará á documentación da última versión estable de ns-3. Se selecciona a ligazón "Documentación da API", dirixirase á páxina de documentación da API ns-3.

Na parte esquerda da páxina atoparás unha representación gráfica da estrutura da documentación. Un bo lugar para comezar é o "libro" dos módulos ns-3 na árbore de navegación ns-3. Se revelas módulos, verá unha lista de documentación de módulos ns-3. Como se comentou anteriormente, o concepto de módulo aquí está directamente relacionado cos ficheiros incluídos no módulo anterior. O subsistema de rexistro ns-3 é discutido na sección Usando o módulo de rexistro, polo que volveremos máis adiante neste tutorial, pero podes aprender sobre a afirmación anterior mirando o módulo Núcleoe despois abrindo o libro Ferramentas de depuracióne despois seleccionando a páxina Logging. Prema en Logging.

Agora debería revisar a documentación Osíxeno para módulo Logging. Na lista de macros da parte superior da páxina, verá unha entrada para NS_LOG_COMPONENT_DEFINE. Antes de facer clic na ligazón, asegúrate de consultar a "Descrición detallada" do módulo de rexistro para entender como funciona en xeral. Para facelo, pode desprazarse cara abaixo ou seleccionar "Máis..." debaixo do gráfico.

Unha vez que teñas unha idea xeral do que está a suceder, consulta a documentación do NS_LOG_COMPONENT_DEFINE específico. Non vou duplicar a documentación aquí, pero para resumir, esta liña declara un compoñente de rexistro chamado FirstScriptExample, que lle permite activar ou desactivar o rexistro de mensaxes da consola por referencia a un nome.

4.2.5 Función principal

Nas seguintes liñas do guión verás,

int 
main (int argc, char *argv[])
{ 

Esta é simplemente unha declaración da función principal do seu programa (script). Como con calquera programa en C++, cómpre definir unha función principal, esta execútase primeiro. Aquí non hai nada especial. O teu script ns-3 é só un programa C++. A seguinte liña establece a resolución de tempo en 1 nanosegundo, que é o valor predeterminado:

Time::SetResolution (Time::NS);

A resolución de tempo, ou simplemente a resolución, é o menor valor de tempo que se pode usar (a menor diferenza representable entre dous tempos). Podes cambiar a resolución exactamente unha vez. O mecanismo que proporciona esta flexibilidade consume memoria, polo que unha vez que se establece explícitamente a resolución, liberamos a memoria, evitando novas actualizacións. (Se non estableces a resolución explícitamente, por defecto será un nanosegundo e a memoria liberarase cando comece a simulación).

As seguintes dúas liñas de script úsanse para activar dous compoñentes de rexistro integrados nas aplicacións EchoClient и EchoServer:

LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO); LogComponentEnable("UdpEchoServerApplication", LOG_LEVEL_INFO);

Se le a documentación do compoñente Rexistro, verá que hai varios niveis de rexistro/granularidade que pode activar en cada compoñente. Estas dúas liñas de código permiten o rexistro de depuración ao nivel INFO para clientes e servidores de eco. Neste nivel, a aplicación imprimirá mensaxes mentres envía e recibe paquetes durante a simulación.

Agora pasaremos ao negocio de crear a topoloxía e executar a simulación. Usamos obxectos auxiliares de topoloxía para facer este traballo o máis sinxelo posible.

4.2.6 Utilización de asistentes de topoloxía

As seguintes dúas liñas de código do noso script crearán realmente os obxectos Node ns-3 que representarán os ordenadores na simulación.

NodeContainer nodes;
nodes.Create (2);

Antes de continuar, imos buscar a documentación para a clase NodeContainer. Outra forma de acceder á documentación dunha clase determinada é a través da pestana clases nas páxinas Osíxeno. Se xa tes Doxygen aberto, simplemente desprázate ata a parte superior da páxina e selecciona a pestana Clases. Deberías ver un novo conxunto de pestanas, unha das cales é unha lista de clases. Baixo esta pestana verá unha lista de todas as clases ns-3. Desprázate cara abaixo ata ns3::NodeContainer. Cando atopes unha clase, selecciónaa para ir á documentación da clase.

Como lembramos, unha das nosas abstraccións fundamentais é o nodo. Representa o ordenador ao que lle imos engadir cousas como pilas de protocolos, aplicacións e tarxetas de periféricos. Auxiliar de topoloxía NodeContainer ofrece un xeito cómodo de crear, xestionar e acceder a calquera obxecto Nodo, que creamos para executar a simulación. A primeira liña anterior simplemente declara NodeContainer, que chamamos nós. A segunda liña chama ao método Create no obxecto de nodos e pídelle ao contedor que cree dous nodos. Como se describe en Osíxeno, o contedor solicita ao sistema ns-3 que cree dous obxectos Nodo e almacena punteiros a estes obxectos internamente.

Os nodos creados no script aínda non fan nada. O seguinte paso para construír a topoloxía é conectar os nosos nodos á rede. A forma máis sinxela de rede que admitimos é unha conexión punto a punto entre dous nodos. Agora imos crear unha conexión deste tipo.

PointToPointHelper

Creamos unha conexión punto a punto usando un patrón familiar, usando un obxecto auxiliar de topoloxía para facer o traballo de baixo nivel necesario para a conexión. Lembre que as nosas dúas abstraccións fundamentais NetDevice и Canle. No mundo real, estes termos corresponden aproximadamente a tarxetas periféricas e cables de rede. Normalmente, estas dúas cousas están estreitamente relacionadas entre si e ninguén pode contar con compartir, por exemplo, dispositivos Ethernet a través dunha canle sen fíos. Os nosos axudantes de topoloxía seguen esta estreita relación e, polo tanto, empregará un único obxecto neste escenario PointToPointHelper para configurar e conectar obxectos ns-3 PointToPointNetDevice и PointToPointChannel. As seguintes tres liñas do guión:

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

primeira liña,

PointToPointHelper pointToPoint;

crea unha instancia dun obxecto na pila PointToPointHelper. Desde un punto de vista de nivel superior a seguinte liña,

pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));

di ao obxecto PointToPointHelper use o valor "5 Mbit/s" (cinco megabits por segundo) como "Taxa de datos».

Desde un punto de vista máis específico, a cadea "DataRate" corresponde ao que chamamos atributo PointToPointNetDevice. Se miras Osíxeno para clase ns3::PointToPointNetDevice e na documentación do método GetTypeId atopará unha lista de atributos definidos para o dispositivo. Entre eles estará o atributo "Taxa de datos" A maioría dos obxectos ns-3 visibles polo usuario teñen listas similares de atributos. Usamos este mecanismo para configurar facilmente a simulación sen recompilación, como verás na seguinte sección.

Semellante a "Taxa de datos" en PointToPointNetDevice, atopará o atributo "Delay" asociado ao PointToPointChannel. A liña final

pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));

di PointToPointHelper use o valor "2 ms" (dous milisegundos) como valor de retardo de propagación para a ligazón punto a punto que crea posteriormente.

NetDeviceContainer

Polo momento temos no guión NodeContainer, que contén dous nodos. Temos PointToPointHelper, que está preparado para crear obxectos PointToPointNetDevices e conectándoos mediante un obxecto PointToPointChannel. Do mesmo xeito que usamos o obxecto auxiliar de topoloxía NodeContainer para crear nós, preguntarémolo PointToPointHelper realizar traballos para nós relacionados coa creación, configuración e instalación dos nosos dispositivos. Necesitamos unha lista de todos os obxectos creados NetDevice, polo que usamos NetDeviceContainer para almacenalos do mesmo xeito que usamos NodeContainer para almacenar os nodos que creamos. As dúas liñas de código seguintes,

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

configuración completa do dispositivo e da canle. A primeira liña declara o contenedor do dispositivo mencionado anteriormente e a segunda fai o traballo principal. Método Instalar obxecto PointToPointHelper acepta NodeContainer como parámetro. Dentro NetDeviceContainer para cada nodo situado en NodeContainer se crea (para a comunicación punto a punto debe haber exactamente dous deles) PointToPointNetDevice créase e gárdase no contedor do dispositivo. PointToPointChannel créase e dous están unidos a el PointToPointNetDevices. Despois de crear obxectos, os atributos almacenados en PointToPointHelper, utilízanse para inicializar os atributos correspondentes nos obxectos creados.

Despois de facer unha chamada pointToPoint.Install (nodos) teremos dous nodos, cada un cun dispositivo de rede punto a punto instalado e un enlace punto a punto entre eles. Ambos os dispositivos estarán configurados para transmitir datos a unha velocidade de cinco megabits por segundo cun atraso de transmisión de dous milisegundos pola canle.

InternetStackHelper

Agora temos nodos e dispositivos configurados, pero os nosos nodos non teñen pilas de protocolos instaladas. As seguintes dúas liñas de código encargaranse diso.

InternetStackHelper stack;
stack.Install (nodes);

InternetStackHelper - é un axudante de topoloxía para pilas de Internet, similar a PointToPointHelper para dispositivos de rede punto a punto. Método Instalar toma NodeContainer como parámetro. Cando se execute, instalará a pila de Internet (TCP, UDP, IP, etc.) en cada nodo de contedores.

IPv4AddressHelper

Despois temos que asociar os nosos dispositivos con enderezos IP. Ofrecemos un asistente de topoloxía para xestionar a asignación de enderezos IP. A única API visible para o usuario é configurar o enderezo IP base e a máscara de rede para usar cando se faga a distribución de enderezos real (isto faise nun nivel inferior dentro do axudante). As dúas seguintes liñas de código do noso script de exemplo primeiro.cc,

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

declare o obxecto auxiliar de enderezos e dígalle que debería comezar a asignar enderezos IP desde a rede 10.1.1.0, usando a máscara de bits 255.255.255.0 para determinalo. Por defecto, os enderezos asignados comezarán a unha e aumentarán de forma monótona, polo que o primeiro enderezo asignado desde esta base será 10.1.1.1, despois 10.1.1.2, etc. En realidade, a un nivel baixo, o sistema ns-3 lembra todos os enderezos IP asignados e xera un erro fatal se crea accidentalmente unha situación na que se xera dúas veces o mesmo enderezo (por certo, este erro é difícil de depurar).

A seguinte liña de código,

Ipv4InterfaceContainer interfaces = address.Assign (devices);

realiza a asignación de enderezos real. En ns-3 establecemos unha conexión entre un enderezo IP e un dispositivo que utiliza o obxecto Interface IPv4. Do mesmo xeito que ás veces necesitamos unha lista de dispositivos de rede creados polo asistente para o seu uso posterior, ás veces necesitamos unha lista de obxectos Interface IPv4. IPv4InterfaceContainer proporciona esta funcionalidade.

Construímos unha rede punto a punto, con pilas instaladas e enderezos IP asignados. Agora necesitamos aplicacións en cada nodo para xerar tráfico.

4.2.7 Uso da aplicación

Outra das principais abstraccións do sistema ns-3 é aplicación (aplicación). Neste escenario estamos a usar dúas especializacións de clase base aplicación ns-3 chamado Aplicación UdpEchoServer и Aplicación UdpEchoClient. Como en casos anteriores, utilizamos obxectos auxiliares para configurar e xestionar os obxectos base. Aquí usamos UdpEchoServerHelper и UdpEchoClientHelper obxectos para facilitarnos a vida.

UdpEchoServerHelper

As seguintes liñas de código do noso script de exemplo first.cc úsanse para configurar unha aplicación de servidor de eco UDP nun dos nós que creamos anteriormente.

UdpEchoServerHelper echoServer (9);

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

Créase a primeira liña de código do fragmento anterior UdpEchoServerHelper. Como é habitual, esta non é unha aplicación en si, é un obxecto que nos axuda a crear aplicacións reais. Unha das nosas convencións é pasar os atributos necesarios ao construtor do obxecto auxiliar. Neste caso, o axudante non pode facer nada útil a non ser que se lle dea o número de porto no que o servidor escoitará os paquetes, este número tamén debe ser coñecido polo cliente. Neste caso, pasamos o número de porto ao construtor auxiliar. O construtor, pola súa banda, simplemente fai SetAttribute co valor pasado. Máis tarde, se o desexa, pode usar SetAttribute para establecer un valor diferente para o atributo Port.

Como moitos outros obxectos auxiliares, o obxecto UdpEchoServerHelper ten un método Instalar. Ao executar este método de forma eficaz, créase unha aplicación básica do servidor de eco e únese ao host. Curiosamente, o método Instalar acepta NodeContainer como parámetro igual que os demais Instalar métodos que vimos.

A conversión implícita de C++ que traballa aquí toma o resultado do método nodo.Obter(1) (que devolve un punteiro intelixente ao obxecto nodo - Ptr ) e utilízao no construtor para o obxecto anónimo NodeContainerque despois se pasa ao método Instalar. Se non pode determinar no código C++ que sinatura do método se compila e se executa, busque entre as conversións implícitas.

Agora vemos iso echoServer.Instalar a piques de instalar a aplicación Aplicación UdpEchoServer en atopado en NodeContainerque utilizamos para xestionar os nosos nodos, nodo con índice 1. Método Instalar devolverá un contenedor que contén punteiros a todas as aplicacións (neste caso unha, xa que pasamos un ficheiro anónimo NodeContainer, que contén un nodo) creado polo axudante.

As aplicacións deben especificar cando comezar a xerar tráfico "comezar" e pode ter que especificar adicionalmente un momento no que detelo "parar". Ofrecemos as dúas opcións. Estes tempos fíxanse mediante os métodos ApplicationContainer comezo и Deixe. Estes métodos aceptan parámetros de tipo Tempo. Neste caso usamos unha secuencia explícita de conversións en C++ para tomar C++ dobrar 1.0 e convérteo nun obxecto tns-3 Time que usa o obxecto Segundos para converterse en segundos. Lembra que as regras de conversión poden ser controladas polo autor do modelo e que C++ ten as súas propias regras, polo que non sempre podes contar con que os parámetros se convertan do xeito que esperabas. Dúas liñas

serverApps.Start (Seconds (1.0));
serverApps.Stop (Seconds (10.0));

fará que a aplicación do servidor de eco se inicie (se active automaticamente) un segundo despois de que se inicie a simulación e se deteña (se apague) despois de dez segundos da simulación. Debido ao feito de que declaramos un evento de simulación (evento de parada da aplicación), que se executará en dez segundos, simularanse polo menos dez segundos de funcionamento da rede.

UdpEchoClientHelper

Aplicación cliente perder configurado dun xeito case similar ao servidor. Hai un obxecto base Aplicación UdpEchoClientdirixido por
UdpEchoClientHelper.

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));;

Non obstante, para o cliente de eco necesitamos establecer cinco atributos diferentes. Os dous primeiros atributos establécense no momento da creación UdpEchoClientHelper. Pasamos parámetros que se usan (dentro do auxiliar) para establecer os atributos "Enderezo remoto" и "RemotePort" de acordo co noso acordo para pasar os parámetros necesarios ao construtor auxiliar.

Lembremos que usamos IPv4InterfaceContainer para rastrexar os enderezos IP que temos asignados aos nosos dispositivos. A interface nula do contenedor de interfaces corresponderá ao enderezo IP do nodo nulo do contenedor de nodos. A primeira interface do contedor de interfaces corresponde ao enderezo IP do primeiro nodo do contenedor de nodos. Entón, na primeira liña de código (arriba), creamos un axudante e dicímoslle que o enderezo remoto do cliente será o enderezo IP asignado ao host onde se atopa o servidor. Tamén dicimos que hai que organizar o envío de paquetes ao porto nove.

O atributo "MaxPackets" indica ao cliente o número máximo de paquetes que podemos enviar durante a simulación. O atributo "Intervalo" indica ao cliente canto tempo debe esperar entre os paquetes, e o atributo "Tamaño do paquete" indica ao cliente canto debe ser a carga útil do paquete. Con esta combinación de atributos dicímoslle ao cliente que envíe un único paquete de 1024 bytes.

Do mesmo xeito que co servidor de eco, establecemos os atributos do cliente de eco comezo и Deixe, pero aquí iniciamos o cliente un segundo despois de que se acende o servidor (dous segundos despois do inicio da simulación).

4.2.8 Simulador

Neste punto necesitamos executar a simulación. Isto faise mediante a función global Simulador::Run.

Simulator::Run ();

Cando chamamos aos métodos antes,

serverApps.Start (Seconds (1.0));
serverApps.Stop (Seconds (10.0));
... 
clientApps.Start (Seconds (2.0));
clientApps.Stop (Seconds (10.0));

en realidade programamos eventos no simulador a 1,0 segundos, 2,0 segundos e dous eventos a 10,0 segundos. Despois da chamada Simulador::Run, o sistema comezará a ver a lista de eventos programados e executalos. Primeiro desencadeará un evento despois de 1,0 segundos, o que activará a aplicación do servidor de eco (este evento pode, á súa vez, programar moitos outros eventos). Despois disparará un evento programado en t=2,0 segundos que iniciará a aplicación cliente de eco. De novo, este evento pode ter moitos máis eventos previstos. A implementación do evento de inicio no cliente de eco comezará a fase de transferencia de datos da simulación enviando un paquete ao servidor.

O acto de enviar un paquete ao servidor desencadeará unha cadea de eventos que se programarán automaticamente entre bastidores e que implementarán a mecánica de envío dun paquete de eco segundo os parámetros de temporización que fixemos no script.

Como resultado, xa que estamos enviando só un paquete (lembra, o atributo MaxPackets estaba definido como un), a cadea de eventos iniciada por este único ping cliente finalizará e a simulación pasará ao modo de espera. Unha vez que isto ocorra, os restantes eventos programados serán os eventos Deixe para servidor e cliente. Cando se executen estes eventos, non quedará ningún evento para o procesamento posterior e Simulador::Run devolverá o control. A simulación está completa.

Todo o que queda é limpar por si mesmo. Isto faise chamando á función global Simulador::Destruír. Porque se chamaron as funcións auxiliares (ou código ns-3 de baixo nivel), que están organizadas de xeito que se inserisen ganchos no simulador para destruír todos os obxectos que se crearon. Non precisabas facer un seguimento de ningún destes obxectos por ti mesmo; todo o que tiñas que facer era chamar Simulador::Destruír e saír. O sistema ns-3 fará este duro traballo por ti. As liñas restantes do noso primeiro script ns-3, first.cc, fan exactamente iso:

Simulator::Destroy ();
return 0;
}

Cando parará o simulador?

ns-3 é un simulador de eventos discretos (DE). Neste simulador, cada evento está asociado co seu tempo de execución e a simulación continúa procesando os eventos na orde en que ocorren a medida que avanza a simulación. Os eventos poden facer que se programen eventos futuros (por exemplo, un temporizador pode reprogramarse para rematar de contar no intervalo seguinte).

Os eventos iniciais adoitan ser iniciados pola entidade, por exemplo IPv6 programará o descubrimento de servizos na rede, solicitudes de veciños, etc. A aplicación programa o primeiro evento de envío de paquetes, etc. Cando se procesa un evento, pode xerar cero, un ou máis eventos. A medida que avanza a simulación, ocorren eventos, xa sexan rematando ou creando outros novos. A simulación deterase automaticamente se a cola de eventos está baleira ou se detecta un evento especial Deixe. Evento Deixe xerado pola función Simulador::Stop (tempo de parada).

Hai un caso típico no que Simulator::Stop é absolutamente necesario para deter a simulación: cando hai eventos autosuficientes. Os eventos autosuficientes (ou repetitivos) son eventos que sempre se reprograman. Como consecuencia, sempre manteñen a cola de eventos non baleira. Hai moitos protocolos e módulos que conteñen eventos repetitivos, por exemplo:

• FlowMonitor: comprobación periódica de paquetes perdidos;

• RIPng: emisión periódica de actualizacións da táboa de enrutamento;

• etc.

Nestes casos Simulador::Stop necesario para deter a simulación correctamente. Ademais, cando ns-3 está en modo de emulación, o RealtimeSimulator úsase para sincronizar o reloxo de simulación co reloxo da máquina e Simulador::Stop necesario para deter o proceso.

Moitos dos programas de simulación do libro de texto non chaman Simulador::Stop explícitamente, xa que rematan automaticamente cando se esgotan os eventos da cola. Non obstante, estes programas tamén aceptarán a chamada Simulator::Stop. Por exemplo, a seguinte instrución adicional no primeiro programa de exemplo programaría unha parada explícita en 11 segundos:

+ Simulator::Stop (Seconds (11.0));
  Simulator::Run ();
  Simulator::Destroy ();
  return 0;
}

O anterior non cambiará realmente o comportamento deste programa, xa que esta simulación particular remata naturalmente despois de 10 segundos. Pero se cambiases o tempo de parada na declaración anterior de 11 segundos a 1 segundo, notarías que a simulación se detén antes de que calquera saída chegue á pantalla (xa que a saída ocorre despois duns 2 segundos de tempo de simulación).

É importante chamar a Simulator::Stop antes de chamar a Simulator::Run; en caso contrario, Simulator::Run nunca pode devolver o control ao programa principal para executar a parada.

4.2.9 Construír o teu guión

Fixemos que a creación dos teus guións sinxelos sexa trivial. Todo o que tes que facer é poñer o teu script no directorio scratch e crearase automaticamente se o executas Waf. Imos probar. Volve ao directorio de nivel superior e copia exemplos/titorial/primeiro.cc ao catálogo rabuñar

$ cd ../.. 
$ cp examples/tutorial/first.cc scratch/myfirst.cc

Agora crea o teu primeiro script de mostra usando waf:

$ ./waf

Deberías ver mensaxes que indican que o teu primeiro exemplo foi creado correctamente.

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
[614/708] cxx: scratch/myfirst.cc -> build/debug/scratch/myfirst_3.o
[706/708] cxx_link: build/debug/scratch/myfirst_3.o -> build/debug/scratch/myfirst
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (2.357s)

Agora podes executar o exemplo (teña en conta que se creas o teu programa no directorio scratch, debes executalo desde rabuñar):

$ ./waf --run scratch/myfirst

Deberías ver unha saída similar:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.418s) Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
Received 1024 bytes from 10.1.1.2

Aquí podes ver que o sistema de compilación verifica que o ficheiro foi construído e despois execútao. Ves que a entrada do compoñente no cliente de eco indica que enviou un único paquete de 1024 bytes ao servidor de eco 10.1.1.2. Tamén ves o compoñente de rexistro no servidor de eco para dicir que recibiu 1024 bytes de 10.1.1.1. O servidor de eco reproduce silenciosamente o paquete e podes ver no rexistro do cliente de eco que recibiu o paquete de volta do servidor.

4.3 ns-3 Código fonte

Agora que usaches algúns dos axudantes ns-3, podes botarlle unha ollada a algúns dos códigos fonte que implementan esta funcionalidade. O código máis recente pódese ver no noso servidor web na seguinte ligazón: https://gitlab.com/nsnam/ns-3-dev.git. Alí verás a páxina de resumo de Mercurial para a nosa árbore de desenvolvemento ns-3. Na parte superior da páxina verás varias ligazóns,

summary | shortlog | changelog | graph | tags | files

Continúa e selecciona a ligazón dos ficheiros. Así será o nivel superior da maioría dos nosos repositorios:

drwxr-xr-x                               [up]
drwxr-xr-x                               bindings python  files
drwxr-xr-x                               doc              files
drwxr-xr-x                               examples         files
drwxr-xr-x                               ns3              files
drwxr-xr-x                               scratch          files
drwxr-xr-x                               src              files
drwxr-xr-x                               utils            files
-rw-r--r-- 2009-07-01 12:47 +0200 560    .hgignore        file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 1886   .hgtags          file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 1276   AUTHORS          file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 30961  CHANGES.html     file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 17987  LICENSE          file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 3742   README           file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 16171  RELEASE_NOTES    file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 6      VERSION          file | revisions | annotate
-rwxr-xr-x 2009-07-01 12:47 +0200 88110  waf              file | revisions | annotate
-rwxr-xr-x 2009-07-01 12:47 +0200 28     waf.bat          file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 35395  wscript          file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 7673   wutils.py        file | revisions | annotate

Os nosos scripts de exemplo están no directorio exemplos. Se fai clic nos exemplos verá unha lista de subdirectorios. Un dos ficheiros do subdirectorio titorial - first.cc. Se fai clic en primeiro.cc verás o código que acabas de aprender.

O código fonte localízase principalmente no directorio src. Podes ver o código fonte premendo no nome do directorio ou facendo clic na ligazón dos ficheiros á dereita do nome do directorio. Se fai clic no directorio src, obterá unha lista de subdirectorios src. Se fai clic no subdirectorio principal, atopará unha lista de ficheiros. O primeiro ficheiro que verás (no momento de escribir esta guía) é abortar.h. Se premedes na ligazón abortar.h, será enviado ao ficheiro fonte para abortar.h, que contén macros útiles para saír dos scripts se se detectan condicións anormais. O código fonte dos axudantes que utilizamos neste capítulo pódese atopar no directorio src/Applications/helper. Non dubides en explorar a árbore de directorios para descubrir onde está e comprender o estilo dos programas ns-3.

Fonte: www.habr.com

Engadir un comentario