Литмир - Электронная Библиотека
Содержание  
A
A

Мы только что показали, что сервер IPv6, работающий на узле с двойным стеком, может обрабатывать как клиенты IPv4, так и клиенты IPv6. Адреса IPv4-клиента передаются серверу IPv6 как адреса IPv4, преобразованные к виду IPv6, что мы рассматривали в разделе 12.2.

11.14. Функция udp_client

Наши функции, предоставляющие более простой интерфейс для функции

getaddrinfo
, в случае UDP изменяются: в этом разделе мы представляем клиентскую функцию, создающую неприсоединенный сокет UDP, а в следующем — другую функцию, создающую присоединенный сокет UDP.

#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
 — это адрес указателя (объявляемого вызывающим процессом) на структуру адреса сокета (которая динамически размещается в памяти функцией
udp_client
), и в этой структуре функция хранит IP-адрес получателя и номер порта для будущих вызовов функции
sendto
. Размер этой структуры адреса сокета возвращается как значение переменной, на которую указывает
lenp
. Последний аргумент не может быть пустым указателем (как это допустимо для последнего аргумента функции
tcp_listen
), поскольку длина структуры адреса сокета требуется в любых вызовах функций
sendto
и
recvfrom
.

В листинге 11.9 показан исходный код для этой функции.

Листинг 11.9. Функция udp_client: создание неприсоединенного сокета UDP

//lib/udp_client.c

 1 #include &quot;unp.h&quot;

 2 int

 3 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(&amp;hints, sizeof(struct addrinfo));

 8  hints.ai_family = AF_UNSPEC;

 9  hints.ai_socktype = SOCK_DGRAM;

10  if ((n = getaddrinfo(host, serv, &amp;hints, &amp;res)) != 0)

11   err_quit(&quot;udp_client error for %s, %s: %s&quot;,

12   host, serv, gai_strerror(n));

13  ressave = res;

14  do {

15   sockfd = socket(res-&gt;ai_family, res-&gt;ai_socktype, res-&gt;ai_protocol);

16   if (sockfd &gt;= 0)

17    break; /* успех */

18  } while ((res = res-&gt;ai_next) != NULL);

19  if (res == NULL) /* значение errno устанавливается при последнем

                        вызове функции socket() */

20  err_sys(&quot;udp_client error for %s, %s&quot;, host, serv);

21  *saptr = Malloc(res-&gt;ai_addrlen);

22  memcpy(*saptr, res-&gt;ai_addr, res-&gt;ai_addrlen);

23  *lenp = res-&gt;ai_addrlen;

24  freeaddrinfo(ressave);

25  return (sockfd);

26 }

Функция

getaddrinfo
преобразует аргументы
hostname
и
service
. Создается дейтаграммный сокет. Выделяется память для одной структуры адреса сокета, и структура адреса сокета, соответствующая созданному сокету, копируется в память.

Пример: не зависящий от протокола UDP-клиент времени и даты

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

udp_client
. В листинге 11.10 представлен не зависящий от протокола исходный код.

Листинг 11.10. UDP-клиент времени и даты, использующий нашу функцию udp_client

//names/daytimeudpcli1.c

 1 #include &quot;unp.h&quot;

 2 int

 3 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_quit

11    (&quot;usage; daytimeudpcli1 &lt;hostname/IPaddress&gt; &lt;service/port#&gt;&quot;);

12  sockfd = Udp_client(argv[1], argv[2], (void**)&amp;sa, &amp;salen);

13  printf(&quot;sending to %s\n&quot;, Sock_ntop_host(sa, salen));

14  Sendto(sockfd, &quot;&quot;, 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
 Мы вызываем нашу функцию
udp_client
и затем выводим IP-адрес и порт сервера, которому мы отправим нашу дейтаграмму UDP. Мы посылаем однобайтовую дейтаграмму и затем читаем и выводим ответ сервера.

135
{"b":"225366","o":1}