UNIX: разработка сетевых приложений - Уильям Стивенс
Шрифт:
Интервал:
Закладка:
16 else
17 err_quit("usage: mysdr <mcast-addr> <port#> <interface-name>");
18 Setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
19 Bind(sockfd, sa, salen);
20 Mcast_join(sockfd, sa, salen, (argc == 4) ? argv[3], NULL, 0);
21 loop(sockfd, salen); /* получение и вывод */
22 exit(0);
23 }
Заранее известные имя и порт2-3 Адрес многоадресной передачи, заданный для анонсов SAP — 224.2.127.254, а его имя — sap.mcast.net. Все заранее известные адреса многоадресной передачи (см. http://www.iana.org/assignments/multicast-addresses) появляются в DNS в иерархии mcast.net. Заранее известный порт UDP — это порт 9875.
Создание сокета UDP12-17 Мы вызываем нашу функцию udp_client, чтобы просмотреть имя и порт, и она заполняет соответствующую структуру адреса сокета. Если не заданы аргументы командной строки, мы используем значения по умолчанию. В противном случае мы получаем адрес многоадресной передачи, порт и имя интерфейса из аргументов командной строки.
Связывание порта с помощью функции bind18-19 Мы устанавливаем параметр сокета SO_REUSEADDR, чтобы позволить множеству экземпляров этой программы запуститься на узле, и с помощью функции bind связываем порт с сокетом. Связывая адрес многоадресной передачи с сокетом, мы запрещаем сокету получать какие-либо другие дейтаграммы UDP, которые могут быть получены для этого порта. Связывание этого адреса многоадресной передачи не является обязательным, но оно обеспечивает возможность фильтрации, благодаря чему ядро может не принимать пакеты, которые его не интересуют.
Присоединение к группе20 Мы вызываем нашу функцию mcast_join, чтобы присоединиться к группе. Если имя интерфейса было задано в качестве аргумента командной строки, оно передается нашей функции, иначе мы позволяем ядру выбрать интерфейс, на котором будет происходить присоединение к группе.
21 Мы вызываем нашу функцию loop, показанную в листинге 21.6, чтобы прочитать и вывести все анонсы.
Листинг 21.6. Цикл, получающий и выводящий анонсы SAP/SDP
//mysdr/loop.c
1 #include "mysdr.h"
2 void
3 loop(int sockfd, socklen_t salen)
4 {
5 socklen_t len;
6 ssize_t n;
7 char *p;
8 struct sockaddr *sa;
9 struct sap_packet {
10 uint32_t sap_header;
11 uint32_t sap_src;
12 char sap_data[BUFFSIZE];
13 } buf;
14 sa = Malloc(salen);
15 for (;;) {
15 len = salen;
17 n = Recvfrom(sockfd, &buf, sizeof(buf) - 1, 0, sa, &len);
18 ((char *)&buf)[n] = 0; /* завершающий нуль */
19 buf.sap_header = ntohl(buf.sap_header);
20 printf("From %s hash 0х%0хn" Sock_ntop(sa, len),
21 buf.sap_header & SAP_HASH_MASK);
22 if (((buf.sap_header & SAP_VERSION_MASK) >> SAP_VERSION_SHIFT) > 1) {
23 err_msg("... version field not 1 (0x%08x)", buf.sap_header);
24 continue;
25 }
26 if (buf.sap_header & SAP_IPV6) {
27 err_msg("... IPv6");
28 continue;
29 }
30 if (buf.sap_header & (SAP_DELETE|SAP_ENCRYPTED|SAP_COMPRESSED)) {
31 err_msg("... can't parse this packet type (0x%80x)",
32 buf.sap_header);
33 continue;
34 }
35 p = buf.sap_data + ((buf.sap_header & SAP AUTHLEN_MASK)
36 >> SAP_AUTHLEN_SHIFT);
37 if (strcmp(p.,"application/sdp") == 0)
38 p += 16;
39 printf(%sn", p);
40 }
41 }
Формат пакета9-13 Структура sap_packet описывает пакет SDP: 32-разрядный заголовок SAP, за которым следует 32-разрядный адрес отправителя и сам анонс. Анонс представляет собой строки текста в стандарте ISO 8859-1 и не может превышать 1024 байта. В каждой дейтаграмме UDP допускается только один анонс сеанса.
Чтение дейтаграммы UDP, вывод параметров отправителя и содержимого15-21 Функция recvfrom ждет следующую дейтаграмму UDP, предназначенную нашему сокету. Когда она приходит, мы помещаем в конец буфера пустой байт, исправляем порядок байтов заголовка и выводим адрес отправителя пакета и хэш SAP.
Проверка заголовка SAP22-34 Мы проверяем заголовок SAP, чтобы убедиться, что он относится к одному из тех типов, с которыми мы умеем работать. Пакеты SAP с адресами IPv6 в заголовках, а также сжатые и зашифрованные пакеты мы не обрабатываем.
Поиск начала и вывод анонса35-39 Мы пропускаем аутентифицирующие данные и тип пакета, после чего выводим содержимое оставшейся части.
В листинге 21.7 показано несколько типичных примеров результата выполнения нашей программы.
Листинг 21.7. Типичный анонс SAP/SDP
freebsd % mysdr
From 128.223.83.33:1028 hash 0x0000 v=0
o=- 60345 0 IN IP4 128.223.214.198
s=UO Broadcast - NASA Videos - 25 Years of Progress
i=25 Years of Progress, parts 1-13. Broadcast with Cisco System's
IP/TV using MPEG1 codec (6 hours 5 Minutes; repeats) More information
about IP/TV and the client needed to view this program is available
from http://videolab.uoregon.edu/download.html
u=http://videolab.uoregon.edu/
e=Hans Kuhn <[email protected]>
p=Hans Kuhn <541/346-1758>
b=AS:1000
t=0 0
a=type:broadcast
a=tool:IP/TV Content Manager 3.2.24
a=x-iptv-file:1 name y:25yop1234567890123.mpg
m=video 63096 RTP/AVP 32 31 96
c=IN IP4 224.2.245.25/127
a=framerate:30
a=rtpmap:96 WBIH/90000
a=x-iptv-svr:video blaster2.uoregon.edu file 1 loop
m=audio 31954 RTP/AVP 14 96 0 3 5 97 98 99 100 101 102 10 11 103 104 105 106
c=IN IP4 224.2.216.85/127
a=rtpmap:96 X-WAVE/8000
a=rtpmap:97 L8/8000/2
a=rtpmap:98 L8/8000
a=rtpmap:99 L8/22050/2
a=rtpmap:100 L8/22050
a=rtpmap:101 L8/11025/2
a=rtpmap:102 L8/11025
a=rtpmap:103 L16/22050/2
a=rtpmap:104 L16/22050
a=rtpmap:105 L16/11025/2
a=rtpmap:106 L16/11025
a=x-iptv-svr:audio blaster2.uoregon.edu file 1 loop
Этот анонс описывает рассылки, посвященные истории NASA (National Aeronautics and Space Administration — НАСА, государственная организация США, занимающаяся исследованием космоса). Описание сеанса SDP состоит из множества строк следующего формата:
type=value
где type всегда является одним символом, значение которого зависит от регистра, a value — это структурированная текстовая строка, зависящая от значения type. Пробелы справа и слева от знака равенства недопустимы. v=0 (в нашем случае) обозначает версию (version).
■ o= обозначает источник (origin). В данном случае имя пользователя не указано, 60345 — идентификатор сеанса, 0 — номер версии этого сеанса, IN — тип сети, IР4 — тип адреса, 128.223.214.198 — адрес. В результате объединения этих пяти элементов — имя пользователя, идентификатор сеанса, тип сети, тип адреса и адрес — образуется глобально уникальный идентификатор сеанса.
■ s= задает имя сеанса (session name), а i= — это информация о сеансе (information). u= указывает URI (Uniform Resource Identifier — уникальный идентификатор ресурса), по которому можно найти более подробную информацию по тематике данного сеанса, а р= и e= задают номер телефона (phone number) и адрес электронной почты (e-mail) ответственного за данную конференцию.
■ b= позволяет оценить пропускную способность, необходимую для приема данного сеанса.
■ t= задает время начала и время окончания сеанса в единицах NTP (Network Time Protocol — синхронизирующий сетевой протокол), то есть число секунд, прошедшее с 1 января 1900 года, измеренное в соответствии с UTC (Universal Time Coordinated — универсальное скоординированное время). Данный сеанс является постоянным и не имеет конкретных моментов начала и окончания, поэтому соответствующие времена полагаются нулевыми.
■ Строки a= представляют собой атрибуты, либо сеанса, если они помещены до первой строки m=, либо мультимедиа, если они помещены после первой строки m=.
■ Строки m= — это анонсы мультимедиа. Первая строка говорит нам о том, что видео передается на порт 63 096 в формате RTP с использованием профиля аудио и видео (Audio/Video Profile, AVP) с возможными типами данных 32, 31 и 96 (то есть MPEG, H.261 и WBIH соответственно). Строка c= сообщает о соединении. В данном случае используется протокол IPv4 с групповым адресом 224.2.245.25 и TTL = 127. Хотя между этими числами стоит символ «косая черта», как в формате CIDR, они ни в коем случае не должны трактоваться как префикс и маска.
Следующая строка m= говорит, что аудиопоток передается на порт 31 954 и может иметь один из типов RTP/AVP, некоторые из которых являются стандартными, в то время как другие указаны ниже в виде атрибутов a=rtpmap:. Строка с= сообщает нам сведения об аудиосоединении: IPv4 с групповым адресом 224.2.216.85 и TTL = 127.
21.10. Отправка и получение