23.7. Получение адресов

We use cookies. Read the Privacy and Cookie Policy

23.7. Получение адресов

Поскольку протокол SCTP ориентирован на многоинтерфейсные узлы, для определения адресов локального и удаленного узла не могут использоваться те же механизмы, что и в TCP. В этом разделе мы изменим код клиента, добавив в него подписку на уведомление о событии COMMUNICATION UP. В этом уведомлении клиент будет получать сведения об адресах, между которыми установлена ассоциация. В листингах 23.9 и 23.10 представлены изменения в коде клиента. Листинги 23.11 и 23.12 содержат добавления к коду клиента.

Листинг 23.9. Клиент включает уведомления

16 bzero(&evnts, sizeof(evnts));

17 evnts.sctp_data_io_event = 1;

18 evnts.sctp_association_event = 1;

19 Setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS, &evnts, sizeof(evnts));

20 sctpstr_cli(stdin, sock_fd, (SA*)&servaddr, sizeof(servaddr));

Включение событий и вызов функции отправки сообщения

16-20 Функция main клиента претерпевает не слишком значительные изменения. Клиент явным образом подписывается на уведомления об изменении состояния ассоциации.

Посмотрим, что нам придется изменить в функции sctpstr_cli, чтобы она смогла вызывать нашу новую функцию обработки уведомлений.

Листинг 23.10. Функция sctp_strcli, способная работать с уведомлениями

21 do {

22  len = sizeof(peeraddr);

23  rd_sz = Sctp_recvmsg(sock_fd, recvline, sizeof(recvline),

24   (SA*)&peeraddr, &len, &sri, &msg_flags);

25  if (msg_flags & MSG_NOTIFICATION)

26   check_notification(sock_fd, recvline, rd_sz);

27 } while (msg_flags & MSG_NOTIFICATION);

28 printf("From str:%d seq:%d (assoc.0x%x) ",

29 sri.sinfo_stream, sri.sinfo_ssn, (u_int)sri.sinfo_assoc_id);

30 printf("%.*s", rd_sz.recvline);

Цикл ожидания сообщения

21-24 Клиент устанавливает переменную, в которой хранится длина адреса, и вызывает функцию sctp_recvmsg для получения эхо-ответа сервера на свое сообщение.

Проверка уведомлений

25-26 Клиент проверяет, не является ли полученное сообщение уведомлением. В последнем случае он вызывает функцию обработки уведомлений, представленную в листинге 23.11.

Переход на начало цикла

27 Если сообщение действительно было уведомлением, происходит переход на начало цикла ожидания сообщений.

Отображение сообщения

28-30 Клиент отображает сообщение и переходит к ожиданию пользовательского ввода.

Теперь мы можем изучить новую функцию sctp_check_notification, которая будет отображать адреса обоих конечных точек при получении уведомления об изменении состояния ассоциации.

Листинг 23.11. Обработка уведомлений

//sctp/sctp_check_notify.c

 1 #include "unp.h"

 2 void

 3 check_notification(int sock_fd, char *recvline, int rd_len)

 4 {

 5  union sctp_notification *snp;

 6  struct sctp_assoc_change *sac;

 7  struct sockaddr_storage *sal, *sar;

 8  int num_rem, num_loc;

 9  snp = (union sctp_notification*)recvline;

10  if (snp->sn_header.sn_type == SCTP_ASSOC_CHANGE) {

11   sac = &snp->sn_assoc_change;

12   if ((sac->sac_state == SCTP_COMM_UP) ||

13    (sac->sac_state == SCTP_RESTART)) {

14    num_rem = sctp_getpaddrs(sock_fd, sac->sac_assoc_id, &sar);

15    printf("There are %d remote addresses and they are: ", num_rem);

16    sctp_print_addresses(sar, num_rem);

17    sctp_freepaddrs(sar);

18    num_loc = sctp_getladdrs(sock_fd.sac->sac_assoc_id, &sal);

19    printf("There are %d local addresses and they are: ", num_loc);

20    sctp_print_addresses(sal, num_loc);

21    sctp_freeladdrs(sal);

22   }

23  }

24 }

Проверка типа уведомления

9-13 Функция преобразует буфер приема к типу универсального указателя на уведомления, чтобы определить тип полученного уведомления. Из всех уведомлений нас интересуют только уведомления об изменении ассоциации, а из них — уведомления о создании или перезапуске ассоциации (SCTP_COMM_UP и SCTP_RESTART). Все прочие уведомления нас не интересуют.

Получение и вывод адресов собеседника

14-17 Функция sctp_getpaddrs возвращает нам список удаленных адресов, которые мы выводим при помощи функции sctp_print_addresses, представленной в листинге 23.12. После работы с ней мы освобождаем ресурсы, выделенные sctp_getpaddrs, вызывая функцию sctp_freepaddrs.

Получение и вывод локальных адресов

18-21 Функция sctp_getladdrs возвращает нам список локальных адресов, которые мы выводим на экран вместе с их общим количеством. После завершения работы с адресами мы освобождаем память вызовом sctp_freeladdrs.

Последняя из новых функций называется sctp_print_addresses. Она выводит на экран адреса из списка, возвращаемого функциями sctp_getpaddrs и sctp_getladdrs. Текст функции представлен в листинге 23.12.

Листинг 23.12. Вывод списка адресов

//sctp/sctp_print_addrs.c

 1 #include "unp.h"

 2 void

 3 sctp_print_addresses(struct sockaddr_storage *addrs, int num)

 4 {

 5  struct sockaddr_storage *ss;

 6  int i, salen;

 7  ss = addrs;

 8  for (i=0; i<num; i++){

 9   printf("%s ", Sock_ntop((SA*)ss, salen));

10 #ifdef HAVE_SOCKADDR_SA_LEN

11   salen = ss->ss_len;

12 #else

13   swilch (ss->ss_family) {

14   case AF_INET:

15    salen = sizeof(struct sockaddr_in);

16    break;

17 #ifdef IPV6

18   case AF_INET6:

19    salen = sizeof(struct sockaddr_in6);

20    break;

21 #endif

22   default:

23    err_auit("sctp_print_addresses: unknown AF");

24    break;

25   }

26 #endif

27   ss = (struct sockaddr_storage*)((char*)ss + salen);

28  }

29 }

Последовательная обработка адресов

7-8 Функция перебирает адреса в цикле. Общее количество адресов указывается вызывающим процессом.

Вывод адреса

9 Адрес преобразуется к удобочитаемому виду функцией sock_ntop, которая, как вы помните, должна работать со структурами адреса сокета всех форматов, поддерживаемых системой.

Определение размера адреса

10-26 Список адресов передается в упакованном формате. Это не просто массив структур sockaddr_storage. Дело в том, что структура sockaddr_storage достаточно велика, и ее нецелесообразно использовать при передаче адресов между ядром и пользовательскими процессами. В системах, где эта структура содержит внутреннее поле длины, обработка списка является делом тривиальным: достаточно извлекать длину из текущей структуры sockaddr_storage. В прочих системах длина определяется на основании семейства адреса. Если семейство не определено, функция завершает работу с сообщением об ошибке.

Перемещение указателя

27 К указателю на элемент списка прибавляется размер адреса. Таким образом осуществляется перемещение по списку адресов.

Данный текст является ознакомительным фрагментом.