کاوش در موتور VoIP Mediastreamer2. قسمت 7

مطالب مقاله از من گرفته شده است کانال ذن.

کاوش در موتور VoIP Mediastreamer2. قسمت 7

استفاده از TShark برای تجزیه و تحلیل بسته های RTP

کاوش در موتور VoIP Mediastreamer2. قسمت 7

در آخر مقاله ما یک مدار کنترل از راه دور را از یک مولد صدا و یک آشکارساز صدا مونتاژ کردیم که ارتباط بین آنها با استفاده از یک جریان RTP انجام شد.

در این مقاله به بررسی انتقال سیگنال صوتی با استفاده از پروتکل RTP ادامه می دهیم. ابتدا، اجازه دهید برنامه آزمایشی خود را به یک فرستنده و یک گیرنده تقسیم کنیم و یاد بگیریم که چگونه جریان RTP را با استفاده از یک تحلیلگر ترافیک شبکه بررسی کنیم.

بنابراین، برای اینکه بتوانیم واضح تر ببینیم کدام عناصر برنامه مسئول انتقال RTP و کدام یک مسئول دریافت هستند، فایل mstest6.c خود را به دو برنامه مستقل برای فرستنده و گیرنده تقسیم می کنیم؛ توابع مشترکی را که هر دو استفاده می کنند قرار می دهیم. در فایل سوم که با آن تماس خواهیم گرفت mstest_common.c، توسط فرستنده و گیرنده با استفاده از دستورالعمل شامل متصل می شود:

/* Файл mstest_common.c Общие функции для передатчика и приемника. */
#include <mediastreamer2/msfilter.h>
#include <mediastreamer2/msticker.h>
#include <mediastreamer2/msrtp.h>
#include <ortp/rtpsession.h>
#include <ortp/payloadtype.h>

define PCMU 0

/*---------------------------------------------------------*/
/* Функция регистрации типов полезных нагрузок. */
void register_payloads(void)
{  
 /* Регистрируем типы нагрузок в таблице профилей. Позднее, по индексу    взятому 
     из заголовка RTP-пакета из этой таблицы будут извлекаться    параметры 
     нагрузки, необходимые для декодирования данных пакета. */
  rtp_profile_set_payload (&av_profile, PCMU, &payload_type_pcm8000);
}

/*---------------------------------------------------------*/
/* Эта функция создана из функции create_duplex_rtpsession() в audiostream.c   медиастримера2. */
 static RtpSession *create_rtpsession (int loc_rtp_port, int loc_rtcp_port,  bool_t ipv6, RtpSessionMode mode)
{  
  RtpSession *rtpr;  rtpr = rtp_session_new ((int) mode);  
  rtp_session_set_scheduling_mode (rtpr, 0);  
  rtp_session_set_blocking_mode (rtpr, 0);
  rtp_session_enable_adaptive_jitter_compensation (rtpr, TRUE);
  rtp_session_set_symmetric_rtp (rtpr, TRUE); 
  rtp_session_set_local_addr (rtpr, ipv6 ? "::" : "0.0.0.0", loc_rtp_port,  loc_rtcp_port); 
  rtp_session_signal_connect (rtpr, "timestamp_jump",  (RtpCallback) rtp_session_resync, 0);
  rtp_session_signal_connect (rtpr, "ssrc_changed",  (RtpCallback) rtp_session_resync, 0);
  rtp_session_set_ssrc_changed_threshold (rtpr, 0);
  rtp_session_set_send_payload_type(rtpr, PCMU);

  /* По умолчанию выключаем RTCP-сессию, так как наш пульт не будет использовать 
  её. */  
 rtp_session_enable_rtcp (rtpr, FALSE);
 return rtpr;
}

اکنون فایل فرستنده جداگانه:

/* Файл mstest6.c Имитатор пульта управления (передатчика). */
#include <mediastreamer2/dtmfgen.h>
#include <mediastreamer2/msrtp.h>
#include "mstest_common.c"

