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

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

5 Configuración
5.1 Uso do módulo de rexistro
5.1.1 Visión xeral do rexistro
5.1.2 Activar o rexistro
5.1.3 Engadir rexistro ao seu código
5.2 Usando argumentos da liña de comandos
5.2.1 Anulación dos valores de atributos predeterminados
5.2.2 Capturar os seus propios comandos
5.3 Utilización do sistema de trazado
5.3.1 Rastrexo ASCII
Analizando trazos ASCII
5.3.2 Rastrexo PCAP

Capítulo 5

axuste

5.1 Uso do módulo de rexistro

Xa analizamos brevemente o módulo de rexistro ns-3 mirando o script primeiro.cc. Neste capítulo, analizaremos os posibles usos do subsistema de rexistro.

5.1.1 Visión xeral do rexistro

Moitos sistemas grandes admiten algún tipo de instalación de rexistro de mensaxes, e ns-3 non é unha excepción. Nalgúns casos, só se escriben mensaxes de erro na "consola do operador" (que normalmente é stderr nos sistemas baseados en Unix). Noutros sistemas, pódense mostrar mensaxes de aviso e información máis detallada. Nalgúns casos, utilízanse ferramentas de rexistro para emitir mensaxes de depuración que poden desenfocar rapidamente a saída.

O subHRD usado en ns-3 asume que todos estes niveis de contido de información son útiles e ofrecemos un enfoque selectivo e en capas para o rexistro de mensaxes. O rexistro pódese desactivar por completo, activarse por compoñente ou de forma global. Para este fin, utilízanse niveis axustables de contido de información. O módulo de rexistro ns-3 ofrece un xeito relativamente sinxelo de obter información útil da súa simulación.

Debes entender que ofrecemos un mecanismo de propósito xeral - o rastrexo - para extraer datos dos teus modelos, que debería ser a saída preferida para as simulacións (para obter máis información sobre o noso sistema de rastrexo, consulta a sección 5.3 do titorial). O rexistro debe ser o método preferido para obter información de depuración, avisos, mensaxes de erro ou para emitir rapidamente mensaxes dos teus scripts ou modelos en calquera momento.

Actualmente, o sistema define sete niveis (tipos) de mensaxes de rexistro en orde crecente de contido de información.

  • LOG_ERROR - mensaxes de erro de rexistro (macro relacionada: NS_LOG_ERROR);
  • LOG_WARN - Rexistra as mensaxes de aviso (macro relacionada: NS_LOG_WARN);
  • LOG_DEBUG - Rexistra mensaxes especiais de depuración relativamente raras (macro relacionada: NS_LOG_DEBUG);
  • LOG_INFO - rexistro de mensaxes de información sobre o progreso do programa (macro relacionada: NS_LOG_INFO);
  • LOG_FUNCTION: rexistra mensaxes que describen cada función chamada (dúas macros relacionadas: NS_LOG_FUNCTION, usada para funcións membros, e NS_LOG_FUNCTION_NOARGS, usada para funcións estáticas);
  • LOG_LOGIC - mensaxes de rexistro que describen o fluxo lóxico dentro dunha función (macro relacionada: NS_LOG_LOGIC);
  • LOG_ALL: rexistra todo o mencionado anteriormente (sen macro asociada).
    Para cada tipo (LOG_TYPE) tamén hai un LOG_LEVEL_TYPE que, se se usa, permite rexistrar todos os niveis superiores ademais do seu propio nivel. (Como consecuencia, LOG_ERROR e LOG_LEVEL_ERROR, e LOG_ALL e LOG_LEVEL_ALL son funcionalmente equivalentes.) Por exemplo, activar LOG_INFO só permitirá mensaxes proporcionadas pola macro NS_LOG_INFO, mentres que activar LOG_LEVEL_INFO tamén incluirá mensaxes proporcionadas polas macros NS_LOGNS_DE_W_LOGARN, e ERROR_NS_W_GARN.

Tamén ofrecemos unha macro de rexistro incondicional que sempre se mostra, independentemente do nivel de rexistro ou do compoñente de selección.

  • NS_LOG_UNCOND: rexistro incondicional da mensaxe asociada (sen nivel de rexistro asociado).

Cada nivel pódese consultar individualmente ou de forma acumulativa. O rexistro pódese configurar usando a variable de ambiente sh NS_LOG ou rexistrando unha chamada de función do sistema. Como se mostrou anteriormente, o sistema de rexistro ten documentación de Doxygen e agora é un bo momento para revisalo se aínda non o fixeches.

Agora que leu a documentación con moito detalle, usemos ese coñecemento para obter información interesante do script de exemplo scratch/myfirst.ccque xa recompilaches.

5.1.2 Activar o rexistro

Usemos a variable de ambiente NS_LOG para executar algúns rexistros máis, pero primeiro, só para orientarse, execute o último script como fixera anteriormente,

$ ./waf --run scratch/myfirst

Debería ver a saída coñecida do primeiro programa de exemplo ns-3

$ 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.413s)
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
Received 1024 bytes from 10.1.1.2

Resulta que as mensaxes "enviadas" e "recibidas" que ves arriba son en realidade mensaxes rexistradas de Aplicación UdpEchoClient и Aplicación UdpEchoServer. Por exemplo, podemos pedirlle á aplicación cliente que imprima información adicional configurando o seu nivel de rexistro mediante a variable de ambiente NS_LOG.

A partir de agora, vou asumir que está a usar un shell tipo sh que usa a sintaxe "VARIABLE=valor". Se está a usar un shell de tipo csh, entón terá que converter os meus exemplos á sintaxe "setenv variable value" requirida por esas shells.

Actualmente, a aplicación cliente de eco UDP responde á seguinte liña de código en scratch/myfirst.cc,

LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);

Activa o nivel de rexistro LOG_LEVEL_INFO. Cando pasamos unha marca de nivel de rexistro, en realidade activamos ese nivel e todos os niveis inferiores. Neste caso, activamos NS_LOG_INFO, NS_LOG_DEBUG, NS_LOG_WARN e NS_LOG_ERROR. Podemos aumentar o nivel de rexistro e obter máis información, sen cambios de script e recompilación, configurando a variable de ambiente NS_LOG do seguinte xeito:

$ export NS_LOG=UdpEchoClientApplication=level_all

Entón, establecemos a variable de shell sh NS_LOG co seguinte valor,

UdpEchoClientApplication=level_all

O lado esquerdo da asignación é o nome do compoñente rexistrado que queremos configurar, e o lado dereito é a bandeira que queremos aplicar para el. Neste caso, imos habilitar todos os niveis de depuración na aplicación. Se executas o script con NS_LOG configurado deste xeito, o sistema de rexistro ns-3 aceptará os cambios e deberías ver a seguinte saída:

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.404s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
Received 1024 bytes from 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

A información adicional de depuración proporcionada pola aplicación está agora no nivel NS_LOG_FUNCTION. Mostra cada instancia dunha chamada de función durante a execución do script. Como regra xeral, nas funcións do método é preferible utilizar (como mínimo)NS_LOG_FUNCTION (this). Use NS_LOG_FUNCTION_NOARGS ()
só en funcións estáticas. Non obstante, teña en conta que o sistema ns-3 non é necesario para soportar ningunha función de rexistro. A decisión sobre a cantidade de información que se rexistra déixase ao desenvolvedor do modelo individual. No caso das aplicacións de eco, hai unha gran cantidade de saída de rexistro dispoñible.

Agora podes ver un rexistro das chamadas de funcións realizadas pola aplicación. Se miras detidamente, notarás un dous puntos entre a liña Aplicación UdpEchoClient e o nome do método, onde pode esperar ver o operador de ámbito C++ (: :). Isto é intencionado.

Este non é realmente o nome da clase, senón o nome do compoñente de rexistro. Cando hai unha coincidencia entre un ficheiro fonte e unha clase, adoita ser o nome da clase, pero debes entender que non é realmente o nome da clase e que hai un só dous puntos en lugar de dous dous. Esta é unha forma de axudarche a separar conceptualmente o nome do bean de rexistro do nome da clase dunha forma relativamente sutil.

Non obstante, nalgúns casos pode ser difícil determinar que método está a xerar realmente a mensaxe de rexistro. Se miras o texto anterior, podes estarte preguntando onde está a liña "Received 1024 bytes from 10.1.1.2" Podes resolver este problema configurando o nivel función_prefixo á variable de ambiente NS_LOG. Proba o seguinte:

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func'

Teña en conta que as comiñas son necesarias porque a barra vertical que usamos para representar a operación OR tamén é un conector de tubo Unix. Agora, se executa o script, verá que o sistema de rexistro garante que cada mensaxe dun rexistro determinado teña o prefixo do nome do compoñente.

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.417s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

Agora podes ver que todas as mensaxes procedentes da aplicación cliente de eco UDP están identificadas como tales. Mensaxe "Received 1024 bytes from 10.1.1.2" agora está claramente identificado como procedente da aplicación cliente echo. A mensaxe restante debe proceder da aplicación do servidor de eco UDP. Podemos activar este compoñente introducindo unha lista de compoñentes separados por dous puntos na variable de ambiente NS_LOG.

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func:
               UdpEchoServerApplication=level_all|prefix_func'

Aviso: no texto de exemplo anterior, terá que eliminar o carácter de nova liña despois dos dous puntos (:), que se usa para formatar o documento. Agora, se executa o script, verá todas as mensaxes de rexistro das aplicacións de eco cliente e servidor. Podes ver que isto pode ser moi útil ao depurar.

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.406s)
UdpEchoServerApplication:UdpEchoServer()
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoServerApplication:StartApplication()
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
UdpEchoServerApplication:HandleRead(): Echoing packet
UdpEchoClientApplication:HandleRead(0x624920, 0x625160)
UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
UdpEchoServerApplication:StopApplication()
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Tamén ás veces é útil poder ver a hora de simulación na que se xerou a mensaxe de rexistro. Podes facelo engadindo o bit OU tempo_prefixo:

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func|prefix_time: UdpEchoServerApplication=level_all|prefix_func|prefix_time'

De novo, terás que eliminar o carácter de nova liña anterior. Se agora executas o script deberías ver a seguinte saída:

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)
0s UdpEchoServerApplication:UdpEchoServer()
0s UdpEchoClientApplication:UdpEchoClient()
0s UdpEchoClientApplication:SetDataSize(1024)
1s UdpEchoServerApplication:StartApplication()
2s UdpEchoClientApplication:StartApplication()
2s UdpEchoClientApplication:ScheduleTransmit()
2s UdpEchoClientApplication:Send()
2s UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Echoing packet
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Teña en conta que o construtor para UdpEchoServer foi chamado durante a simulación 0 segundos. Isto ocorre realmente antes de que comece a simulación, pero o tempo móstrase como cero segundos. O mesmo ocorre coa mensaxe do constructor UdpEchoClient.

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)
0s UdpEchoServerApplication:UdpEchoServer()
0s UdpEchoClientApplication:UdpEchoClient()
0s UdpEchoClientApplication:SetDataSize(1024)
1s UdpEchoServerApplication:StartApplication()
2s UdpEchoClientApplication:StartApplication()
2s UdpEchoClientApplication:ScheduleTransmit()
2s UdpEchoClientApplication:Send()
2s UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Echoing packet
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Lembremos que o guión cero/primeiro.cc iniciou a aplicación do servidor de eco un segundo antes do inicio da simulación. Agora podes ver que o método Iniciar a aplicación o servidor é realmente chamado no primeiro segundo. Tamén pode notar que o cliente de eco comeza no segundo segundo da simulación, como pedimos no guión.

Agora podes seguir o progreso da simulación durante a chamada ScheduleTransmit no cliente que chama a devolución de chamada HandleRead Enviar na aplicación do servidor de eco. Teña en conta que o tempo transcorrido para enviar un paquete a través dunha ligazón punto a punto é de 3,69 milisegundos. Podes ver que o servidor de eco rexistra unha mensaxe de que respondeu ao paquete e, despois dun atraso de canle, verás que o cliente de eco recibe o paquete de eco no seu método HandleRead.

Nesta simulación, suceden moitas cousas sen que te decates. Pero pode seguir todo o proceso moi facilmente activando todos os compoñentes de rexistro no sistema. Tenta establecer a variable NS_LOG co seguinte valor:

$ export 'NS_LOG=*=level_all|prefix_func|prefix_time'

O asterisco anterior é un carácter comodín para o compoñente de rexistro. Isto incluirá todas as entradas en todos os compoñentes utilizados na simulación. Non vou reproducir a saída aquí (no momento de escribir produce 1265 liñas de saída para un único paquete de eco), pero podes redirixir esta información a un ficheiro e visualizala no teu editor favorito.

$ ./waf --run scratch/myfirst > log.out 2>&1

Eu persoalmente uso esta versión extremadamente detallada do rexistro cando teño un problema e non teño idea de onde saíu mal as cousas. Podo seguir a execución do código con bastante facilidade sen establecer puntos de interrupción e pasar polo código no depurador. Podo editar a saída no meu editor favorito e buscar o que espero e ver que ocorre algo que non esperaba. Unha vez que teño unha idea xeral do que está a fallar, entro ao depurador para analizar o problema. Este tipo de saída pode ser especialmente útil cando o teu script fai algo completamente inesperado. Se só usas o depurador, podes perderte un xiro por completo. O rexistro fai que se noten tales xiros.

5.1.3 Engadir rexistro ao seu código

Podes engadir novas entradas ás túas simulacións facendo chamadas ao compoñente de rexistro desde varias macros. Imos facelo nun guión myfirst.cc, que temos no directorio "limpio". Lembre que definimos un compoñente de rexistro neste escenario:

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

Sabes que pode activar o rexistro de todas as mensaxes deste compoñente configurando a variable de ambiente NS_LOG en diferentes niveis. Imos adiante e engadir algunhas entradas ao guión. A macro utilizada para engadir mensaxes de nivel de información ao rexistro é NS_LOG_INFO. Engademos unha mensaxe (xusto antes de comezar a crear nodos) que che indica que o script está na fase de "Creación de topoloxía". Isto faise no seguinte fragmento de código,
Abre scratch/myfirst.cc no teu editor favorito e engade a liña,
NS_LOG_INFO ("Creating Topology");
xusto antes das liñas,

NodeContainer nodes;
nodes.Create (2);

Agora compila o script usando waf, e borre a variable NS_LOG para desactivar o fluxo de rexistro que activamos anteriormente:

$ ./waf
$ export NS_LOG=
Теперь, если вы запустите скрипт,
$ ./waf --run scratch/myfirst

Non verá a nova mensaxe porque non se activou o compoñente de rexistro asociado (FirstScriptExample). Para ver a túa mensaxe, debes activar o compoñente de rexistro FirstScriptExample cun nivel non inferior a NS_LOG_INFO. Se só queres ver este nivel de rexistro específico, podes activalo así,

$ export NS_LOG=FirstScriptExample=info

Se executa o script agora, verá unha nova mensaxe "Creando topoloxía",

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.404s)
Creating Topology
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
Received 1024 bytes from 10.1.1.2

5.2 Usando argumentos da liña de comandos

5.2.1 Anulación dos valores de atributos predeterminados

Outra forma de cambiar o comportamento dos scripts ns-3 sen editar nin construír é usar argumentos da liña de comandos. Ofrecemos un mecanismo para analizar argumentos da liña de comandos e establecer automaticamente variables locais e globais en función dos resultados.

O primeiro paso para usar o sistema de argumentos da liña de comandos é declarar un analizador de liña de comandos. Isto é bastante sinxelo de facer (no teu programa principal), como no seguinte código,

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

Este sinxelo fragmento de dúas liñas é realmente moi útil por si mesmo. Abre a porta ao sistema de atributos e variables globais ns-3. Imos engadir dúas liñas de código ao comezo da función de script principal scratch/myfirst.cc. Seguindo, compilamos o script e executámolo, ao executar realizamos unha solicitude de axuda do seguinte xeito:

$ ./waf --run "scratch/myfirst --PrintHelp"

Este comando preguntará Waf executar script scratch/myfirst e páselle un argumento de liña de comandos - Imprimir Axuda. As comiñas son necesarias para indicar a que programa está destinado o argumento. O analizador da liña de comandos detectará o argumento - Imprimir Axuda e mostrará a resposta,

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.413s)
TcpL4Protocol:TcpStateMachine()
CommandLine:HandleArgument(): Handle arg name=PrintHelp value=
--PrintHelp: Print this help message.
--PrintGroups: Print the list of groups.
--PrintTypeIds: Print all TypeIds.
--PrintGroup=[group]: Print all TypeIds of group.
--PrintAttributes=[typeid]: Print all attributes of typeid.
--PrintGlobals: Print the list of globals.

Agora vexamos a opción - Atributos de impresión. Xa mencionamos o sistema de atributos ns-3 cando estudamos o primeiro script.cc. Vimos as seguintes liñas de código,

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

e dixeron iso Taxa de datos é en realidade un atributo PointToPointNetDevice. Usemos o analizador de argumentos da liña de comandos para ver os atributos PointToPointNetDevice. A lista de axuda di o que debemos proporcionar TypeId. Este é o nome da clase á que pertencen os atributos de interese. No noso caso será ns3::PointToPointNetDevice. Sigamos avanzando, entra,

$ ./waf --run "scratch/myfirst --PrintAttributes=ns3::PointToPointNetDevice"

O sistema imprimirá todos os atributos deste tipo de dispositivo de rede. Verás que entre os atributos da lista están,

--ns3::PointToPointNetDevice::DataRate=[32768bps]:
The default data rate for point to point links

Este é o valor predeterminado que utilizará o sistema ao crear o obxecto PointToPointNetDevice. Anularemos este valor predeterminado mediante o parámetro atributo в PointToPointHelper máis alto. Usemos os valores predeterminados para dispositivos e canles punto a punto. Para iso, eliminaremos as chamadas SetDeviceAttribute и SetChannelAttribute de myfirst.cc, que temos nun directorio limpo.

Agora o teu script debería simplemente declaralo PointToPointHelper e non realice ningunha operación de instalación como se mostra no seguinte exemplo,

...
NodeContainer nodes;
nodes.Create (2);
PointToPointHelper pointToPoint;
NetDeviceContainer devices;
devices = pointToPoint.Install (nodes);
...

Continúa e crea un novo script con Waf (./waff) e volvamos e incluímos algunha entrada da aplicación do servidor de eco UDP e incluímos o prefixo de tempo.

$ export 'NS_LOG=UdpEchoServerApplication=level_all|prefix_time'

Se executas o script deberías ver a seguinte saída:

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.405s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.25732s Received 1024 bytes from 10.1.1.1
2.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Lembre que a última vez que miramos o tempo de simulación, no momento en que o servidor de eco recibiu o paquete, foi de 2,00369 segundos.

2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1

Agora recibe o paquete en 2.25732 segundos. Isto débese a que simplemente restablecemos a taxa de datos de PointToPointNetDevice de cinco megabits por segundo ao valor predeterminado, que é de 32768 bits por segundo. Se substituímos un novo DataRate mediante a liña de comandos, poderiamos acelerar de novo a nosa simulación. Farémolo do seguinte xeito, segundo a fórmula que implica o elemento de axuda:

$ ./waf --run "scratch/myfirst --ns3::PointToPointNetDevice::DataRate=5Mbps"

Isto devolverá o atributo DataRate ao seu valor predeterminado de cinco megabits por segundo. Sorprende o resultado? Resulta que para devolver o comportamento orixinal do guión, tamén necesitamos configurar o atraso da canle para que coincida coa velocidade da luz. Podemos pedirlle ao sistema de liña de comandos que imprima os atributos da canle, igual que fixemos co dispositivo de rede:

$ ./waf --run "scratch/myfirst --PrintAttributes=ns3::PointToPointChannel"

