
4 Visão Geral do Conceito
4.1 Abstrações Chave
4.1.1 Node (Nó)
4.1.2 Application (Aplicação)
4.1.3 Channel (Canal)
4.1.4 Net Device (Dispositivo de Rede)
4.1.5 Auxiliares Topológicos
4.2 Primeiro Script ns-3
4.2.1 Código Boilerplate
4.2.2 Módulos Plugáveis
4.2.3 Espaço de Nomes ns3
4.2.4 Registro
4.2.5 Função Principal
4.2.6 Uso de Auxiliares Topológicos
4.2.7 Uso de Application
4.2.8 Simulador
4.2.9 Montagem do Seu Cenário
4.3 Código Fonte do ns-3
Capítulo 4
Visão Geral do Conceito
A primeira coisa que precisamos fazer antes de começar a estudar ou escrever código ns‑3 é explicar alguns conceitos e abstrações fundamentais no sistema. Muito disso pode parecer óbvio para alguns, mas recomendamos dedicar um tempo para ler esta seção, para garantir que você comece com uma base sólida.
4.1 Abstrações Chave
Nesta seção, vamos discutir alguns termos que são comumente usados na rede, mas têm um significado específico no ns‑3.
4.1.1 Node (Nó)
Na gíria da internet, um dispositivo de computador conectado à rede é chamado de host ou, às vezes, sistema final. Como ns‑3 é um simulador de rede, e não um simulador de internet, intencionalmente não usamos o termo host, pois está intimamente relacionado à internet e seus protocolos. Em vez disso, usamos um termo mais geral, também utilizado por outros simuladores, que se origina da teoria dos grafos — nó (node).
No ns-3, a abstração básica de um dispositivo computacional é chamada de nó. Essa abstração é representada na classe C++ Node. A classe NodeNode (nó) fornece métodos para gerenciar representações de dispositivos computacionais em simulações.
Você deve entender Nó como um computador ao qual você adicionará funcionalidade. Você adicionará coisas como aplicações, pilhas de protocolos e placas de expansão com drivers, permitindo que o computador execute trabalhos úteis. Usamos o mesmo modelo básico no ns-3.
4.1.2 Application (Aplicação)
Em geral, o software de computador é dividido em duas categorias amplas. O software de sistema organiza vários recursos do computador, como memória, ciclos de CPU, disco, rede, etc., de acordo com algum modelo computacional. O software de sistema normalmente não utiliza esses recursos para executar tarefas que trazem benefícios diretos ao usuário. O usuário, para atingir um objetivo específico, normalmente executa um aplicativo que acessa e utiliza recursos controlados pelo software de sistema.
Frequentemente, a linha de divisão entre software de sistema e software de aplicação é traçada na mudança de nível de privilégios, que ocorre nas armadilhas do sistema operacional. No ns-3, não existe uma verdadeira concepção de sistema operacional e, consequentemente, não há conceitos de níveis de privilégios ou chamadas de sistema. No entanto, temos a ideia de aplicativo. Assim como no 'mundo real', para executar tarefas, os aplicativos de software funcionam em computadores, os aplicativos ns-3 operam em nós ns-3 para controlar simulações no mundo simulado.
No ns-3, a abstração básica para um programa do usuário que gera alguma atividade para simulação é o aplicativo. Essa abstração é representada em C++ pela classe Application. A classe Application fornece métodos para gerenciar em simulações as representações de nossa versão dos aplicativos de nível do usuário. Espera-se que os desenvolvedores especializem a classe Application no sentido da programação orientada a objetos para criar novos aplicativos. Neste guia, usaremos as especializações da classe Application, chamadas de UdpEchoClientApplication e UdpEchoServerApplication. Como era de se esperar, esses aplicativos compõem um conjunto de aplicativos cliente/servidor usados para gerar e simular eco de pacotes de rede.
4.1.3 Channel (Canal)
No mundo real, você pode conectar um computador a uma rede. Muitas vezes, os meios pelos quais os dados são transmitidos nessas redes são chamados de canais. Quando você conecta um cabo Ethernet a uma tomada na parede, está ligando o computador a um canal de comunicação Ethernet. No mundo simulado do ns-3, um nó se conecta a um objeto que representa um canal de comunicação. Aqui, a principal abstração da sub-rede de comunicação é chamada de canal e é representada na classe C++ Channel.
Classe ChannelChannel fornece métodos para gerenciar a interação entre objetos da sub-rede e conectar nós a eles. Os canais também podem ser especializados pelos desenvolvedores em termos de programação orientada a objetos. A especialização de um canal pode modelar algo simples como um fio. Um canal especializado também pode modelar coisas complexas como um grande switch Ethernet ou um espaço tridimensional cheio de obstáculos no caso de redes sem fio.
Neste guia, usaremos versões especializadas do canal chamadas CsmaChannelCsmaChannel, PointToPointChannelPointToPointChannel e WifiChannelWifiChannel. CsmaChannel, por exemplo, modela uma versão da sub-rede de comunicação que implementa um ambiente de comunicação de acesso múltiplo com controle de portadora. Isso nos dá funcionalidade semelhante à do Ethernet.
4.1.4 Net Device (Dispositivo de Rede)
Antigamente, para conectar um computador a uma rede, você precisava comprar um determinado cabo de rede e um dispositivo de hardware chamado (na terminologia de PCs) placa de periférico, que precisava ser instalada no computador. Se a placa de periférico implementasse algumas funções de rede, elas eram chamadas de placas de interface de rede ou cartões de rede. Hoje, a maioria dos computadores vem com hardware de interface de rede integrado, e os usuários não os veem como dispositivos separados.
Um cartão de rede não funcionará sem um driver de software que controla seu hardware. No Unix (ou Linux), parte do hardware é classificada como device. Dispositivos são gerenciados com drivers de dispositivos, e dispositivos de rede (NIC) são gerenciados usando drivers de dispositivos de rede (network device drivers) e têm o termo coletivo de dispositivos de rede (net devices). Em Unix e Linux, você se conecta a dispositivos de rede por nomes como, por exemplo, eth0.
No ns-3, a abstração de dispositivo de rede abrange tanto o driver de software quanto o hardware simulado. Ao simular, o dispositivo de rede é 'instalado' em um nó, permitindo que se conecte a outros nós através de canais. Assim como em um computador real, um nó pode estar ligado a vários canais através de diversos dispositivos. Dispositivos de Rede.
A abstração de rede do dispositivo é representada na classe C++ NetDevice. A classe NetDevice fornece métodos para gerenciar conexões com objetos Node e Channel; e pode ser especializada por desenvolvedores em termos de programação orientada a objetos. Neste guia, usaremos várias versões especializadas de NetDevice chamadas CsmaNetDevice, PointToPointNetDevice. e WifiNetDevice. Assim como o adaptador de rede Ethernet é projetado para operar na rede Ethernet, CsmaNetDevice é projetado para operar na CsmaChannel, PointToPointNetDevice. é projetado para operar na PointToPointChannel, e WifiNetDevice — é projetado para operar na WifiChannel.
4.1.5 Auxiliares Topológicos
Em uma rede real, você encontrará computadores host com placas de rede adicionadas (ou integradas). No ns-3, diríamos que você verá nós com NetDevices conectados. Em uma grande rede simulada, você precisará organizar conexões entre muitos objetos. Nó, NetDevice e Canal.
Como a conexão de NetDevices a nós, NetDevices a canais, atribuição de endereços IP, etc., no ns-3 é uma tarefa comum, para tornar isso o mais simples possível, fornecemos os chamados assistentes topológicos. Por exemplo, para criar um NetDevice, é necessário realizar várias operações do núcleo do ns-3, adicionar um endereço MAC, instalar este dispositivo de rede em um Node, configurar a pilha de protocolos do nó e, em seguida, conectar o NetDevice ao Channel. Operações adicionais serão necessárias para conectar vários dispositivos a canais de múltiplos pontos e depois associar redes individuais em uma rede unificada (Internetworks). Fornecemos objetos auxiliares de topologia que, para seu conforto, combinam essas numerosas operações em um modelo fácil de usar.
4.2 Primeiro Script ns-3
Se você configurou o sistema como sugerido acima, terá um release ns-3 no diretório chamado repos em seu diretório pessoal. Vá para o diretório release
Se você não tem esse diretório, significa que ao compilar o release do ns-3 você não especificou o diretório de saída, realize a compilação assim:
$ .\/waf configure —build-profile=release —out=build\/release,
$ .\/waf build
lá você deve ver uma estrutura de diretório semelhante à 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.pycVá para o diretório examples\/tutorial. Você deve ver um arquivo denominado first.cc. Este é um script que cria uma conexão ponto a ponto entre dois nós e envia um pacote entre eles. Vamos analisar este script linha a linha, abrindo first.cc no seu editor favorito.
4.2.1 Código Boilerplate
A primeira linha do arquivo é a linha do modo do editor emacs. Ela informa ao emacs sobre as convenções de formatação (estilo de codificação) que usaremos no nosso código-fonte.
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */Essa é sempre uma questão bastante controversa, então devemos esclarecer, para imediatamente afastá-la. O projeto ns-3, assim como a maioria dos grandes projetos, adotou um estilo de codificação que deve ser seguido por todo o código fornecido. Se você deseja contribuir com seu código para o projeto, eventualmente terá que se adequar ao padrão de codificação do ns-3, conforme descrito no arquivo doc\/codingstd.txt ou que é mostrado na página do projeto: .
Recomendamos que você se familiarize com a aparência do código do ns-3 e siga esse padrão sempre que trabalhar com nosso código. Toda a equipe de desenvolvedores e colaboradores concordou com isso após alguma relutância. A linha do modo emacs mencionada acima facilita a formatação correta se você utilizar o editor emacs.
O simulador ns-3 é licenciado sob a Licença Pública Geral GNU. Você verá o respectivo cabeçalho legal GNU em cada arquivo de distribuição do ns-3. Frequentemente, você pode ver um aviso de direitos autorais de uma das instituições participantes do projeto ns-3 acima do texto GPL e do autor, mostrado logo abaixo.
/*
* 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 Módulos Plugáveis
O próprio código começa com uma série de diretivas de inclusão (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"Para ajudar nossos usuários de scripts de alto nível a lidarem com a grande quantidade de arquivos de cabeçalho presentes no sistema, agrupamos esses arquivos de acordo com seu uso em grandes módulos. Fornecemos um arquivo de cabeçalho que carregará recursivamente todos os arquivos de cabeçalho utilizados nesse módulo. Em vez de procurar qual cabeçalho você precisa e, possivelmente, obter a lista correta de dependências, oferecemos a você a possibilidade de carregar um grupo de arquivos com um alto grau de detalhe. Este não é o método mais eficiente, mas certamente torna a escrita de scripts muito mais fácil.
Cada um dos arquivos incluídos do ns-3 é colocado em um diretório chamado ns3 (subdiretório de construção), para evitar conflitos de nomes de arquivos durante o processo de construção. O arquivo ns3/core-module.h corresponde ao módulo ns-3, que você encontrará no diretório src/core na versão instalada por você. Na listagem desse diretório, você encontrará uma grande quantidade de arquivos de cabeçalho. Quando você faz a construção, Waf coloca os arquivos de cabeçalho públicos no diretório ns3 em um subdiretório build/debug
Se você não tem esse diretório, significa que ao compilar o release do ns-3 você não especificou o diretório de saída, realize a compilação assim:
$ ./waf configure --build-profile=debug --out=build/debug
$ .\/waf build
ou
$ ./waf configure --build-profile=optimized --out=build/optimized
$ .\/waf build
ou build/optimized, dependendo da sua configuração. Waf também irá gerar automaticamente um arquivo de inclusão do módulo para carregar todos os arquivos de cabeçalho públicos. Como você, é claro, seguiu rigorosamente este guia, você já executou
$ ./waf -d debug --enable-examples --enable-tests configurepara configurar o projeto para compilar builds de depuração, incluindo exemplos e testes. Você também executou
$ ./wafpara compilar o projeto. Então, agora, quando você olhar para o diretório ../../build/debug/ns3, lá, entre outros, você encontrará os arquivos de cabeçalho de quatro módulos mencionados acima. Você pode olhar o conteúdo desses arquivos e descobrir que eles incluem todos os arquivos públicos usados pelos módulos correspondentes.
4.2.3 Espaço de Nomes ns3
A próxima linha no script first.cc é a declaração do espaço de nomes.
using namespace ns3;O projeto ns-3 é implementado no namespace C++, chamado ns3. Isso agrupa todas as declarações relacionadas ao ns-3 fora do espaço de nomes global, o que, esperamos, ajudará na integração com outro código. O uso do operador C++ introduz o namespace ns-3 na região declarativa atual (global). É uma maneira peculiar de dizer que, após essa declaração, você não precisará usar o operador de escopo ns3:: antes de todo o código ns-3 para utilizá-lo. Se você não está familiarizado com namespaces, consulte qualquer manual de C++ e compare o namespace ns3 com o uso do namespace std e as declarações. using namespace std; nos exemplos de uso do operador de saída cout e fluxos.
4.2.4 Registro
A próxima linha do script é
NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");Usaremos esta afirmação como um ponto conveniente para discutir nosso sistema de documentação Doxygen. Se você olhar para o site do projeto ns-3, encontrará um link para 'Documentação' (Documentation) no painel de navegação. Se você clicar nesse link, será direcionado para nossa página de documentação. Há um link para 'Última versão' que o levará à documentação da versão estável mais recente do ns-3. Se você clicar no link 'Documentação da API', você será levado à página da documentação da API do ns-3.
Do lado esquerdo da página, você encontrará uma representação gráfica da estrutura da documentação. Um bom lugar para começar é o 'livro' Modules ns-3 na árvore de navegação do ns-3. Se você expandir Módulos, verá uma lista de documentação dos módulos ns-3. Como discutido anteriormente, o conceito de módulo aqui está diretamente relacionado aos arquivos incluídos no módulo acima. O subsistema de registro (logging) do ns-3 é abordado na seção Uso do módulo de registro, então voltaremos a isso mais adiante neste guia, mas você pode aprender sobre a afirmação acima, consultando o módulo Núcleo, e depois abrindo o livro Ferramentas de depuração, e depois escolhendo a página Logging. Clique em Logging.
Agora você deve revisar a documentação Doxygen do módulo Logging. Na lista de macros no topo da página, você verá uma entrada para NS_LOG_COMPONENT_DEFINE. Antes de seguir o link, consulte a 'Descrição detalhada' do módulo de registro para entender seu funcionamento em geral. Para isso, você pode rolar para baixo ou selecionar 'Mais...' abaixo do diagrama.
Assim que você tiver uma ideia geral do que está acontecendo, prossiga e veja a documentação específica do NS_LOG_COMPONENT_DEFINE. Não vou duplicar a documentação aqui, mas em resumo, essa linha declara um componente de registro chamado FirstScriptExample, que permite ativar e desativar o registro de mensagens no console pelo link do nome.
4.2.5 Função Principal
Nas próximas linhas do script, você verá,
int
main (int argc, char *argv[])
{ Este é apenas um anúncio da função principal do seu programa (script). Assim como em qualquer programa em C++, você precisa definir a função principal, que é executada primeiro. Não há nada de especial aqui. Seu script ns-3 é apenas um programa em C++. A linha seguinte define a resolução de tempo como sendo 1 nanosegundo, que é o valor padrão:
Time::SetResolution (Time::NS);A resolução de tempo, ou simplesmente resolução, é o menor valor de tempo que pode ser utilizado (a menor diferença representável entre dois valores de tempo). Você pode alterar a resolução exatamente uma vez. O mecanismo que fornece essa flexibilidade consome memória, portanto, assim que a resolução é definida explicitamente, liberamos a memória, evitando atualizações futuras. (Se você não definir a resolução explicitamente, o padrão será de um nanosegundo, e a memória será liberada ao iniciar a simulação.)
As duas próximas linhas do script são usadas para habilitar dois componentes de registro que estão embutidos nas aplicações EchoClient e EchoServer:
LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO); LogComponentEnable("UdpEchoServerApplication", LOG_LEVEL_INFO);Se você leu a documentação do componente Logging, verá que existem vários níveis de detalhes de registro que você pode ativar para cada componente. Essas duas linhas de código ativam o registro de depuração no nível INFO para clientes e servidores de eco. Nesse nível, o aplicativo imprimirá mensagens ao enviar e receber pacotes durante a simulação.
Agora, vamos direto à criação da topologia e à execução da simulação. Vamos usar objetos de assistente de topologia para facilitar esse trabalho.
4.2.6 Uso de Auxiliares Topológicos
As duas linhas de código a seguir em nosso script criarão na verdade objetos Node ns-3 que representarão computadores na simulação.
NodeContainer nodes;
nodes.Create (2);Antes de continuarmos, vamos procurar a documentação para a classe NodeContainer. Outra forma de acessar a documentação para esta classe é através da aba Classes nas páginas Doxygen. Se você já tiver o Doxygen aberto, basta rolar para cima até o topo da página e selecionar a aba Classes. Você deve ver um novo conjunto de abas, uma das quais é a lista de classes. Sob essa aba, você verá uma lista de todas as classes ns-3. Role para baixo até ns3::NodeContainer. Quando você encontrar a classe, selecione-a para acessar a documentação da classe.
Como lembramos, uma de nossas abstrações chave é o nó. Ele representa o computador ao qual vamos adicionar coisas como pilhas de protocolo, aplicativos e placas de interface. O assistente de topologia NodeContainer fornece uma maneira conveniente de criar, gerenciar e acessar quaisquer objetos Nó, que criamos para executar a simulação. A primeira linha acima simplesmente declara NodeContainer, que chamamos de nodes. A segunda linha chama o método Create para o objeto nodes e solicita ao container que crie dois nós. Como descrito em Doxygen, o container solicita ao sistema ns-3 a criação de dois objetos Nó e mantém os ponteiros para esses objetos dentro de si.
Os nós criados no script ainda não fazem nada. O próximo passo na construção da topologia é conectar nossos nós à rede. A forma mais simples de rede que suportamos é uma conexão ponto a ponto entre dois nós. Agora, vamos criar tal conexão.
PointToPointHelper
Criamos uma conexão ponto a ponto, agindo de acordo com um padrão familiar, usando um objeto auxiliar topológico para realizar o trabalho de baixo nível necessário para a conexão. Lembremos que nossas duas abstrações principais NetDevice e Canal. No mundo real, esses termos correspondem aproximadamente a placas periféricas e cabos de rede. Geralmente, essas duas coisas estão intimamente ligadas, e ninguém pode contar com a troca, por exemplo, de dispositivos Ethernet via um canal sem fio. Nossos auxiliares topológicos seguem essa ligação estreita e, portanto, você usará um objeto PointToPointHelper para configurar e conectar objetos ns-3 PointToPointNetDevice. e PointToPointChannel. As três linhas seguintes no cenário:
PointToPointHelper pointToPoint;
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));A primeira linha,
PointToPointHelper pointToPoint;cria uma instância do objeto na pilha PointToPointHelper. Do ponto de vista de alto nível, a linha seguinte,
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));diz ao objeto PointToPointHelper usar o valor "5 Mbps" (cinco megabits por segundo) como "DataRate».
De uma forma mais específica, a linha "DataRate" corresponde ao que chamamos de atributo PointToPointNetDevice.. Se você olhar para Doxygen para a classe ns3::PointToPointNetDevice e na documentação do método GetTypeId você encontrará uma lista de atributos definidos para o dispositivo. Entre eles estará o atributo "DataRate". A maioria dos objetos visíveis para o usuário do ns-3 tem listas de atributos semelhantes. Usamos esse mecanismo para uma configuração simples da simulação sem recompilação, como você verá na próxima seção.
Semelhante a "DataRate" no PointToPointNetDevice, você encontrará o atributo "Delay", relacionado ao PointToPointChannel. A linha final,
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));diz PointToPointHelper usa o valor "2 ms" (duas milissegundos) como o valor de atraso de propagação pelo canal ponto a ponto que ele posteriormente cria.
NetDeviceContainer
Até agora, temos no cenário NodeContainer, que contém dois nós. Temos um PointToPointHelper, que está preparado para criar objetos PointToPointNetDevices e conectá-los usando um objeto PointToPointChannel. Assim como usamos um objeto auxiliar de topologia NodeContainer para criar nós, pediremos que PointToPointHelper realize para nós o trabalho de criação, configuração e instalação de nossos dispositivos. Precisamos de uma lista de todos os objetos criados NetDevice, portanto, usamos NetDeviceContainer para armazená-los assim como utilizamos NodeContainer para o armazenamento dos nós que criamos. As duas próximas linhas de código,
NetDeviceContainer devices;
devices = pointToPoint.Install (nodes);concluem a configuração dos dispositivos e do canal. A primeira linha declara o contêiner de dispositivos mencionado acima, enquanto a segunda realiza a tarefa principal. O método Instalar da instalação PointToPointHelper aceita NodeContainer como parâmetro. Dentro NetDeviceContainer para cada nó que está NodeContainer é criado (para a comunicação ponto-a-ponto deve haver exatamente dois) PointToPointNetDevice. é criado e armazenado no contêiner de dispositivos. PointToPointChannel é criado e dois são anexados a ele PointToPointNetDevices. Após a criação dos objetos, os atributos armazenados em PointToPointHelper, são utilizados para inicializar os atributos correspondentes nos objetos criados.
Após a execução da chamada pointToPoint.Install (nodes) teremos dois nós, cada um com um dispositivo de rede 'ponto-a-ponto' instalado e um canal de 'ponto-a-ponto' entre eles. Ambos os dispositivos serão configurados para transmitir dados a uma taxa de cinco megabits por segundo com uma latência de transmissão de dois milissegundos pelo canal.
InternetStackHelper
Agora temos nós e dispositivos configurados, mas nossos nós não têm pilhas de protocolo instaladas. As duas próximas linhas de código cuidarão disso.
InternetStackHelper stack;
stack.Install (nodes);InternetStackHelper — é um assistente topológico para pilhas de internet, assim como PointToPointHelper para dispositivos de rede ponto-a-ponto. O método Instalar recebe NodeContainer como parâmetro. Ao ser executado, ele instalará a pilha de Internet (TCP, UDP, IP, etc.) em cada nó do contêiner.
Ipv4AddressHelper
Em seguida, precisamos vincular nossos dispositivos a endereços IP. Fornecemos um assistente topológico para gerenciar a distribuição de endereços IP. O único API visível ao usuário é a configuração do endereço IP base e da máscara de rede a serem utilizadas ao realizar a distribuição de endereços (isso é feito em um nível mais baixo dentro do assistente). As duas próximas linhas de código em nosso exemplo de script first.cc,
Ipv4AddressHelper address;
address.SetBase ("10.1.1.0", "255.255.255.0");declaram um objeto auxiliar de endereço e dizem-lhe que ele deve começar a alocar endereços IP da rede 10.1.1.0, usando a máscara de sub-rede 255.255.255.0. Por padrão, os endereços alocados começarão em um e aumentarão monotonamente, de modo que o primeiro endereço alocado desta faixa será 10.1.1.1, depois 10.1.1.2, e assim por diante. Na realidade, em um nível baixo, o sistema ns‑3 memoriza todos os endereços IP alocados e gera um erro fatal se você acidentalmente criar uma situação em que o mesmo endereço seja gerado duas vezes (aliás, esse erro é difícil de depurar).
A linha de código a seguir,
Ipv4InterfaceContainer interfaces = address.Assign (devices);executa a atribuição real do endereço. No ns‑3, estabelecemos a conexão entre o endereço IP e o dispositivo, usando o objeto Ipv4Interface. Assim como precisamos às vezes de uma lista de dispositivos de rede criada pelo assistente para uso posterior, também precisamos de uma lista de objetos Ipv4Interface. Ipv4InterfaceContainer fornece essa funcionalidade.
Construímos uma rede ponto a ponto, com pilhas configuradas e endereços IP alocados. Agora precisamos de um aplicativo em cada nó para gerar tráfego.
4.2.7 Uso de Application
Outra das abstrações principais do sistema ns‑3 é Aplicação (aplicativo). Neste cenário, usamos duas especializações da classe base Aplicação ns‑3 chamadas UdpEchoServerApplication e UdpEchoClientApplication. Assim como nos casos anteriores, usamos objetos auxiliares para configurar e gerenciar os objetos base. Aqui, usamos os UdpEchoServerHelper e UdpEchoClientHelperobjetos para facilitar nossa vida.
UdpEchoServerHelper
As linhas de código a seguir em nosso exemplo de script first.cc são usadas para configurar o aplicativo servidor de eco UDP em um dos nós que criamos anteriormente.
UdpEchoServerHelper echoServer (9);
ApplicationContainer serverApps = echoServer.Install (nodes.Get (1));
serverApps.Start (Seconds (1.0));
serverApps.Stop (Seconds (10.0));A primeira linha de código no fragmento acima cria UdpEchoServerHelper. Como de costume, não se trata de um aplicativo em si, mas de um objeto que nos ajuda a criar aplicativos reais. Um de nossos acordos é passar os atributos necessários para o construtor do objeto auxiliar. Nesse caso, o assistente não pode fazer nada útil se não receber o número da porta na qual o servidor aguardará os pacotes, esse número também deve ser conhecido pelo cliente. Neste caso, passamos para o construtor do assistente o número da porta. O construtor, por sua vez, simplesmente executa SetAttribute com o valor passado. Mais tarde, se desejar, usando SetAttribute você poderá definir um outro valor para o atributo ‘Port’.
Assim como muitos outros objetos auxiliares, o objeto UdpEchoServerHelper possui o método Instalar. A execução desse método, na verdade, resulta na criação de um aplicativo básico de eco servidor que é vinculado ao nó. É interessante que o método Instalar aceita NodeContainer recebe como parâmetro, assim como outros Instalar métodos que já vimos.
A conversão implícita de C++, que funciona aqui, aceita o resultado do método node.Get (1) (que retorna um ponteiro inteligente para o objeto nó — Ptr) e o utiliza no construtor para um objeto anônimo NodeContainer, que é então passado para o método Instalar. Se você não consegue determinar no código C++ qual método com qual assinatura será compilado e executado, então busque entre as conversões implícitas.
Agora vemos que echoServer.Install está prestes a instalar o aplicativo UdpEchoServerApplication no encontrado em NodeContainer, que usamos para gerenciar nossos nós, o nó com índice 1. O método Instalar retornará o contêiner que contém ponteiros para todos os aplicativos (neste caso, um, já que passamos um anônimo NodeContainer, contendo um nó) criado pelo assistente.
Os aplicativos precisam especificar o momento de início da geração de tráfego ‘start’ e pode ser necessário especificar também o momento quando deve ser interrompido ‘stop’.Fornecemos ambos os parâmetros. Esses momentos são definidos usando os métodos ApplicationContainer Iniciar e Stop. Esses métodos aceitam parâmetros do tipo Tempo. Neste caso, usamos uma sequência explícita de conversões de C++ para pegar o C++ double 1.0 e transformá-lo em um objeto tns‑3 Time, usando o objeto Seconds para conversão em segundos. Lembre-se de que as regras de conversão podem ser controladas pelo autor do modelo, e C++ possui suas próprias regras, portanto, você nem sempre pode esperar que os parâmetros sejam convertidos como esperado. Duas linhas,
serverApps.Start (Seconds (1.0));
serverApps.Stop (Seconds (10.0));fará com que o aplicativo de eco do servidor seja iniciado (ligado automaticamente) um segundo após o início da simulação e parado (desligado) após dez segundos de simulação. Como declaramos um evento de simulação (evento de parada do aplicativo) que será executado após dez segundos, simulará pelo menos dez segundos de operação da rede.
UdpEchoClientHelper
O aplicativo cliente echo é configurado de forma praticamente idêntica ao servidor. Existe um objeto básico UdpEchoClientApplication, que é gerenciado 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));;No entanto, para o eco-cliente, precisamos definir cinco atributos diferentes. Os dois primeiros atributos são definidos durante a criação UdpEchoClientHelper. Passamos os parâmetros que são usados (internamente pelo helper) para definir os atributos "RemoteAddress" e "RemotePort" de acordo com nosso acordo sobre a transferência dos parâmetros necessários para o construtor do helper.
Lembramos que usamos Ipv4InterfaceContainer para rastrear os endereços IP que atribuirmos aos nossos dispositivos. A interface zero no contêiner de interfaces corresponderá ao endereço IP do nó zero no contêiner de nós. A primeira interface no contêiner de interfaces corresponderá ao endereço IP do primeiro nó no contêiner de nós. Assim, na primeira linha de código (no topo), estamos criando um helper e informamos que o endereço remoto do cliente será o endereço IP atribuído ao nó onde o servidor está localizado. Também informamos que os pacotes devem ser enviados para a porta nove.
O atributo «MaxPackets» informa ao cliente a quantidade máxima de pacotes que podemos enviar durante a simulação. O atributo «Interval» informa ao cliente quanto tempo esperar entre os pacotes, e o atributo «PacketSize» informa ao cliente o tamanho que a carga útil do pacote deve ter. Com essa combinação de atributos, dizemos ao cliente para enviar um pacote de 1024 bytes.
Como no caso do servidor de eco, configuramos os atributos do cliente de eco Iniciar e Stop, mas aqui iniciamos o cliente um segundo após ligar o servidor (duas segundos após o início da simulação).
4.2.8 Simulador
Neste ponto, precisamos iniciar a simulação. Isso é feito através da função global Simulator::Run.
Simulator::Run ();Quando chamamos os métodos anteriormente,
serverApps.Start (Seconds (1.0));
serverApps.Stop (Seconds (10.0));
...
clientApps.Start (Seconds (2.0));
clientApps.Stop (Seconds (10.0));na verdade, agendamos eventos no simulador para 1,0 segundos, 2,0 segundos e dois eventos para 10,0 segundos. Após chamar Simulator::Run, o sistema começará a percorrer a lista de eventos agendados e a executá-los. Primeiro, ele iniciará o evento após 1,0 segundos, ativando o aplicativo do servidor de eco (esse evento pode, por sua vez, agendar muitos outros eventos). Em seguida, ele iniciará o evento programado para t = 2,0 segundos, que iniciará o aplicativo do cliente de eco. Novamente, esse evento pode agendar muitos outros eventos. A implementação do evento de inicialização no cliente de eco iniciará a fase de transmissão de dados da simulação, enviando um pacote para o servidor.
O ato de enviar um pacote para o servidor acionará uma cadeia de eventos que serão agendados automaticamente nos bastidores e que implementarão a mecânica do envio de pacotes de eco de acordo com os parâmetros de sincronização que definimos no cenário.
No final, uma vez que estamos enviando apenas um pacote (lembrando que o atributo MaxPackets foi definido como um), a cadeia de eventos iniciada por essa única solicitação de eco do cliente será finalizada, e a simulação entrará em modo de espera. Assim que isso acontecer, os eventos restantes agendados serão os eventos Stop para o servidor e o cliente. Quando esses eventos forem executados, não haverá mais eventos para processamento e Simulator::Run o controle será retornado. A simulação foi concluída.
Resta apenas limpar. Isso é feito chamando a função global Simulator::Destroy. Como funções de helpers (ou código de baixo nível do ns‑3) foram chamadas, organizadas para que ganchos fossem inseridos no simulador para destruir todos os objetos criados. Você não precisa rastrear nenhum desses objetos sozinho — tudo o que precisava fazer era chamar Simulator::Destroy e sair. O sistema ns‑3 fará esse trabalho árduo por você. As linhas restantes do nosso primeiro script ns‑3, first.cc, fazem exatamente isso:
Simulator::Destroy ();
return 0;
}Quando o simulador vai parar?
ns‑3 é um simulador de eventos discretos (DE). Em um simulador desse tipo, cada evento está associado ao tempo em que ocorre, e a simulação continua processando eventos na ordem em que acontecem. Os eventos podem causar o agendamento de eventos futuros (por exemplo, um temporizador pode se reprogramar para finalizar uma contagem no próximo intervalo).
Os eventos iniciais são geralmente iniciados por um objeto, por exemplo, o IPv6 programará a descoberta de serviços na rede, solicitações de vizinhos, etc. O aplicativo agenda o primeiro evento de envio de pacotes, etc. Quando um evento é processado, ele pode gerar zero, um ou vários eventos. À medida que a simulação avança, os eventos simplesmente se encerram ou geram novos. A simulação para automaticamente se a fila de eventos estiver vazia ou um evento especial for detectado Stop. O evento Stop é gerado pela função Simulator::Stop (parar o tempo).
Há um caso típico em que o Simulator::Stop é absolutamente necessário para interromper a simulação: quando há eventos autossustentáveis. Eventos autossustentáveis (ou recorrentes) são aqueles que sempre se reprogramam. Como consequência, eles mantêm a fila de eventos sempre cheia. Existem muitos protocolos e módulos que contêm eventos recorrentes, por exemplo:
• FlowMonitor — verificação periódica de pacotes perdidos;
• RIPng — divulgação periódica de atualizações da tabela de roteamento;
• etc.
Nesses casos, Simulator::Stop é necessário para parar corretamente a simulação. Além disso, quando o ns‑3 está em modo de emulação, o RealtimeSimulator é usado para sincronizar os relógios da simulação com os da máquina, e Simulator::Stop é necessário para interromper o processo.
Muitos dos programas de simulação no manual não chamam Simulator::Stop claramente, já que eles terminam automaticamente ao esgotar os eventos na fila. No entanto, esses programas também aceitarão a chamada Simulator::Stop. Por exemplo, o seguinte comando adicional no primeiro exemplo do programa agendará uma parada explícita no 11º segundo:
+ Simulator::Stop (Seconds (11.0));
Simulator::Run ();
Simulator::Destroy ();
return 0;
}O mencionado acima não alterará efetivamente o comportamento deste programa, uma vez que essa simulação específica termina naturalmente após 10 segundos. Mas se você alterasse o tempo de parada no comando acima de 11 segundos para 1 segundo, você notaria que a simulação para antes que qualquer saída chegue à tela (uma vez que a saída ocorre aproximadamente após 2 segundos de tempo de simulação).
É importante chamar Simulator::Stop antes de chamar Simulator::Run; caso contrário, Simulator::Run pode nunca retornar o controle ao programa principal para executar a parada!
4.2.9 Montagem do Seu Cenário
Facilitamos a criação de seus scripts simples. Tudo o que você precisa fazer é colocar seu script no diretório scratch, e ele será automaticamente compilado quando você executar Waf. Vamos tentar. Volte para o diretório de nível superior e copie examples/tutorial/first.cc no diretório scratch
$ cd ../..
$ cp examples/tutorial/first.cc scratch/myfirst.ccAgora compile seu primeiro exemplo de script usando waf:
$ ./wafVocê deve ver mensagens indicando que seu primeiro exemplo foi criado com sucesso.
Waf: Entrando no diretório `/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: Saindo do diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finalizado com sucesso (2.357s)Agora você pode executar o exemplo (note que se você compilar seu programa no diretório scratch, então você também deve executá-lo a partir de scratch):
$ ./waf --run scratch/myfirstVocê deve ver uma saída semelhante:
Waf: Entrando no diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Saindo do diretório `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finalizado com sucesso (0.418s) Enviado 1024 bytes para 10.1.1.2
Recebido 1024 bytes de 10.1.1.1
Recebido 1024 bytes de 10.1.1.2Aqui você vê que o sistema de compilação verifica se o arquivo foi construído e, em seguida, o executa. Você percebe no registro do cliente de eco que ele enviou um pacote de 1024 bytes para o servidor de eco 10.1.1.2. Você também pode ver o componente de registro no servidor de eco, que indica que recebeu 1024 bytes de 10.1.1.1. O servidor de eco repete o pacote em silêncio, e você vê no registro do cliente de eco que ele recebeu seu pacote de volta do servidor.
4.3 Código Fonte do ns-3
Agora que você usou alguns dos assistentes do ns-3, pode dar uma olhada em alguns códigos fonte que implementam essa funcionalidade. O código mais recente pode ser visualizado em nosso servidor web pelo seguinte link: . Lá você verá uma página de resumo do Mercurial para nossa árvore de desenvolvimento do ns-3. No topo da página, você encontrará alguns links,
summary | shortlog | changelog | graph | tags | filesSiga em frente e selecione o link para arquivos. Aqui está como será o nível superior da maioria de nossos repositórios:
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 | annotateNossos exemplos de scripts estão no diretório examples. Se você clicar nos exemplos, verá uma lista de subdiretórios. Um dos arquivos na subdiretório tutorial — first.cc. Se você clicar em first.cc você verá o código que acabou de estudar.
O código fonte está principalmente no diretório src. Você pode visualizar o código-fonte clicando no nome do diretório ou no link dos arquivos à direita do nome do diretório. Se você clicar no diretório src, verá uma lista de subdiretórios src. Se você clicar na subdiretório core, você encontrará uma lista de arquivos. O primeiro arquivo que você verá (no momento da redação deste guia) — abort.h. Se você clicar no link abort.h, você será enviado para o arquivo fonte para abort.h, que contém macros úteis para sair de scripts se condições anormais forem detectadas. O código-fonte para os auxiliares que usamos neste capítulo pode ser encontrado no diretório src/Applications/helper. Sinta-se à vontade para explorar a árvore de diretórios para entender o que está onde e se familiarizar com o estilo de programação do ns‑3.
Fonte: habr.com