/*----------------------------------------------------------*/
int main()
{ 
  ms_init();

/* Создаем экземпляры фильтров. */
  MSFilter *voidsource = ms_filter_new(MS_VOID_SOURCE_ID); 
  MSFilter *dtmfgen = ms_filter_new(MS_DTMF_GEN_ID);

/* Создаем фильтр кодера. */
  MSFilter *encoder = ms_filter_create_encoder("PCMU");

/* Регистрируем типы нагрузки. */
  register_payloads();

/* Создаем RTP-сессию передатчика. */
  RtpSession *tx_rtp_session = create_rtpsession (8010, 8011, FALSE, RTP_SESSION_SENDONLY);  
 rtp_session_set_remote_addr_and_port(tx_rtp_session,"127.0.0.1", 7010, 7011); 
 rtp_session_set_send_payload_type(tx_rtp_session, PCMU);  
 MSFilter *rtpsend = ms_filter_new(MS_RTP_SEND_ID); 
 ms_filter_call_method(rtpsend, MS_RTP_SEND_SET_SESSION, tx_rtp_session);

/* Создаем источник тактов - тикер. */ 
 MSTicker *ticker_tx = ms_ticker_new();

/* Соединяем фильтры передатчика. */ 
 ms_filter_link(voidsource, 0, dtmfgen, 0);  
 ms_filter_link(dtmfgen, 0, encoder, 0);
 ms_filter_link(encoder, 0, rtpsend, 0);

/* Подключаем источник тактов. */
  ms_ticker_attach(ticker_tx, voidsource);

/* Настраиваем структуру, управляющую выходным сигналом генератора. */ 
 MSDtmfGenCustomTone dtmf_cfg; 
 dtmf_cfg.tone_name[0] = 0; 
 dtmf_cfg.duration = 1000; 
 dtmf_cfg.frequencies[0] = 440;

/* Будем генерировать один тон, частоту второго тона установим в 0. */  
 dtmf_cfg.frequencies[1] = 0; 
 dtmf_cfg.amplitude = 1.0; 
 dtmf_cfg.interval = 0.;  
 dtmf_cfg.repeat_count = 0.;

/* Организуем цикл сканирования нажатых клавиш. Ввод нуля завершает
* цикл и работу программы. */  
 char key='9'; 
 printf("Нажмите клавишу команды, затем ввод.n"  
"Для завершения программы введите 0.n");  
while(key != '0')  
{
 key = getchar();   
 if ((key >= 49) && (key <= 54)) 
   {
      printf("Отправлена команда: %cn", key);
      /* Устанавливаем частоту генератора в соответствии с
       * кодом нажатой клавиши. */
      dtmf_cfg.frequencies[0] = 440 + 100*(key-49);

      /* Включаем звуковой генератор c обновленной частотой. */
      ms_filter_call_method(dtmfgen, MS_DTMF_GEN_PLAY_CUSTOM,      (void*)&dtmf_cfg); 
   }
   /* Укладываем тред в спячку на 20мс, чтобы другие треды 
   * приложения получили время на работу. */ 
  ms_usleep(20000);
  }
}

و در نهایت فایل گیرنده:

/* Файл mstest7.c Имитатор приемника. */
include <mediastreamer2/mssndcard.h>
include <mediastreamer2/mstonedetector.h>
include <mediastreamer2/msrtp.h>

/* Подключаем заголовочный файл с функциями управления событиями  медиастримера.*/
include <mediastreamer2/mseventqueue.h>
/* Подключаем файл общих функций. */
include "mstest_common.c"

/* Функция обратного вызова, она будет вызвана фильтром, как только он   обнаружит совпадение характеристик входного сигнала с заданными. */
static void tone_detected_cb(void *data, MSFilter *f, unsigned int event_id,MSToneDetectorEvent *ev)
{ 
 printf("Принята команда: %sn", ev->tone_name);
}

/*----------------------------------------------------------*/
int main()
{ 
 ms_init();

/* Создаем экземпляры фильтров. */  
 MSSndCard *card_playback =  ms_snd_card_manager_get_default_card(ms_snd_card_manager_get()); 
 MSFilter *snd_card_write = ms_snd_card_create_writer(card_playback); 
 MSFilter *detector = ms_filter_new(MS_TONE_DETECTOR_ID);

/* Очищаем массив находящийся внутри детектора тонов, он описывает
* особые приметы разыскиваемых сигналов.*/
  ms_filter_call_method(detector, MS_TONE_DETECTOR_CLEAR_SCANS, 0);

/* Подключаем к фильтру функцию обратного вызова. */  
ms_filter_set_notify_callback(detector,  (MSFilterNotifyFunc)tone_detected_cb, NULL);

/* Создаем массив, каждый элемент которого описывает характеристику
* одного из тонов, который требуется обнаруживать:
Текстовое имя
* данного элемента, частота в герцах, длительность в миллисекундах,
* минимальный уровень относительно 0,775В. */
  MSToneDetectorDef scan[6]= 
 {   
    {"V+",440, 100, 0.1}, /* Команда "Увеличить громкость". */
    {"V-",540, 100, 0.1}, /* Команда "Уменьшить громкость". */
    {"C+",640, 100, 0.1}, /* Команда "Увеличить номер канала". */
    {"C-",740, 100, 0.1}, /* Команда "Уменьшить номер канала". */
    {"ON",840, 100, 0.1}, /* Команда "Включить телевизор". */
    {"OFF", 940, 100, 0.1}/* Команда "Выключить телевизор". */
  };

/* Передаем "приметы" сигналов детектор тонов. */
  int i; 
 for (i = 0; i < 6; i++) 
 { 
   ms_filter_call_method(detector, MS_TONE_DETECTOR_ADD_SCAN,    &scan[i]); 
 }

/* Создаем фильтр декодера */
  MSFilter *decoder=ms_filter_create_decoder("PCMU");

/* Регистрируем типы нагрузки. */
  register_payloads();

/* Создаем RTP-сессию приемника. */
  MSFilter *rtprecv = ms_filter_new(MS_RTP_RECV_ID);
  RtpSession *rx_rtp_session = create_rtpsession (7010, 7011, FALSE, RTP_SESSION_RECVONLY);
  ms_filter_call_method(rtprecv, MS_RTP_RECV_SET_SESSION, rx_rtp_session);

/* Создаем источник тактов - тикер. */ 
 MSTicker *ticker_rx = ms_ticker_new();

/* Соединяем фильтры приёмника. */
  ms_filter_link(rtprecv, 0, decoder, 0);
  ms_filter_link(decoder, 0, detector, 0);
  ms_filter_link(detector, 0, snd_card_write, 0);

/* Подключаем источник тактов. */
  ms_ticker_attach(ticker_rx, rtprecv);
  char key='9';
  printf( "Для завершения программы введите 0.n");
  while(key != '0') 
 {
    key = getchar();
   /* Укладываем тред в спячку на 20мс, чтобы другие треды    * приложения получили время на работу. */
   ms_usleep(20000); 
 }
}

فرستنده و گیرنده را کامپایل می کنیم، سپس هر کدام را در کنسول خود راه اندازی می کنیم. سپس باید مانند قبل کار کند - فقط باید اعداد 1 تا 6 را در کنسول فرستنده وارد کنیم و پاسخ به آنها باید در کنسول گیرنده ظاهر شود. صداها باید در بلندگو قابل شنیدن باشند. اگر همه چیز چنین است، پس ما بین گیرنده و فرستنده ارتباط برقرار کرده ایم - انتقال مداوم بسته های RTP از فرستنده به گیرنده وجود دارد.

اکنون زمان نصب یک تحلیلگر ترافیک است؛ برای این کار ما نسخه کنسول برنامه عالی Wireshark را نصب می کنیم - به نام TShark. من TShark را برای بحث بیشتر به منظور تسهیل شرح مدیریت برنامه انتخاب کردم. با Wireshark، من به دریایی از اسکرین شات ها نیاز دارم که با انتشار نسخه جدید Wireshark به سرعت منسوخ می شوند.

اگر می دانید چگونه از Wireshark استفاده کنید، می توانید از آن برای مطالعه نمونه های ما استفاده کنید. اما حتی در این مورد، توصیه می‌کنم که به TShark تسلط داشته باشید، زیرا به شما کمک می‌کند تا آزمایش‌های خودکار برنامه‌های VoIP خود را انجام دهید و همچنین ضبط از راه دور را انجام دهید.

TShark را با دستور نصب کنید:

$ sudo apt-get install tshark

به طور سنتی، ما نتیجه نصب را با درخواست نسخه برنامه بررسی می کنیم:

$ tshark --version

اگر پاسخ کافی دریافت شد، ادامه می دهیم.

از آنجایی که بسته های ما در حال حاضر فقط در داخل کامپیوتر قرار می گیرند، می توانیم به tshark بگوییم که فقط چنین بسته هایی را نشان دهد. برای انجام این کار، باید از اینترفیس، packet capture را انتخاب کنید حلقه برگشت (Loopback) با عبور دادن گزینه TShark -ilo:

$ sudo tshark -i lo

پیام های مربوط به بسته های ارسال شده توسط فرستنده ما بلافاصله شروع به ریختن به کنسول می کنند (به طور مداوم، صرف نظر از اینکه دکمه کنترل از راه دور را فشار داده ایم یا نه). شاید برنامه هایی در رایانه شما وجود داشته باشد که بسته ها را از طریق یک حلقه محلی نیز ارسال می کند، در این صورت ما مخلوطی از بسته های خود و دیگران را دریافت خواهیم کرد. برای اطمینان از اینکه در لیست فقط بسته های ارسال شده توسط کنترل از راه دور را می بینیم، یک فیلتر بر اساس شماره پورت اضافه می کنیم. با فشردن Ctrl-C آنالایزر را متوقف می کنیم و فیلتری را برای شماره پورت وارد می کنیم که ریموت کنترل از آن به عنوان پورت مقصد برای انتقال خود استفاده می کند (8010): -f "udp port 8010". اکنون خط فرمان ما به شکل زیر خواهد بود:

$ sudo tshark -i lo -f "udp port 8010"

خروجی زیر در کنسول ظاهر می شود (10 خط اول):

 1 0.000000000    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172 
 2 0.020059705    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172
 3 0.040044409    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172 
 4 0.060057104    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172
 5 0.080082311    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172  
 6 0.100597153    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172 
 7 0.120122668    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172
 8 0.140204789    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172
 9 0.160719008    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172
10 0.180673685    127.0.0.1 → 127.0.0.1    UDP 214 8010 → 7010 Len=172

در حال حاضر، این‌ها بسته‌ها نیستند، بلکه فهرستی از رویدادها هستند، که در آن هر خط پیامی در مورد بسته بعدی است که در رابط مورد توجه قرار گرفته است. از آنجایی که قبلاً از فیلتر کردن بسته ها مراقبت کرده ایم، در لیست فقط پیام هایی در مورد بسته ها از فرستنده خود می بینیم. بعد، بیایید این جدول را با شماره ستون ها رمزگشایی کنیم:

شماره رویداد.
زمان وقوع آن.
آدرس IP مبدا بسته و آدرس IP مقصد بسته.
پروتکل بسته به صورت UDP نشان داده می شود زیرا بسته های RTP به عنوان بار در داخل بسته های UDP ارسال می شوند.
اندازه بسته به بایت
شماره پورت مبدأ بسته و شماره پورت مقصد بسته.
اندازه بار بسته، از اینجا می‌توان نتیجه گرفت که فرستنده ما بسته‌های RTP به اندازه 172 بایت تولید می‌کند که مانند یک اردک در صندوقچه، داخل یک بسته UDP با اندازه 214 بایت قرار دارد.
اکنون زمان آن است که به داخل بسته های UDP نگاه کنیم، برای این کار ما TShark را با مجموعه ای گسترده از کلیدها راه اندازی می کنیم:

sudo tshark -i lo -f "udp port 8010"  -P -V -O rtp -o rtp.heuristic_rtp:TRUE -x

در نتیجه، خروجی برنامه غنی می شود - رمزگشایی از محتویات داخلی بسته که باعث آن شده است به هر رویداد اضافه می شود. برای مشاهده بهتر خروجی، می‌توانید TShark را با فشار دادن Ctrl-C متوقف کنید، یا خروجی آن را با افزودن یک خط لوله به برنامه tee به دستور run، و با مشخص کردن نام فایل، سه <filename> در یک فایل کپی کنید:

$ sudo tshark -i lo -f "udp port 8010"  -P -V -O rtp -o rtp.heuristic_rtp:TRUE -x | tee  log.txt

حالا بیایید به آنچه در فایل به دست آوردیم نگاه کنیم، این اولین بسته از آن است:

1 0.000000000    127.0.0.1 → 127.0.0.1    RTP 214 PT=ITU-T G.711 PCMU, SSRC=0x6B8B4567, Seq=58366, Time=355368720
Frame 1: 214 bytes on wire (1712 bits), 214 bytes captured (1712 bits) on interface 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1User Datagram Protocol, Src Port: 8010, Dst Port: 7010
Real-Time Transport Protocol    [Stream setup by HEUR RT (frame 1)]
        [Setup frame: 1] 
       [Setup Method: HEUR RT]
    10.. .... = Version: RFC 1889 Version (2)
    ..0. .... = Padding: False
    ...0 .... = Extension: False
    .... 0000 = Contributing source identifiers count: 0   
   0... .... = Marker: False
    Payload type: ITU-T G.711 PCMU (0)
    Sequence number: 58366    [Extended sequence number: 58366]
    Timestamp: 355368720
    Synchronization Source identifier: 0x6b8b4567 (1804289383)
    Payload: ffffffffffffffffffffffffffffffffffffffffffffffff...

0000  00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00   ..............E.
0010  00 c8 3c 69 40 00 40 11 ff b9 7f 00 00 01 7f 00   ..<i@.@.........
0020  00 01 1f 4a 1b 62 00 b4 fe c7 80 00 e3 fe 15 2e   ...J.b..........
0030  7f 10 6b 8b 45 67 ff ff ff ff ff ff ff ff ff ff   ..k.Eg..........
0040  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
0050  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
0060  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
0070  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
0080  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
0090  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
00a0  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
00b0  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
00c0  ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff   ................
00d0  ff ff ff ff ff ff                                  ......

ما مقاله بعدی را به تجزیه و تحلیل اطلاعات موجود در این لیست اختصاص خواهیم داد و به ناچار در مورد ساختار داخلی بسته RTP صحبت خواهیم کرد.

منبع: www.habr.com

اضافه کردن نظر