Descubriremos que o atributo de retardo da canle está definido como segue:

--ns3::PointToPointChannel::Delay=[0ns]:
Transmission delay through the channel

Podemos entón, a través do sistema de liña de comandos, establecer estes dous valores predeterminados.

$ ./waf --run "scratch/myfirst
--ns3::PointToPointNetDevice::DataRate=5Mbps
--ns3::PointToPointChannel::Delay=2ms"

neste caso restauramos o tempo que tiñamos cando definimos explícitamente o DataRate e o Delay no script:

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.417s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.00369s Received 1024 bytes from 10.1.1.1
2.00369s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Teña en conta que o servidor recibe de novo o paquete despois de 2,00369 segundos. En realidade, poderiamos establecer calquera dos atributos utilizados no script deste xeito. En particular, poderiamos establecer os atributos de MaxPackets en valores que non sexan un UdpEchoClient.

Como o usarías? Inténtalo. Lembra que debes comentar o lugar onde anulamos o valor do atributo predeterminado e establecemos explícitamente MaxPackets no guión. Despois debes reconstruír o script. Tamén pode usar a liña de comandos para obter axuda de sintaxe para establecer un novo valor de atributo predeterminado. Unha vez que entenda isto, pode controlar o número de paquetes que se mostran na liña de comandos. Xa que somos persoas estudosas, a nosa liña de comandos debería verse así:

$ ./waf --run "scratch/myfirst
--ns3::PointToPointNetDevice::DataRate=5Mbps
--ns3::PointToPointChannel::Delay=2ms
--ns3::UdpEchoClient::MaxPackets=2"

A pregunta natural que xorde neste punto é como saber sobre a existencia de todos estes atributos. De novo, o sistema de liña de comandos ten unha función de axuda para este asunto. Se pedimos axuda á liña de comandos, deberíamos ver:

$ ./waf --run "scratch/myfirst --PrintHelp"
myfirst [Program Arguments] [General Arguments]
General Arguments:
--PrintGlobals: Print the list of globals.
--PrintGroups: Print the list of groups.
--PrintGroup=[group]: Print all TypeIds of group.
--PrintTypeIds: Print all TypeIds.
--PrintAttributes=[typeid]: Print all attributes of typeid.
--PrintHelp: Print this help message.

Se selecciona o argumento "PrintGroups" debería ver unha lista de todos os grupos rexistrados TypeId. Os nomes dos grupos son consistentes cos nomes dos módulos do directorio fonte (aínda que en maiúscula). Imprimir toda a información á vez sería demasiado voluminoso, polo que hai un filtro adicional dispoñible para imprimir información por grupo. Entón, centrándonos de novo no módulo punto a punto:

./waf --run "scratch/myfirst --PrintGroup=PointToPoint"
TypeIds in group PointToPoint:
ns3::PointToPointChannel
ns3::PointToPointNetDevice
ns3::PointToPointRemoteChannel
ns3::PppHeader

Aquí podes atopar nomes de TypeId dispoñibles para buscas de atributos, por exemplo en
--PrintAttributes = ns3 :: PointToPointChannelcomo se mostra arriba.

Outra forma de coñecer os atributos é a través de Doxygen ns-3. Hai unha páxina que enumera todos os atributos rexistrados no simulador.

5.2.2 Capturar os seus propios comandos

Tamén pode engadir os seus propios ganchos a través do sistema de liña de comandos. Isto faise de forma sinxela usando o método analizador da liña de comandos EngadirValor.
Usemos esta función para especificar o número de paquetes que se mostrarán dun xeito completamente diferente. Imos engadir unha variable local chamada nPaquetes nunha función Inicio. Axustarémolo a un para que coincida co noso comportamento predeterminado anterior. Para permitir que o analizador da liña de comandos cambie este valor, necesitamos capturar este valor no analizador. Facemos isto engadindo unha chamada EngadirValor. Vaia e cambia o guión scratch/myfirst.cc entón para comezar co seguinte código,

int
main (int argc, char *argv[])
{
uint32_t nPackets = 1;
CommandLine cmd;
cmd.AddValue("nPackets", "Number of packets to echo", nPackets);
cmd.Parse (argc, argv);
...

Desprácese cara abaixo ata o punto do script onde establecemos o atributo MaxPackets e cambialo para que estea configurado na variable nPackets en lugar da constante 1, como se mostra a continuación.

echoClient.SetAttribute ("MaxPackets", UintegerValue (nPackets));

Agora, se executa o script e proporciona o argumento -PrintHelp, debería ver o novo argumento de usuario. listado na pantalla de axuda. Entra,

$ ./waf --run "scratch/myfirst --PrintHelp"
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.403s)
--PrintHelp: Print this help message.
--PrintGroups: Print the list of groups.
--PrintTypeIds: Print all TypeIds.
--PrintGroup=[group]: Print all TypeIds of group.
--PrintAttributes=[typeid]: Print all attributes of typeid.
--PrintGlobals: Print the list of globals.
User Arguments:
--nPackets: Number of packets to echo

Se queres cambiar o número de paquetes transmitidos, podes facelo configurando o argumento da liña de comandos -nPackets.

$ ./waf --run "scratch/myfirst --nPackets=2"

Agora deberías ver

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.404s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.25732s Received 1024 bytes from 10.1.1.1
2.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
Sent 1024 bytes to 10.1.1.2
3.25732s Received 1024 bytes from 10.1.1.1
3.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Agora enviaches dous paquetes. Moi sinxelo, non?
Podes ver que, como usuario ns-3, podes usar o sistema de argumentos da liña de comandos para manipular valores e atributos globais. Se es o autor do modelo, pode engadir novos atributos aos seus obxectos e estarán dispoñibles automaticamente para a súa configuración polos usuarios a través do sistema de liña de comandos. Se es un autor de scripts, podes engadir novas variables aos teus scripts e conectalos perfectamente ao teu sistema de liña de comandos.

5.3 Utilización do sistema de trazado

O obxectivo do modelado é xerar resultados para estudos posteriores, e o sistema de rastrexo ns-3 é o mecanismo principal para iso. Dado que ns-3 é un programa en C++, pódense usar medios estándar para xerar saída a partir dun programa C++:

#include <iostream>
...
int main ()
{
...
std::cout << "The value of x is " << x << std::endl;
...
}

Incluso podes usar un módulo de rexistro para engadir unha pequena estrutura á túa solución. Hai moitos problemas coñecidos causados ​​por este enfoque e, polo tanto, proporcionamos un subsistema xeral de rastrexo de eventos para resolver estes problemas.

Os principais obxectivos do sistema de rastrexo ns-3 son:

  • Para tarefas básicas, o sistema de rastrexo debería permitir ao usuario xerar un rastrexo estándar para fontes populares e seleccionar obxectos que xeran o rastrexo;

  • Os usuarios intermedios deberían poder ampliar o sistema de rastrexo para cambiar o formato de saída xerado ou inserir novas fontes de rastrexo, sen modificar o núcleo do simulador;

  • Os usuarios avanzados poden modificar o núcleo do simulador para engadir novas fontes de rastrexo e sumidoiros. O sistema de rastrexo ns-3 baséase nos principios de fontes e receptores de rastrexo independentes, así como nun mecanismo unificado para conectar fontes aos consumidores.

O sistema de rastrexo ns-3 está construído sobre os principios de fontes e receptores de rastrexo independentes, así como un mecanismo unificado para conectar fontes a receptores. As fontes de rastrexo son obxectos que poden sinalar eventos que ocorren na simulación e proporcionar acceso aos datos subxacentes de interese. Por exemplo, unha fonte de rastrexo pode indicar cando un dispositivo de rede recibiu un paquete e poñer o contido do paquete a disposición dos receptores de rastrexo interesados.

As fontes de rastrexo por si mesmas son inútiles a non ser que estean "acopladas" con outras partes do código que realmente fan algo útil coa información proporcionada polo sumidoiro. Os rastreadores son consumidores de eventos e datos proporcionados por fontes de rastrexo. Por exemplo, pode crear un sumidoiro de rastrexo que (cando está conectado á fonte de rastrexo do exemplo anterior) imprimirá as partes de interese no paquete recibido.

A razón para esta separación explícita é permitir aos usuarios conectar novos tipos de sumidoiros a fontes de rastrexo existentes sen ter que editar e recompilar o núcleo do simulador. Polo tanto, no exemplo anterior, o usuario pode definir un novo trazador no seu script e conectalo a unha fonte de traza existente definida no núcleo de simulación só editando o script do usuario.

Neste tutorial, repasaremos algunhas das fontes e sumidoiros predefinidos e mostraremos como se poden configurar co mínimo esforzo por parte do usuario. Consulte o Manual ns-3 ou as seccións de instrucións para obter información sobre a configuración avanzada de rastrexo, incluíndo a expansión do espazo de nomes de rastrexo e a creación de novas fontes de rastrexo.

5.3.1 Rastrexo ASCII

ns-3 ofrece unha funcionalidade auxiliar que proporciona un sistema de rastrexo de baixo nivel para axudarche cos detalles ao configurar rastrexos de paquetes sinxelos. Se activa esta función, verá a saída en ficheiros ASCII. Para aqueles familiarizados coa saída ns-2, este tipo de trazo é semellante a fóra.tr, que é xerado por moitos scripts.

Poñémonos mans á obra e engademos algúns resultados de rastrexo ASCII ao noso script scratch/myfirst.cc. Xusto antes da chamada Simulator :: Run (), engade as seguintes liñas de código:
AsciiTraceHelper ascii;

pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr"));

Como moitos outros modismos ns-3, este código usa un obxecto auxiliar para crear trazos ASCII. A segunda liña contén dúas chamadas de método aniñadas. Método "dentro". CreateFileStream() usa o idioma de obxecto anónimo para crear un obxecto de fluxo de ficheiros na pila (sen un nome de obxecto) e pásao ao método chamado. Afondaremos nisto no futuro, pero todo o que necesitas saber neste momento é que estás creando un obxecto que representa un ficheiro chamado o meu primeiro.tr e transfirao a ns-3. Encomendamos a ns-3 o coidado do obxecto creado durante toda a súa vida útil, durante o cal resolve problemas causados ​​por unha limitación (intencional) pouco coñecida asociada aos construtores de copia de obxectos de fluxo C++.

Chamada externa EnableAsciiAll() dille ao asistente que quere incluír o rastrexo ASCII na súa simulación para todas as conexións de dispositivos punto a punto e que quere que os receptores de rastrexo (especificados) rexistren a información do movemento de paquetes en formato ASCII.

Para aqueles familiarizados con ns-2, os eventos rastrexados son equivalentes aos puntos de rastrexo coñecidos que rexistran eventos "+", "-", "d" e "r".
Agora podes construír o script e executalo desde a liña de comandos:

$ ./waf --run scratch/myfirst

Como moitas veces antes, verás varias mensaxes de Waf e despois "'construír' rematou correctamente" con algunhas mensaxes do programa en execución.

Cando se executa, o programa creará un ficheiro co nome o meu primeiro.tr. Pola natureza da obra Waf, por defecto o ficheiro non se crea no directorio local, senón no directorio de nivel superior do repositorio. Se queres cambiar o camiño onde se gardan os trazos, podes usar o parámetro Waf para especificalo --cwd. Non fixemos isto, polo que para mirar o ficheiro de rastrexo ASCII myfirst.tr no teu editor favorito, teremos que navegar ata o directorio de nivel superior do noso repositorio.

Analizando trazos ASCII

Hai moita información alí nunha forma bastante densa, pero o primeiro que debes notar é que o ficheiro consta de liñas individuais. Isto farase claramente visible se amplía a xanela de visualización.

Cada liña do ficheiro corresponde a un evento de rastrexo. Neste caso, rastrexamos eventos na cola de transmisión presente en cada dispositivo de rede punto a punto na simulación. A cola de transmisión é a cola pola que debe pasar cada paquete para unha ligazón punto a punto. Teña en conta que cada liña do ficheiro de traza comeza cun único carácter (e ten un espazo despois). Este símbolo terá o seguinte significado:

+: produciuse unha operación de cola na cola do dispositivo;
-: produciuse unha operación de recuperación de elementos na cola do dispositivo;
d: o paquete foi eliminado, normalmente porque a cola estaba chea;
r: O paquete foi recibido por un dispositivo de rede.

Vexamos máis de cerca a primeira liña do ficheiro de rastrexo. Desglosarémolo en partes (con sangrías para que se aclare) e o número de liña á esquerda:

0 +
1 2
2 /NodeList/0/DeviceList/0/$ns3::PointToPointNetDevice/TxQueue/Enqueue
3 ns3::PppHeader (
4   Point-to-Point Protocol: IP (0x0021))
6   ns3::Ipv4Header (
7     tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none]
8     length: 1052 10.1.1.1 > 10.1.1.2)
9     ns3::UdpHeader (
10      length: 1032 49153 > 9)
11      Payload (size=1024)

A primeira sección deste evento de rastrexo estendido (liña 0) é a operación. Temos aquí un símbolo +, que corresponde á operación de poñer en cola para a transmisión. A segunda sección (liña 1) é o tempo de simulación, expresado en segundos. Podes lembrar o que preguntamos Aplicación UdpEchoClient comezar a enviar paquetes en dous segundos. Aquí vemos a confirmación de que isto realmente está a suceder.

A seguinte sección do exemplo de rastrexo (da liña 2) mostra que fonte de rastrexo xerou este evento (indicando o rastrexo do espazo de nomes). Podes pensar no espazo de nomes de rastrexo coma se o farías cun espazo de nomes do sistema de ficheiros. A raíz do espazo de nomes é Lista de nodos. Corresponde ao contedor xestionado no código ns-3 principal. Contén todos os nodos que se crean no script. Do mesmo xeito que un sistema de ficheiros pode ter directorios na súa raíz, Lista de nodos podemos ter moitos nodos. Polo tanto, a liña /NodeList/0 refírese ao nodo nulo da NodeList, que normalmente pensamos como "nodo 0". Cada nodo ten unha lista de dispositivos que foron instalados. Esta lista sitúase a continuación no espazo de nomes. Podes ver que procede este evento de rastrexo Lista de dispositivos/0, que é o dispositivo nulo instalado no nodo.

seguinte subcadea, $ ns3 :: PointToPointNetDevice, indica que dispositivo está na posición cero: a lista de dispositivos do nodo cero. Lembre que a operación + atopada na liña 0 significaba que se engadiu un elemento á cola de transmisión do dispositivo. Isto reflíctese nos últimos segmentos do "camiño da pista": TxQueue/Enqueue.

As seccións restantes da traza deben ser bastante intuitivas. As liñas 3-4 indican que o paquete está encapsulado nun protocolo punto a punto. As liñas 5-7 mostran que o paquete ten unha cabeceira de versión IP4 e orixinouse no enderezo IP 10.1.1.1 e está destinado a 10.1.1.2. As liñas 8-9 mostran que este paquete ten unha cabeceira UDP e, finalmente, a liña 10 mostra que a carga útil é dos 1024 bytes esperados.

A seguinte liña do ficheiro de rastrexo mostra que o mesmo paquete foi extraído da cola de transmisión no mesmo nodo.

A terceira liña do ficheiro de rastrexo mostra que o paquete foi recibido por un dispositivo de rede no servidor do servidor de eco. Reproducín o evento a continuación.

0 r
1 2.25732
2 /NodeList/1/DeviceList/0/$ns3::PointToPointNetDevice/MacRx
3   ns3::Ipv4Header (
4     tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none]
5     length: 1052 10.1.1.1 > 10.1.1.2)
6     ns3::UdpHeader (
7       length: 1032 49153 > 9)
8       Payload (size=1024)

Teña en conta que a operación de rastrexo agora é r e que o tempo de simulación aumentou a 2,25732 segundos. Se seguiches o titorial con atención, isto significa que deixaches o DataRate e o Retraso de enlace dos dispositivos de rede nos seus valores predeterminados. Este tempo debería ser familiar, como viches na sección anterior.

A entrada do espazo de nomes de orixe de rastrexo (liña 2) foi modificada para reflectir que este evento se orixina no nodo 1 (/Lista de nodos/1) e o paquete é recibido pola fonte de rastrexo (/MacRx). Debería ser bastante sinxelo seguir o movemento do paquete a través da topoloxía mirando os rastros restantes no ficheiro.

5.3.2 Rastrexo PCAP

Os asistentes de dispositivos ns-3 tamén se poden usar para crear ficheiros de rastrexo en formato .pcap. Acrónimo pcap (normalmente escrito en minúscula) significa captura de paquetes e en realidade é unha API que inclúe definir o formato de ficheiro .pcap. O programa máis popular que pode ler e mostrar este formato é Wireshark (anteriormente chamado Etéreo). Non obstante, hai moitos analizadores de rastrexo de tráfico que usan este formato de paquete. Animamos aos usuarios a utilizar as moitas ferramentas dispoñibles para analizar os rastros de pcap. Neste tutorial centrarémonos en ver trazos de pcap usando tcpdump.

A habilitación do rastrexo pcap faise cunha liña de código.

pointToPoint.EnablePcapAll ("myfirst");

Pega esta liña de código despois do código de rastrexo ASCII que acabamos de engadir scratch/myfirst.cc. Teña en conta que só pasamos a cadea "myfirst", non "myfirst.pcap" nin nada semellante. Isto ocorre porque o parámetro é un prefixo, non un nome de ficheiro completo. Durante a simulación, o asistente creará un ficheiro de rastrexo para cada dispositivo punto a punto. Os nomes dos ficheiros construiranse utilizando o prefixo, o número de nodo, o número de dispositivo e o sufixo ".pcap».

Para o noso script de exemplo, acabaremos vendo ficheiros chamados "myfirst-0-0.pcap"E"myfirst-1-0.pcap", que son trazos pcap para o nodo 0-dispositivo 0 e o nodo 1-dispositivo 0 respectivamente. Unha vez que engade a liña de código para activar o rastrexo de pcap, pode executar o script do xeito habitual:

$ ./waf --run scratch/myfirst

Se miras no directorio de nivel superior da túa distribución, deberías ver tres ficheiros: un ficheiro de rastrexo ASCII o meu primeiro.tr, que estudamos previamente, arquivos myfirst-0-0.pcap и myfirst-1-0.pcap - novos ficheiros pcap que acabamos de xerar.

Lendo a saída con tcpdump

Polo momento, a forma máis sinxela de ver ficheiros pcap é usar tcpdump.

$ tcpdump -nn -tt -r myfirst-0-0.pcap
reading from file myfirst-0-0.pcap, link-type PPP (PPP)
2.000000 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, length 1024
2.514648 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, length 1024
tcpdump -nn -tt -r myfirst-1-0.pcap
reading from file myfirst-1-0.pcap, link-type PPP (PPP)
2.257324 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, length 1024
2.257324 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, length 1024

No vertedoiro myfirst-0-0.pcap (dispositivo cliente) podes ver que o paquete de eco se envía despois de 2 segundos de simulación. Se miras o segundo vertedoiro (myfirst-1-0.pcap), verá que o paquete se recibe en 2,257324 segundos. Verá no segundo volcado que o paquete devólvese en 2.257324 segundos e, finalmente, que o paquete foi recibido de volta polo cliente no primeiro volcado en 2.514648 segundos.

Saída de lectura con Wireshark

Se non está familiarizado Wireshark, hai unha páxina web desde a que podes descargar programas e documentación: http://www.wireshark.org/. Wireshark é unha GUI que se pode usar para mostrar estes ficheiros de rastrexo. Se tes Wireshark, podes abrir calquera dos ficheiros de rastrexo e mostrar o contido coma se capturases os paquetes mediante un detector de paquetes.

Fonte: www.habr.com

Engadir un comentario