UNIX: разработка сетевых приложений
Мы только что показали, что сервер IPv6, работающий на узле с двойным стеком, может обрабатывать как клиенты IPv4, так и клиенты IPv6. Адреса IPv4-клиента передаются серверу IPv6 как адреса IPv4, преобразованные к виду IPv6, что мы рассматривали в разделе 12.2.
11.14. Функция udp_client
Наши функции, предоставляющие более простой интерфейс для функции
, в случае UDP изменяются: в этом разделе мы представляем клиентскую функцию, создающую неприсоединенный сокет UDP, а в следующем — другую функцию, создающую присоединенный сокет UDP.getaddrinfo#include "unp.h"int udp_client(const char *<i>hostname</i>, const char *<i>service</i>,void **<i>saptr</i>, socklen_t *<i>lenp</i>);<i>Возвращает: дескриптор неприсоединенного сокета в случае успешного выполнения, в случае ошибки не возвращает ничего</i>Эта функция создает неприсоединенный сокет UDP, возвращая три элемента. Во-первых, возвращаемое значение функции — это дескриптор сокета. Во-вторых,
— это адрес указателя (объявляемого вызывающим процессом) на структуру адреса сокета (которая динамически размещается в памяти функциейsaptr), и в этой структуре функция хранит IP-адрес получателя и номер порта для будущих вызовов функцииudp_client. Размер этой структуры адреса сокета возвращается как значение переменной, на которую указываетsendto. Последний аргумент не может быть пустым указателем (как это допустимо для последнего аргумента функцииlenp), поскольку длина структуры адреса сокета требуется в любых вызовах функцийtcp_listenиsendto.recvfromВ листинге 11.9 показан исходный код для этой функции.
Листинг 11.9. Функция udp_client: создание неприсоединенного сокета UDP
//lib/udp_client.c1 #include "unp.h"2 int3 udp_client(const char *host, const char *serv, void **saptr, socklen_t *lenp)4 {5 int sockfd, n;6 struct addrinfo hints, *res, *ressave;7 bzero(&hints, sizeof(struct addrinfo));8 hints.ai_family = AF_UNSPEC;9 hints.ai_socktype = SOCK_DGRAM;10 if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)11 err_quit("udp_client error for %s, %s: %s",12 host, serv, gai_strerror(n));13 ressave = res;14 do {15 sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);16 if (sockfd >= 0)17 break; /* успех */18 } while ((res = res->ai_next) != NULL);19 if (res == NULL) /* значение errno устанавливается при последнемвызове функции socket() */20 err_sys("udp_client error for %s, %s", host, serv);21 *saptr = Malloc(res->ai_addrlen);22 memcpy(*saptr, res->ai_addr, res->ai_addrlen);23 *lenp = res->ai_addrlen;24 freeaddrinfo(ressave);25 return (sockfd);26 }Функция
преобразует аргументыgetaddrinfoиhostname. Создается дейтаграммный сокет. Выделяется память для одной структуры адреса сокета, и структура адреса сокета, соответствующая созданному сокету, копируется в память.serviceПример: не зависящий от протокола UDP-клиент времени и даты
Теперь мы перепишем наш клиент времени и даты, показанный в листинге 11.3, так, чтобы в нем использовалась наша функция
. В листинге 11.10 представлен не зависящий от протокола исходный код.udp_clientЛистинг 11.10. UDP-клиент времени и даты, использующий нашу функцию udp_client
//names/daytimeudpcli1.c1 #include "unp.h"2 int3 main(int argc, char **argv)4 {5 int sockfd, n;6 char recvline[MAXLINE + 1];7 socklen_t salen;8 struct sockaddr *sa;9 if (argc != 3)10 err_quit11 ("usage; daytimeudpcli1 <hostname/IPaddress> <service/port#>");12 sockfd = Udp_client(argv[1], argv[2], (void**)&sa, &salen);13 printf("sending to %s\n", Sock_ntop_host(sa, salen));14 Sendto(sockfd, "", 1, 0, sa, salen); /* посылается 1-байтоваядейтаграмма */15 n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);16 recvline[n] = 0; /* завершающий пустой байт */17 Fputs(recvline, stdout);18 exit(0);19 }Мы вызываем нашу функцию12-17и затем выводим IP-адрес и порт сервера, которому мы отправим нашу дейтаграмму UDP. Мы посылаем однобайтовую дейтаграмму и затем читаем и выводим ответ сервера.udp_client