UNIX: разработка сетевых приложений
Шрифт:
25 Setsockopt(sockfd, IPPROTO_IPV6, IPV6_RTHDR, ptr, len);
26 free(ptr);
27 }
28 dg_cli(stdin, sockfd, ai->ai_addr, ai->ai_addrlen); /* do it all */
29 exit(0);
30 }
Создание маршрута
11-21
Если при вызове программы было указано более одного аргумента, все параметры командной строки, за исключением последнего, формируют маршрут от отправителя. Сначала мы
inet6_rth_space
, затем выделяем буфер соответствующего размера вызовом malloc
. После этого каждый адрес маршрута преобразуется в числовую форму функцией host_serv
и добавляется к маршруту функцией inet6_rth_add
. Примерно то же самое выполнял и TCP-клиент IPv4, за тем исключением, что здесь мы используем библиотечные функции, а не свои собственные. Поиск адресата и создание сокета
22-23
Мы определяем адрес назначения при помощи host_serv
и создаем сокет для отправки пакетов. Установка «закрепленного» параметра IPV6_RTHDR и вызов рабочей функции
24-27
В разделе 27.7 будет показано, что не обязательно отправлять одни и те же вспомогательные данные с каждым пакетом. Вместо этого можно вызвать setsockopt
таким образом, что один и тот же заголовок будет добавляться ко всем пакетам в рамках одного сеанса. Этот параметр устанавливается только в том случае, если указатель ptr
не нулевой, то есть мы уже должны были выделить буфер под заголовок маршрутизации. На последнем этапе мы вызываем рабочую функцию dg_cli
, которая не меняется с листинга 8.4. Программа UDP-сервера не изменилась по сравнению с предыдущими примерами. Сервер открывает сокет и вызывает функцию
dg_echo
. В листинге 27.6 представлена функция dg_echo
, печатающая информацию о маршруте от источника (если таковой был получен) и обращающая этот маршрут для отправки сообщения в обратном направлении. Листинг 27.6. Функция dg_echo, печатающая маршрут
//ipopts/dgechoprintroute.c
1 #include "unp.h"
2 void
3 dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
4 {
5 int n;
6 char mesg[MAXLINE];
7 int on;
8 char control[MAXLINE];
9 struct msghdr msg;
10 struct cmsghdr *cmsg;
11 struct iovec iov[1];
12 on = 1;
13 Setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVRTHDR, &on, sizeof(on));
14 bzero(&msg, sizeof(msg));
15 iov[0].iov_base = mesg;
16 msg.msg_name = pcliaddr;
17 msg.msg_iov = iov;
18 msg.msg_iovlen = 1;
19 msg.msg_control = control;
20 for (;;) {
21 msg.msg_namelen = clilen;
22 msg.msg_controllen = sizeof(control);
23 iov[0].iov_len = MAXLINE;
24 n = Recvmsg(sockfd, &msg, 0);
25 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
26 cmsg = CMSG_NXTHDR(&msg, cmsg)) {
27 if (cmsg->cmsg_level == IPPROTO_IPV6 &&
28 cmsg->cmsg_type == IPV6_RTHDR) {
29 inet6_srcrt_print(CMSG_DATA(cmsg));
30 Inet6_rth_reverse(CMSG_DATA(cmsg), CMSG_DATA(cmsg));
31 }
32 }
33 iov[0].iov_len = n;
34 Sendmsg(sockfd, &msg, 0);
35 }
36 }
Включение
параметра IPV6_RECVRTHDR и подготовка структуры msghdr
12-13
Чтобы получить информацию о маршруте, мы должны установить параметр сокета IPV6_RECVRTHDR
. Кроме того, мы должны использовать функцию recvmsg
, поэтому мы настраиваем поля структуры msghdr
, которые не требуют изменения. Настройка изменяемых полей и вызов recvmsg
21-24
Мы устанавливаем размер полей длины и вызываем recvmsg
. Поиск и обработка маршрута от отправителя
25-32
Мы перебираем вспомогательные данные, используя CMSG_FIRSTHDR
и CMSG_NXTHDR
. Несмотря на то, что мы ожидаем получить только один объект вспомогательных данных, выполнить такой перебор всегда полезно. Если мы обнаруживаем заголовок маршрутизации, он распечатывается функцией inet6_srcrt_print
(листинг 27.7). Затем маршрут обращается функцией inet6_rth_reverse
для последующего использования при возвращении пакета клиенту. В данном случае обращение производится без копирования в новый буфер, так что можно использовать старый объект вспомогательных данных для отправки пакета клиенту. Отправка эхо-пакета
33-34
Мы устанавливаем длину пакета и передаем его клиенту вызовом sendmsg
. Благодаря наличию вспомогательных библиотечных функций IPv6 наша функция
inet6_srcrt_print
становится почти тривиальной. Листинг 27.7. Функция inet6_srcrt_print: вывод маршрута
1 #include "unp.h"
2 void
3 inet6_srcrt_print(void *ptr)
4 {
5 int i, segments;
6 char str[INET6_ADDRSTRLEN];
7 segments = Inet6_rth_segments(ptr);
8 printf("received source route: ");
9 for (i = 0; i < segments; i++)
10 printf("%s ", Inet_ntop(AF_INET6, Inet6_rth_getaddr(ptr, i),
11 str, sizeof(str)));
Поделиться с друзьями: