Мы только что показали, что сервер 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. Мы посылаем однобайтовую дейтаграмму и затем читаем и выводим ответ сервера.


Перейти на страницу:
Изменить размер шрифта: