UNIX: разработка сетевых приложений
Часть 304 из 399 Информация о книге
Чтобы продемонстрировать использование этих параметров, мы создали UDP-клиент и UDP-сервер. Клиент представлен в листинге 27.5. Он принимает маршрут от отправителя в командной строке подобно TCP-клиенту IPv4, представленному в листинге 27.4. Сервер печатает маршрут полученного сообщения и обращает этот маршрут для отправки сообщения в обратном направлении.
Листинг 27.5. UDP-клиент, использующий маршрутизацию от отправителя
1 #include "unp.h"2 int3 main(int argc, char **argv)4 {5 int с, sockfd, len = 0;6 u_char *ptr = NULL;7 void *rth;8 struct addrinfo *ai;9 if (argc < 2)10 err_quit("usage: udpcli01 [ <hostname> ... ] <hostname>");11 if (argc > 2) {12 int i;13 len = Inet6_rth_space(IPV6_RTHDR_TYPE_0, argc-2);14 ptr = Malloc(len);15 Inet6_rth_init(ptr, len, IPV6_RTHDR_TYPE_0, argc-2);16 for (i = 1; i < argc-1; i++) {17 ai = Host_serv(argv[i], NULL, AF_INET6, 0);18 Inet6_rth_add(ptr,19 &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr);20 }21 }22 ai = Host_serv(argv[argc-1], SERV_PORT_STR, AF_INET6, SOCK_DGRAM);23 sockfd = Socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);24 if (ptr) {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. Примерно то же самое выполнял и TCP-клиент IPv4, за тем исключением, что здесь мы используем библиотечные функции, а не свои собственные.inet6_rth_addПоиск адресата и создание сокетаМы определяем адрес назначения при помощи22-23и создаем сокет для отправки пакетов.host_servУстановка «закрепленного» параметра IPV6_RTHDR и вызов рабочей функцииВ разделе 27.7 будет показано, что не обязательно отправлять одни и те же вспомогательные данные с каждым пакетом. Вместо этого можно вызвать24-27таким образом, что один и тот же заголовок будет добавляться ко всем пакетам в рамках одного сеанса. Этот параметр устанавливается только в том случае, если указательsetsockoptне нулевой, то есть мы уже должны были выделить буфер под заголовок маршрутизации. На последнем этапе мы вызываем рабочую функциюptr, которая не меняется с листинга 8.4.dg_cliПрограмма UDP-сервера не изменилась по сравнению с предыдущими примерами. Сервер открывает сокет и вызывает функцию
. В листинге 27.6 представлена функцияdg_echo, печатающая информацию о маршруте от источника (если таковой был получен) и обращающая этот маршрут для отправки сообщения в обратном направлении.dg_echoЛистинг 27.6. Функция dg_echo, печатающая маршрут
//ipopts/dgechoprintroute.c1 #include "unp.h"2 void3 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) {