
5 Настройка
5.1 Използване на модула за логване
5.1.1 Преглед на логването
5.1.2 Разрешаване на логването
5.1.3 Добавяне на логване към вашия код
5.2 Използване на аргументи от командния ред
5.2.1 Пренасочване на стойностите на атрибутите по подразбиране
5.2.2 Захващане на вашите собствени команди
5.3 Използване на системата за проследяване
5.3.1 ASCII Проследяване
Парсинг на ASCII проследявания
5.3.2 Проследяване на PCAP
Глава 5
Настройка
5.1 Използване на модула за логване
Вече накратко разгледахме модула за логване на ns‑3, преглеждайки скрипта first.cc. В тази глава ще се вгледаме по-внимателно в възможностите за използване на подсистемата за логване.
5.1.1 Преглед на логването
Много големи системи поддържат някакво средство за регистриране на съобщения, а ns‑3 не прави изключение. В някои случаи, в „конзолата на оператора“ (която обикновено е stderr в Unix-базирани системи) се записват само съобщения за грешки. В други системи могат да се показват предупреждаващи съобщения, както и по-подробна информация. В някои случаи, средствата за логване се използват за визуализиране на отладъчни съобщения, които бързо могат да „замъглят“ изхода.
Подходът, използван в ns‑3, предполага, че всички тези нива на информативност са полезни, и ние предлагаме селективен, многостепенен подход за регистриране на съобщения. Логването може да бъде напълно деактивирано, включено за отделни компоненти или в глобален мащаб. Затова служат настраиваеми нива на информативност. Модулът за логване на ns‑3 предлага сравнително прост начин за получаване на полезна информация от вашата симулация.
Трябва да разберете, че предоставяме механизъм за общо предназначение – проследяване – за извличане на данни от вашите модели, който трябва да бъде предпочитан за изход при моделиране (за повече информация за нашата система за проследяване вижте раздел 5.3 от учебника). Логването трябва да бъде предпочитаният метод за получаване на отладъчна информация, предупреждения, съобщения за грешки или бързо извеждане на съобщения от вашите сценарии или модели по всяко време.
В момента в системата са определени седем нива (типа) на съобщенията за лог нарастваща информативност.
- LOG_ERROR – регистриране на съобщения за грешки (свързан макрос: NS_LOG_ERROR);
- LOG_WARN — регистриране на предупреждаващи съобщения (свързаният макрос: NS_LOG_WARN);
- LOG_DEBUG — регистриране на относително редки специални съобщения за отстраняване на грешки (свързаният макрос: NS_LOG_DEBUG);
- LOG_INFO — регистриране на информационни съобщения относно напредъка на програмата (свързаният макрос: NS_LOG_INFO);
- LOG_FUNCTION — регистриране на съобщения, описващи всяка извикана функция (два свързани макроса: NS_LOG_FUNCTION, използван за член-функции, и NS_LOG_FUNCTION_NOARGS, използван за статични функции);
- LOG_LOGIC — регистриране на съобщения, описващи логическия поток в рамките на функцията (свързаният макрос: NS_LOG_LOGIC);
- LOG_ALL — регистриране на всичко споменато по-горе (няма свързан макрос).
За всеки тип (LOG_TYPE) съществува и тип LOG_LEVEL_TYPE, който, ако се използва, позволява регистриране на всички нива над него във връзка със собствения му. (Следователно, LOG_ERROR и LOG_LEVEL_ERROR, както и LOG_ALL и LOG_LEVEL_ALL са функционално еквивалентни.) Например, активирането на LOG_INFO ще разреши само съобщения, предоставени от макроса NS_LOG_INFO, а при активиране на LOG_LEVEL_INFO ще бъдат включени съобщения, предоставени от макросите NS_LOG_DEBUG, NS_LOG_WARN и NS_LOG_ERROR.
Ние също така предлагаме макрос за безусловно регистриране, който се показва винаги, независимо от нивото на логиране или избраната компонента.
- NS_LOG_UNCOND — безусловно регистриране на свързано съобщение (без свързано ниво на регистриране).
Всяко ниво може да бъде запитано поотделно или кумулативно. Логването може да бъде конфигурирано с помощта на променливата на средата sh NS_LOG или чрез регистриране на повикване на системна функция. Както беше показано по-горе, системата за логиране разполага с Doxygen-документация и сега е времето да я прегледате, ако не сте го направили преди.
Сега, когато сте прочели документацията много подробно, нека използваме тези знания, за да получим интересна информация от примера на сценария scratch/myfirst.cc, който вече компилирахте.
5.1.2 Разрешаване на логването
Нека използваме променливата на средата NS_LOG, за да стартираме още няколко логове, но първо, просто за ориентация, стартирайте последния скрипт, както правихте преди,
$ ./waf --run scratch/myfirstТрябва да видите вече познатия изход от първата примерна програма ns-3.
$ Waf: Вход в директорию `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Выхождение из директории `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build` 'build'
завершено успешно (0.413s)
Отправлено 1024 байта на 10.1.1.2
Получено 1024 байта от 10.1.1.1
Получено 1024 байта от 10.1.1.2Оказалось, что «отправленные» и «полученные» сообщения, которые вы видите выше, на самом деле это зарегистрированные сообщения от UdpEchoClientApplication и UdpEchoServerApplication. Например, мы можем попросить клиентское приложение распечатать дополнительную информацию, установив её уровень журналирования через переменную окружения NS_LOG.
С этого момента я собираюсь предположить, что вы используете sh-подобную оболочку, которая использует синтаксис «VARIABLE = value». Если вы используете csh-подобную оболочку, тогда вам придется преобразовать мои примеры в синтаксис «переменная значение setenv», необходимый для этих оболочек.
В данный момент приложение UDP-эко-клиента отвечает на следующую строку кода в scratch/myfirst.cc,
LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);Она активирует уровень журналирования LOG_LEVEL_INFO. Когда мы передаем флаг уровня журналирования, мы на самом деле включаем данный уровень и все более низкие уровни. В данном случае мы включили NS_LOG_INFO, NS_LOG_DEBUG, NS_LOG_WARN и NS_LOG_ERROR. Мы можем повысить уровень журналирования и получить больше информации, без изменений скрипта и перекомпиляции, путем установки переменной окружения NS_LOG следующим образом:
$ export NS_LOG=UdpEchoClientApplication=level_allТаким образом, мы устанавливаем следующее значение переменной NS_LOG в оболочке sh,
UdpEchoClientApplication=level_allЛевая часть присвоения — это имя журналируемого компонента, который мы хотим настроить, а правая часть — это флаг, который мы хотим для этого применить. В данном случае мы собираемся включить в приложении все уровни отладки. Если вы запустите скрипт с установленной таким образом NS_LOG, система журналирования ns‑3 примет изменения, и вы должны увидеть следующий вывод:
Waf: Вход в директорию `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Выхождение из директории `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' завершено успешно (0.404s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
Отправлено 1024 байта на 10.1.1.2
Получено 1024 байта от 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
Получено 1024 байта от 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()Допълнителна информация за отстраняване на грешки, предоставяна от приложението, вече отговаря на нивото NS_LOG_FUNCTION. Тя показва всеки случай на извикване на функции по време на изпълнението на скрипта. Обикновено, в методите на функциите е предпочитано да се използва (поне)NS_LOG_FUNCTION (this). Използвайте NS_LOG_FUNCTION_NOARGS ()
само в статични функции. Въпреки това, имайте предвид, че в системата ns-3 няма изисквания за поддържане на каквато и да е функционалност за журнализиране. Решението за това колко информация да бъде регистрирана остава индивидуално за разработчика на модела. В случай на приложения за ехо е налично голямо количество изходни данни за журнализиране.
Сега можете да прегледате журнала на извикванията на функции, които са направени от приложението. Ако се загледате внимателно, ще забележите двоеточие между реда UdpEchoClientApplication и името на метода, на мястото, където може би сте очаквали да видите оператор за област на видимост C++ (: :). Това е направено намерено.
Всъщност, това не е името на класа, а името на компонента за журнализиране. Когато има съвпадение между изходния файл и класа, обикновено това е името на класа, но трябва да разберете, че всъщност това не е името на класа и там има едно двоеточие вместо двойно двоеточие. Това е начин да ви помогне по относително фин начин концептуално да отделите името на компонента за журнализиране от името на класа.
Въпреки това, в някои случаи може да бъде трудно да се определи кой метод всъщност генерира съобщението за журнала. Ако погледнете текста по-горе, може да имате въпрос откъде идва редът "Received 1024 bytes from 10.1.1.2". Можете да решите този проблем, като зададете нивото prefix_func в променливата na околната среда NS_LOG. Опитайте да направите следното:
$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func'Обърнете внимание, че кавичките са необходими, тъй като вертикалната черта, която използваме за обозначаване на операцията ИЛИ, също е разделител на конвейерите в Unix. Сега, ако стартирате сценария, ще видите, че системата за журнализиране гарантира, че всяко съобщение от дадения журнал има префикс с името на компонента.
Waf: Влизане в директория `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'\nWaf: Излизане от директория `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'\n'build' приключи успешно (0.417s)\nUdpEchoClientApplication:UdpEchoClient()\nUdpEchoClientApplication:SetDataSize(1024)\nUdpEchoClientApplication:StartApplication()\nUdpEchoClientApplication:ScheduleTransmit()\nUdpEchoClientApplication:Send()\nUdpEchoClientApplication:Send(): Изпратени 1024 байта до 10.1.1.2\nПолучени 1024 байта от 10.1.1.1\nUdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)\nUdpEchoClientApplication:HandleRead(): Получени 1024 байта от 10.1.1.2\nUdpEchoClientApplication:StopApplication()\nUdpEchoClientApplication:DoDispose()\nUdpEchoClientApplication:~UdpEchoClient()Сега можете да видите, че всичките съобщения, получени от приложението за UDP ехоклиент, са идентифицирани като такива. Съобщението „Received 1024 bytes from 10.1.1.2“ вече е ясно определено, че идва от приложението за ехоклиент. Останалото съобщение трябва да идва от приложението за UDP ехосървър. Можем да включим този компонент, като въведем списък с компоненти, разделени с двоеточия, в променливата на средата NS_LOG.
$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func:\n UdpEchoServerApplication=level_all|prefix_func'Предупреждение: в горния пример на текста ще трябва да премахнете символа за нов ред след двоеточието (:), той е използван за форматиране на документа. Сега, ако стартирате сценария, ще видите всичките съобщения от регистрационния файл от клиентските и сървърните приложения за ехоклиент. Можете да видите, че това може да бъде много полезно при отстраняване на грешки.
Waf: Влизане в директория `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'\nWaf: Излизане от директория `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'\n'build' приключи успешно (0.406s)\nUdpEchoServerApplication:UdpEchoServer()\nUdpEchoClientApplication:UdpEchoClient()\nUdpEchoClientApplication:SetDataSize(1024)\nUdpEchoServerApplication:StartApplication()\nUdpEchoClientApplication:StartApplication()\nUdpEchoClientApplication:ScheduleTransmit()\nUdpEchoClientApplication:Send()\nUdpEchoClientApplication:Send(): Изпратени 1024 байта до 10.1.1.2\nUdpEchoServerApplication:HandleRead(): Получени 1024 байта от 10.1.1.1\nUdpEchoServerApplication:HandleRead(): Повтаряне на пакета\nUdpEchoClientApplication:HandleRead(0x624920, 0x625160)\nUdpEchoClientApplication:HandleRead(): Получени 1024 байта от 10.1.1.2\nUdpEchoServerApplication:StopApplication()\nUdpEchoClientApplication:StopApplication()\nUdpEchoClientApplication:DoDispose()\nUdpEchoServerApplication:DoDispose()\nUdpEchoClientApplication:~UdpEchoClient()\nUdpEchoServerApplication:~UdpEchoServer()Също така понякога е полезно да видите времето на симулацията, в което е създадено съобщението в регистрационния файл. Можете да направите това, като добавите ИЛИ бит prefix_time:
$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func|prefix_time: UdpEchoServerApplication=level_all|prefix_func|prefix_time'Отново, ще трябва да премахнете символа за нов ред по-горе. Ако сега стартирате сценария, трябва да видите следния изход:
Waf: Вход в директорию `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Выход из директории `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
`build` успешно завершён (0.418с)
0с UdpEchoServerApplication:UdpEchoServer()
0с UdpEchoClientApplication:UdpEchoClient()
0с UdpEchoClientApplication:SetDataSize(1024)
1с UdpEchoServerApplication:StartApplication()
2с UdpEchoClientApplication:StartApplication()
2с UdpEchoClientApplication:ScheduleTransmit()
2с UdpEchoClientApplication:Send()
2с UdpEchoClientApplication:Send(): Отправлено 1024 байта на 10.1.1.2
2.00369с UdpEchoServerApplication:HandleRead(): Получено 1024 байта от 10.1.1.1
2.00369с UdpEchoServerApplication:HandleRead(): Эхо-пакет
2.00737с UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737с UdpEchoClientApplication:HandleRead(): Получено 1024 байта от 10.1.1.2
10с UdpEchoServerApplication:StopApplication()
10с UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()Обърнете внимание, че конструкторът за UdpEchoServer беше извикан по време на симулацията на 0 секунди. В действителност това се случва преди началото на симулацията, но времето се показва като нула секунди. Същото важи и за съобщението на конструктора UdpEchoClient.
Waf: Вход в директорию `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Выход из директории `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
`build` успешно завершён (0.418с)
0с UdpEchoServerApplication:UdpEchoServer()
0с UdpEchoClientApplication:UdpEchoClient()
0с UdpEchoClientApplication:SetDataSize(1024)
1с UdpEchoServerApplication:StartApplication()
2с UdpEchoClientApplication:StartApplication()
2с UdpEchoClientApplication:ScheduleTransmit()
2с UdpEchoClientApplication:Send()
2с UdpEchoClientApplication:Send(): Отправлено 1024 байта на 10.1.1.2
2.00369с UdpEchoServerApplication:HandleRead(): Получено 1024 байта от 10.1.1.1
2.00369с UdpEchoServerApplication:HandleRead(): Эхо-пакет
2.00737с UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737с UdpEchoClientApplication:HandleRead(): Получено 1024 байта от 10.1.1.2
10с UdpEchoServerApplication:StopApplication()
10с UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()Напомням, че скриптът scratch/first.cc стартира приложението за ехо-сървър една секунда преди началото на симулацията. Сега можете да видите, че методът StartApplication на сървъра всъщност е извикан в първата секунда. Също така можете да забележите, че ехо-клиентът стартира на втората секунда от симулацията, както поискахме в скрипта.
Сега можете да следите напредъка на симулацията като извикате ScheduleTransmit в клиента, който извиква Send обратен вик HandleRead в приложението за ехо-сървър. Обърнете внимание, че времето за изпращане на пакет през двупосочна връзка е 3,69 милисекунди. Ясно е, че ехо-сървърът регистрира съобщение за това, че е отговорил на пакета, а след това, след забавянето на канала, виждате, че ехо-клиентът получава ехо-пакета в своя метод HandleRead.
В тази симулация много неща се случват незабелязано за вас. Но може да проследите целия процес, като включите всички компоненти за логване в системата. Опитайте да зададете следната стойност в променливата NS_LOG,
$ export 'NS_LOG=*=level_all|prefix_func|prefix_time'Звездичката по-горе е символ за замяна на компонента за логване. Това ще активира всички записи във всички компоненти, използвани в симулацията. Няма да пресъздавам изхода тук (в момента на написване той генерира 1265 реда изход за един ехо-пакет), но можете да пренасочите тази информация в файл и да я прегледате в любимия си редактор.
$ ./waf --run scratch/myfirst > log.out 2>&1Лично аз използвам тази изключително подробна версия на регистрирането, когато имам проблем и нямам идея къде нещата са се объркали. Мога доста лесно да следя изпълнението на кода, без да поставям точки на спиране и да дебъгвам стъпка по стъпка. Просто мога да редактирам изхода в любимия си редактор и да търся това, което очаквам, и да виждам как се случва нещо неочаквано. Когато имам обща представа за това, което не е наред, преминавам към дебъгера за по-подробно проучване на проблема. Тази форма на изход може да бъде особено полезна, когато вашият скрипт прави нещо напълно неочаквано. Ако разчитате само на дебъгера, можете напълно да пропуснете неочакван обрат. Регистрирането прави такива обрати забележими.
5.1.3 Добавяне на логване към вашия код
Можете да добавите нови записи към вашите симулации, извършвайки извиквания на компонента log от няколко макроса. Нека направим това в сценария myfirst.cc, който имаме в "чиста" директория. Припомнете си, че в този сценарий определихме компонента за регистриране:
NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");Знаете, че можете да включите регистрирането на всички съобщения от този компонент, като зададете променливата среда NS_LOG на различни нива. Нека продължим и добавим някои записи в скрипта. Макросът, използван за добавяне на съобщения на информационно ниво, е NS_LOG_INFO. Нека добавим съобщение (веднага преди да започнем да създаваме възли), което ще ви каже, че сценарият е на етапа на създаване на топология ("Creating Topology"). Това се прави в следния фрагмент от кода,
Отворете scratch/myfirst.cc в любимия ви редактор и добавете ред,
NS_LOG_INFO ("Creating Topology");
точно преди редовете,
NodeContainer nodes;
nodes.Create (2);Сега компилирайте скрипта, използвайки waf, и изчистете променливата NS_LOG, за да деактивирате потока на регистриране, който активирахме по-рано:
$ ./waf
$ export NS_LOG=
Сега, ако стартирате скрипта,
$ ./waf --run scratch/myfirstняма да видите новото съобщение, тъй като свързаният с него компонент за регистриране (FirstScriptExample) не е бил активиран. За да видите вашето съобщение, трябва да активирате компонента за регистриране FirstScriptExample с ниво не по-ниско от NS_LOG_INFO. Ако просто искате да видите това конкретно ниво на регистриране, можете да го включите така,
$ export NS_LOG=FirstScriptExample=infoАко сега стартирате скрипта, ще видите ново съобщение «Създаване на топология»
Waf: Влизане в директорията `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Излизане от директорията `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' завърши успешно (0.404s)
Създаване на топология
Изпратени са 1024 байта до 10.1.1.2
Получени са 1024 байта от 10.1.1.1
Получени са 1024 байта от 10.1.1.25.2 Използване на аргументи от командния ред
5.2.1 Пренасочване на стойностите на атрибутите по подразбиране
Друг начин за промяна на поведението на ns‑3 скриптовете без редактиране и компилиране е използването на аргументи на командния ред. Ние предлагаме механизъм за парсене на аргументите на командния ред и автоматично задаване на локални и глобални променливи въз основа на резултатите.
Първата стъпка в използването на системата за аргументи на командния ред е обявяването на анализатор на командния ред. Това е доста просто да се направи (в основната ви програма), както в следния код:
int
main (int argc, char *argv[])
{
...
CommandLine cmd;
cmd.Parse (argc, argv);
...
}Този прост двуредов фрагмент всъщност е много полезен сам по себе си. Той отваря врата към глобалната променлива на ns‑3 и системата за атрибути. Нека добавим два реда код в началото на основната функция на скрипта scratch/myfirst.cc. След това компилираме скрипта и го стартираме, като при стартирането правим запитване за помощ по следния начин:
$ ./waf --run "scratch/myfirst --PrintHelp"Тази команда ще поиска Waf да стартира скрипта scratch/myfirst и да му предаде аргумент на командния ред --PrintHelp. Кавичките са задължителни, за да покажат за коя програма е предназначен аргументът. Парсерът на командния ред ще открие аргумента --PrintHelp и ще изведе отговор,
Waf: Влизане в директорията `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Излизане от директорията `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' завърши успешно (0.413s)
TcpL4Protocol:TcpStateMachine()
CommandLine:HandleArgument(): Обработен аргумент име=PrintHelp стойност=
--PrintHelp: Изведете това съобщение за помощ.
--PrintGroups: Изведете списъка на групите.
--PrintTypeIds: Изведете всички TypeIds.
--PrintGroup=[група]: Изведете всички TypeIds на групата.
--PrintAttributes=[typeid]: Изведете всички атрибути на typeid.
--PrintGlobals: Изведете списъка на глобалните променливи.Сега разгледайте опцията --PrintAttributes. Вече споменахме системата за атрибути ns‑3, когато разглеждахме скрипта first.cc. Видяхме следните редове код,
PointToPointHelper pointToPoint;
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));и казахме, че DataRate всъщност е атрибут PointToPointNetDevice. Нека приложим анализатора на аргументите на командния ред, за да разгледаме атрибутите PointToPointNetDevice. Списъкът за помощ казва, че трябва да предоставим TypeId. Това е името на класа, към който принадлежат интересуващите ни атрибути. В нашия случай това ще бъде ns3::PointToPointNetDevice. Нека продължим да напредваме, въведете,
$ .\/waf --run "scratch\/myfirst --PrintAttributes=ns3::PointToPointNetDevice"Системата ще отпечата всички атрибути на този тип мрежово устройство. Ще видите, че сред атрибутите в списъка има,
--ns3::PointToPointNetDevice::DataRate=[32768bps]:
Стандартната скорост на данните за точка-точка връзкиТова е стойността по подразбиране, която ще се използва в системата при създаване на обект PointToPointNetDevice. Ние ще преопределим тази стойност по подразбиране с параметъра Атрибут в PointToPointHelper горе. Нека да използваме стойностите по подразбиране за устройствата и каналите за точка-точка. За тази цел ще премахнем извикванията SetDeviceAttribute и SetChannelAttribute от myfirst.cc, който имаме в чистата директория.
Вашият скрипт сега трябва просто да обяви PointToPointHelper и да не извършва никакви операции по настройка, както е показано в примера по-долу,
...
NodeContainer nodes;
nodes.Create (2);
PointToPointHelper pointToPoint;
NetDeviceContainer devices;
devices = pointToPoint.Install (nodes);
...Продължете и създайте нов скрипт с Waf (.\/waf) и нека се върнем назад и включим малко запис от UDP ехо-приложението на сървъра и включим префикса на времето.
$ export 'NS_LOG=UdpEchoServerApplication=level_all|prefix_time'Ако стартирате скрипта, трябва да видите следния изход:
Waf: Влизане в директория `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'\nWaf: Излизане от директория `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'\n'build' завърши успешно (0.405s)\n0s UdpEchoServerApplication:UdpEchoServer()\n1s UdpEchoServerApplication:StartApplication()\nИзпратени 1024 байта до 10.1.1.2\n2.25732s Получени 1024 байта от 10.1.1.1\n2.25732s Повторно изпращане на пакета\nПолучени 1024 байта от 10.1.1.2\n10s UdpEchoServerApplication:StopApplication()\nUdpEchoServerApplication:DoDispose()\nUdpEchoServerApplication:~UdpEchoServer()Нека си припомним, че миналия път, когато разглеждахме времето на симулация, моментът на получаване на пакета от ехо-сервера беше на 2,00369 секунди.
2.00369s UdpEchoServerApplication:HandleRead(): Получени 1024 байта от 10.1.1.1Сега той получава пакета в 2.25732 секунди. Това е, защото просто намалихме скоростта на предаване на PointToPointNetDevice от пет мегабита в секунда до стандартната стойност, която е 32768 бита в секунда. Ако бяхме задали нова DataRate чрез командния ред, можехме да ускорим модела отново. Ще направим това по следния начин, съгласно формулата, подразбираща елемент от помощта:
$ .\/waf --run "scratch\/myfirst --ns3::PointToPointNetDevice::DataRate=5Mbps"В резултат на това стойността на атрибута DataRate по подразбиране ще се върне на пет мегабита в секунда. Учудени ли сте от резултата? Оказва се, че за да върнем първоначалното поведение на сценария, трябва също така да зададем закъснението на канала, съответстващо на скоростта на светлината. Можем да помолим системата за команден ред да отпечата атрибутите на канала, както правихме за мрежовото устройство:
$ .\/waf --run "scratch\/myfirst --PrintAttributes=ns3::PointToPointChannel"Ще открием, че атрибутът за закъснение на канала е зададен по следния начин:
--ns3::PointToPointChannel::Delay=[0ns]:
Забавяне при предаване през каналаСлед това можем чрез системата за команден ред да зададем и двете стойности по подразбиране,
$ .\/waf --run "scratch\/myfirst
--ns3::PointToPointNetDevice::DataRate=5Mbps
--ns3::PointToPointChannel::Delay=2ms"в този случай възстановяваме времето, което имахме, когато изрично зададохме DataRate и Delay в сценария:
Waf: Влизане в директория `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
Waf: Напускане на директория `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
'build' завърши успешно (0.417s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Изпратени 1024 байта до 10.1.1.2
2.00369s Получени 1024 байта от 10.1.1.1
2.00369s Преекспедиране на пакета
Получени 1024 байта от 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()Обърнете внимание, че пакетът отново е получен от сървъра след 2,00369 секунди. Всъщност бихме могли да зададем по този начин всеки от използваните в сценария атрибути. В частност, бихме могли да зададем различни стойности на атрибутите MaxPackets UdpEchoClient.
Как бихте използвали това? Опитайте. Помнете, че трябва да коментирате мястото, където пренастройваме стойността на атрибута по подразбиране и изрично да зададем MaxPackets в сценария. След това трябва да преработите скрипта. Можете също така чрез системата за команден ред да получите информация за синтаксиса за задаване на нова стойност на атрибута по подразбиране. Когато разберете това, ще можете да управлявате броя на пакетите, показвани в командния ред. Тъй като сме старателни хора, нашата командна редица трябва да изглежда приблизително така:
$ .\/waf --run "scratch\/myfirst
--ns3::PointToPointNetDevice::DataRate=5Mbps
--ns3::PointToPointChannel::Delay=2ms
--ns3::UdpEchoClient::MaxPackets=2"Естественият въпрос, който възниква на този етап, е как да научите за съществуването на всички тези атрибути. Отново, системата за команден ред разполага с функция за помощ в този контекст. Ако поискаме помощ от командния ред, трябва да видим:
$ .\/waf --run "scratch\/myfirst --PrintHelp"
myfirst [Program Arguments] [General Arguments]
General Arguments:
--PrintGlobals: Печать списка глобальных переменных.
--PrintGroups: Печать списка групп.
--PrintGroup=[group]: Печать всех TypeIds группы.
--PrintTypeIds: Печать всех TypeIds.
--PrintAttributes=[typeid]: Печать всех атрибутов typeid.
--PrintHelp: Печать этого сообщения помощи.Ако изберете аргумента "PrintGroups", трябва да видите списък на всички регистрирани групи TypeId. Имената на групите съвпадат с имената на модулите в изходната директория (въпреки че с главна буква). Отпечатването на цялата информация наведнъж ще бъде твърде обемно, затова е наличен допълнителен филтър за печат на информация по групи. Например, отново фокусирайки се върху модула "точка-точка":
.\/waf --run "scratch\/myfirst --PrintGroup=PointToPoint"
TypeIds в група PointToPoint:
ns3::PointToPointChannel
ns3::PointToPointNetDevice
ns3::PointToPointRemoteChannel
ns3::PppHeaderТук можете да намерите наличните имена TypeId за търсене на атрибути, например, в
--PrintAttributes = ns3::PointToPointChannel, както е показано по-горе.
Още един начин да се запознаете с атрибутите е чрез Doxygen ns‑3. Там има страница, която изброява всички регистрирани в симулатора атрибути.
5.2.2 Захващане на вашите собствени команди
Можете също да добавите свои собствени хукеве чрез системата на командния ред. Това е доста просто с помощта на метода на парсера на командния ред AddValue.
Нека използваме тази възможност, за да укажем броя на пакетите, които трябва да се показват, по съвсем различен начин. Да добавим локална променлива с име nPackets в функция main. Ще я зададем на единица, за да отговаря на нашето предишно поведение по подразбиране. За да позволим на парсера на командния ред да промени тази стойност, трябва да я уловим в парсера. Правим това, като добавим извикване AddValue. Отидете и променете скрипта scratch/myfirst.cc , така че да започне с следния код,
int
main (int argc, char *argv[])
{
uint32_t nPackets = 1;
CommandLine cmd;
cmd.AddValue("nPackets", "Брой на пакетите за ехо", nPackets);
cmd.Parse (argc, argv);
...Превъртете надолу до точката в скрипта, където задаваме атрибута MaxPackets и го променете, така че да бъде настроен на променливата nPackets вместо константата 1, както е показано по-долу.
echoClient.SetAttribute ("MaxPackets", UintegerValue (nPackets));Сега, ако стартирате скрипта и подставите аргумента —PrintHelp, трябва да видите новия аргумент за потребителя, изброен на дисплея на помощния екран. Въведете,
$ .\/waf --run "scratch\/myfirst --PrintHelp"
Waf: Влизам в директория `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
Waf: Излизам от директория `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
'build' успешно завършен (0.403s)
--PrintHelp: Печати това съобщение за помощ.
--PrintGroups: Печати списъка на групите.
--PrintTypeIds: Печати всички TypeIds.
--PrintGroup=[group]: Печати всички TypeIds на групата.
--PrintAttributes=[typeid]: Печати всички атрибути на typeid.
--PrintGlobals: Печати списъка на глобалните променливи.
Потребителски аргументи:
--nPackets: Брой пакети за echoАко искате да промените броя на предаваните пакети, можете да го направите, задавайки аргумента -nPackets в командния ред.
$ .\/waf --run "scratch\/myfirst --nPackets=2"Сега трябва да видите
Waf: Влизам в директория `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
Waf: Излизам от директория `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
'build' успешно завършен (0.404s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Изпратени 1024 байта до 10.1.1.2
2.25732s Получени 1024 байта от 10.1.1.1
2.25732s Echoing пакет
Получени 1024 байта от 10.1.1.2
Изпратени 1024 байта до 10.1.1.2
3.25732s Получени 1024 байта от 10.1.1.1
3.25732s Echoing пакет
Получени 1024 байта от 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()Сега изпратихте два пакета. Доста лесно, нали?
Можете да видите, че като потребител на ns‑3, можете да използвате системата от аргументи в командния ред, за да управлявате глобалните стойности и атрибутите. Ако сте автор на модел, можете да добавите нови атрибути към вашите обекти, които ще бъдат автоматично достъпни за настройка от вашите потребители чрез системата за команден ред. Ако сте автор на сценарии, можете да добавяте нови променливи в вашите скриптове и безпроблемно да ги свържете със системата за команден ред.
5.3 Използване на системата за проследяване
Целта на моделирането е да създаде изходни данни за по-нататъшно проучване, а системата за трасировка ns‑3 е основният механизъм за това. Тъй като ns‑3 е програма на C++, могат да бъдат използвани стандартните средства за генериране на изходни данни от програми на C++:
#include <iostream>
...
int main ()
{
...
std::cout << "The value of x is " << x << std::endl;
...
}Можете дори да използвате модула за журнализиране, за да добавите малка структура към вашето решение. Известно е, че много проблеми произлизат от този подход и затова за да се справим с тези проблеми, предоставихме обща подсистема за трасировка на събития.
Основните цели на системата за трасировка ns‑3:
За основните задачи системата за трасировка трябва да позволява на потребителя да генерира стандартна трасировка за популярни източници и да избира обектите, които генерират трасировка;
Потребителите на междинно ниво трябва да могат да разширяват системата за проследяване, за да променят генерирания изходен формат или да добавят нови източници на проследяване, без да е необходимо модифициране на ядрото на симулатора;
Опитните потребители могат да модифицират ядрото на симулатора, за да добавят нови източници и приемници на проследяване. Системата за проследяване ns-3 е изградена на принципите на независими източници на проследяване и приемници, както и унифициран механизъм за свързване на източниците с потребителите.
Системата за проследяване ns-3 е изградена на принципите на независими източници и приемници на проследяване, както и унифициран механизъм за свързване на източниците с приемниците. Източниците на проследяване са обекти, които могат да сигнализират за събития, случващи се в симулацията, и да предоставят достъп до интересуващите основни данни. Например, източник на проследяване може да посочва, когато мрежово устройство получи пакет и да осигури достъп до съдържанието на пакета за заинтересованите приемници на проследяване.
Източниците на проследяване сами по себе си са безполезни, ако не са "свързани" с други части от кода, които всъщност правят нещо полезно с информацията, предоставена от приемника. Проследяващите устройства са потребители на събития и данни, предоставени от източниците на проследяване. Например, може да се създаде приемник на проследяване, който при свързване с източника на проследяване от предишния пример да отпечата интересуващите части от получения пакет.
Разумното основание за такова изрично разделение е да се позволи на потребителите да свързват нови типове приемници с съществуващи източници на проследяване, без да е необходимо редактиране и прекомпилиране на ядрото на симулатора. Така в горния пример потребителят може да определи нов проследяващ модул в своя скрипт и да го свърже с съществуващ източник на проследяване, определен в ядрото на симулацията, само с редактиране на потребителския скрипт.
В това ръководство ще разгледаме някои от предварително зададените източници и приемници и ще покажем как да ги настроите с минимални усилия от страна на потребителя. Вижте ръководството на ns‑3 или секциите с инструкции за информация относно разширената конфигурация на трасировката, включително разширение на пространството на имената на трасировката и създаване на нови източници на трасировка.
5.3.1 ASCII Проследяване
ns‑3 предоставя помощна функционалност, която осигурява ниско ниво на системата за трасировка, за да ви помогне с детайлите при настройката на опростени трасировки на пакети. Ако активирате тази функция, ще видите изходните данни в ASCII файлове. За тези, които са запознати с изхода на ns-2, този тип трасировка е подобен на out.tr, който е генериран от множество скриптове.
Нека да преминем направо към същината и да добавим някои ASCII резултати от трасировката в нашия скрипт scratch/myfirst.cc. Точно преди извикването на Simulator :: Run (), добавете следните редове код:
AsciiTraceHelper ascii;
pointToPoint.EnableAsciiAll(ascii.CreateFileStream("myfirst.tr"));Както в много други идиоми на ns‑3, този код използва помощен обект за създаване на ASCII трасировки. Вторият ред съдържа два вложени извика на метода. Методът „вътре в“ CreateFileStream() използва идиома на анонимния обект за създаване на обект на файлов поток в стека (без име на обект) и го предава на извиканата функция. В бъдеще ще се задълбочим в този въпрос, но всичко, което трябва да знаете на този етап е, че създавате обект, представляващ файл с име myfirst.tr и го предавате на ns‑3. Ние оставяме на ns‑3 грижата за създадения обект през цялото време на живота му, в течение на който се решават проблеми, причинени от малко известното (преднамерено) ограничение, свързано с копирующите конструктори на потокови обекти C++.
Външният извикване EnableAsciiAll() казва на помощника, че искате да включите ASCII трасировка за всички връзки на устройство точка-точка в симулацията ви и че искате (специфицираните) приемници на трасировката да записват информация за движението на пакетите в ASCII формат.
За тези, които са запознати с ns-2, наблюдаваните събития са еквивалентни на известните точки на трасировка, които записват събития „+“, „-“, „d“ и „r“.
Сега можете да компилирате скрипта и да го стартирате от командния ред:
$ ./waf --run scratch/myfirstКолко много пъти преди това сте виждали съобщения от Waf и след това ‘build’ finished successfully (сборката е завършена успешно) с няколко съобщения от работещата програма.
Докато работи, програмата ще създаде файл с име myfirst.tr. Поради особеностите на работа Waf, по подразбиране файлът не се създава в локалната директория, а в директорията на горното ниво на репозиторията. Ако искате да промените пътя, където се съхраняват трасетата, можете да използвате параметъра за Waf --cwd. Ние не направихме това, така че за да погледнем файла ASCII трасировка myfirst.tr в любимия ви редактор, ще трябва да отидем в директорията на горното ниво на нашия репозиторий.
Парсинг на ASCII проследявания
Има много информация в доста компактна форма, но първото, на което трябва да обърнете внимание, е, че файлът се състои от отделни редове. Това ще стане ясно, ако разширите прозореца на прегледа.
Всеки ред в файла съответства на събитие от трасировката. В този случай трасироваме събитията в опашката за предаване, присъстваща във всяко мрежово устройство точка-точка в симулацията. Опашката за предаване е опашката, през която всеки пакет трябва да премине за канала точка-точка. Обърнете внимание, че всеки ред в файла за трасировка започва с единичен символ (и има интервал след него). Този символ ще има следното значение:
+: в опашката на устройството е настъпила операция за поставяне в опашка;
-: в опашката на устройството е настъпила операция за извличане на елемент;
d: пакетът е бил отхвърлен, обикновено, защото опашката е пълна;
r: пакетът е бил получен от мрежовото устройство.
Нека разгледаме по-подробно първия ред в файла за трасировка. Ще го разделя на части (с отстъпи за яснота) и номер на реда отляво:
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)Първият раздел на това разширено събитие от трасировката (ред 0) е операция. Тук имаме символ +, който съответства на операцията за поставяне в опашка за предаване. Вторият раздел (ред 1) е времето на моделирането, изразено в секунди. Можете да си спомните, че попитахме UdpEchoClientApplication стартиране на изпращането на пакети след две секунди. Тук виждаме потвърждение, че това наистина се случва.
В следващия раздел на примера с проследяване (от ред 2) се показва кой източник на проследяване е породил това събитие (посочва се пространство имен). Можете да представите пространството имен на проследяване подобно на пространството имен на файловата система. Коренът на пространството имен е NodeList. Той отговаря на контейнер, управляван основно от кода на ns-3. Тук се съдържат всички възли, които се създават в скрипта. Подобно на файловата система, която може да има директории в корена си, в NodeList имаме множество възли. Така че редът \/NodeList\/0 указва нулевия възел в NodeList, който обикновено разбираме като „възел 0“. Всеки възел има списък с устройства, които са били инсталирани. Този списък се намира следващия в пространството имен. Можете да видите, че това събитие на проследяване произлиза от DeviceList\/0, което е нулевото устройство, инсталирано в възела.
Следващият подред, $ ns3 :: PointToPointNetDevice, указва кое устройство е на нулевата позиция: списъка на устройствата на нулевия възел. Напомняме, че операцията +, намираща се в ред 0, означава, че е добавен елемент към опашката на предаване на устройството. Това е отразено в последните сегменти на „пътя на проследяването“: TxQueue\/Enqueue.
Останалите раздели в проследяването трябва да са интуитивно достатъчно ясни. Редове 3-4 показват, че пакетът е инкапсулиран в протокола точка-точка. Редове 5-7 показват, че пакетът има заглавие на версия IP4 и произлиза от IP адрес 10.1.1.1 и е предназначен за 10.1.1.2. Редове 8-9 показват, че този пакет има заглавие UDP и, накрая, ред 10 показва, че полезният товар е очакваните 1024 байта.
Следващият ред в файла за проследяване показва, че същият пакет е извлечен от опашката на предаване на същия възел.
Третият ред в файла за проследяване показва, че пакетът е приет от мрежовото устройство на възела с ехо-сервер. Възпроизведох това събитие по-долу.
0 r
1 2.25732
2 \/NodeList\/1\/DeviceList\/0\/$ns3::PointToPointNetDevice\/MacRx
3 ns3::Ipv4Header (\n4 tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none]\n5 length: 1052 10.1.1.1 > 10.1.1.2)\n6 ns3::UdpHeader (\n7 length: 1032 49153 > 9)\n8 Payload (size=1024)Обърнете внимание, че операцията за проследяване сега е r, а времето за симулация е увеличено до 2,25732 секунди. Ако сте следвали внимателно инструкциите в учебника, това означава, че сте оставили DataRate на мрежовите устройства и закъснението на канала в техните стойности по подразбиране. Това време трябва да ви е познато, тъй като вече сте го видели в предишния раздел.
Записът на пространството на имената на източника на проследяване (ред 2) е променен, за да отрази, че това събитие произхожда от възел 1 (/NodeList/1) и пакет принят источником трассировки (/MacRx). Трябва да ви е сравнително лесно да проследите движението на пакета през топологията, преглеждайки останалите в файла за трасета.
5.3.2 Проследяване на PCAP
Помощниците на устройството ns-3 също могат да се използват за генериране на файлове за проследяване в формат .pcap. Акронимът pcap (обикновено се пише с малки букви) обозначава улавяне на пакети и всъщност е API, което включва определяне на формата на файла .pcap. Най-популярната програма, която може да чете и показва този формат, е Wireshark (преди наричана Ethereal). Въпреки това, има много анализатори на трафика, които използват този формат на пакетите. Препоръчваме на потребителите да използват множество инструменти, налични за анализ на pcap трасета. В това ръководство ще се съсредоточим върху прегледа на pcap трасета с помощта на tcpdump.
Включването на pcap трасировката се извършва с една линия код.
pointToPoint.EnablePcapAll("myfirst");Поставете тази линия код след кода за ASCII проследяване, който току-що добавихме в scratch/myfirst.cc. Обърнете внимание, че предадохме само низ "myfirst", а не "myfirst.pcap" или нещо подобно. Това е така, защото параметърът е префикс, а не пълното име на файла. По време на симулацията помощникът всъщност ще създаде файл за проследяване за всяко устройство точка-точка. Имената на файловете ще бъдат изградени с помощта на префикса, номера на възела, номера на устройството и суфикса.pcap».
В нашия примерен сценарий в крайна сметка ще видим файлове с имена "myfirst-0-0.pcap» и „myfirst-1-0.pcap", които представляват pcap трасировки за възел 0-устройство 0 и възел 1-устройство 0 съответно. След като добавите линия код за включване на pcap трасировката, можете да стартирате скрипта по обичайния начин:
$ ./waf --run scratch/myfirstАко погледнете в директорията на върховото ниво на вашето разпределение, трябва да видите три файла: файл ASCII за трасировка myfirst.tr, който разгледахме по-рано, файлове myfirst-0-0.pcap и myfirst-1-0.pcap — нови pcap файлове, които току-що генерирахме.
Четене на изхода с помощта на tcpdump
В момента най-лесно е да преглеждате pcap файлове с tcpdump.
$ tcpdump -nn -tt -r myfirst-0-0.pcap
чете от файла myfirst-0-0.pcap, тип на връзката PPP (PPP)
2.000000 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, дължина 1024
2.514648 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, дължина 1024
tcpdump -nn -tt -r myfirst-1-0.pcap
чете от файла myfirst-1-0.pcap, тип на връзката PPP (PPP)
2.257324 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, дължина 1024
2.257324 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, дължина 1024В дампа myfirst-0-0.pcap (клиентското устройство) можете да видите, че ехо-пакетът се изпраща след 2 секунди симулация. Ако погледнете втория дамп (myfirst-1-0.pcap), ще видите, че пакетът е приет в момент 2.257324 секунди. Ще видите във втория дамп, че пакетът се връща в момент 2.257324 секунди и накрая, че пакетът е получен обратно от клиента в първия дамп в момент 2.514648 секунди.
Четене на изхода с помощта на Wireshark
Ако не сте запознати с Wireshark, има уебсайт, от който можете да изтеглите програми и документация: . Wireshark — това е графичен потребителски интерфейс, който можете да използвате за показване на тези файлове за проследяване. Ако имате Wireshark, можете да отворите всеки от файловете за проследяване и да покажете съдържанието, сякаш сте заснели пакетите, използвайки анализатор на пакети.
Източник: habr.com
