ПРИМЕЧАНИЕ
Нам достаточно отправить дейтаграмму, содержащую 0 байт, поскольку ответ сервера времени и даты инициируется самим получением дейтаграммы от клиента, независимо от ее длины и содержания. Но многие реализации SVR4 не допускают нулевой длины дейтаграмм UDP.
Мы запускаем наш клиент, задавая имя узла с записью типа AAAA и типа А. Поскольку функция
getaddrinfo
в первую очередь возвращает структуру с записью типа AAAA, создается сокет IPv6:
freebsd % <b>daytimeudpcli1 aix daytime</b>
sending to 3ffe:b80:1f8d:2:204:acff:fe17:bf38
Sun Jul 23:21:12 2003
Затем мы задаем адрес того же узла в точечно-десятичной записи, в результате чего создается сокет IPv4:
freebsd % <b>daytimeudpcli1 192.168.42.2 daytime</b>
sending to 192.168.42.2
Sun Jul 23:21:40 2003
11.15. Функция udp_connect
Наша функция
udp_connect
создает присоединенный сокет UDP.
#include "unp.h"
int udp_connect(const char *<i>hostname</i>, const char *<i>service</i>);
<i>Возвращает; дескриптор присоединенного сокета в случае успешного выполнения, в случае ошибки ничего не возвращает</i>
В случае присоединенного сокета UDP два последних аргумента, которые требовались в функции
udp_client
, больше не нужны. Вызывающий процесс может вызвать функцию
write
вместо
sendto
, таким образом нашей функции не нужно возвращать структуру адреса сокета и ее длину. В листинге 11.11 представлен исходный код.
Листинг 11.11. Функция udp_connect: создание присоединенного сокета UDP
//lib/udp_connect.c
1 #include "unp.h"
2 int
3 udp_connect(const char *host, const char *serv)
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_connect 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 continue; /* игнорируем этот адрес */
18 if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
19 break; /* успех */
20 Close(sockfd); /* игнорируем этот адрес */
21 } while ((res = res->ai_next) != NULL);
22 if (res == NULL) /* значение errno устанавливается при
последнем вызове функции connect() */
23 err_sys("udp_connect error for %s, %s", host, serv);
24 freeaddrinfo(ressave);
25 return (sockfd);
26 }
Эта функция почти идентична функции
tcp_connect
. Однако отличие в том, что при вызове функции
connect
для сокета UDP ничего не отправляется собеседнику. Если что-то не в порядке (собеседник недоступен или на заданном порте не запущен сервер), вызывающий процесс не обнаружит этого, пока не пошлет собеседнику дейтаграмму.
11.16. Функция udp_server
Наша последняя функция, предоставляющая более простой интерфейс для функции
getaddrinfo
, — это функция
udp_server
.
#include "unp.h"
int udp_server(const char *<i>hostname</i>, const char *<i>service</i>, socklen_t *<i>lenptr</i>);
<i>Возвращает; дескриптор неприсоединенного сокета в случае успешного выполнения, в случае ошибки не возвращает ничего</i>
Аргументы функции те же, что и для функции
tcp_listen
: необязательный
hostname
, обязательный
service
(для связывания номер порта) и необязательный указатель на переменную, в которой возвращается размер структуры адреса сокета. В листинге 11.12 представлен исходный код.
Листинг 11.12. Функция udp_server: создание неприсоединенного сокета для сервера UDP
//lib/udp_server.c
1 #include "unp.h"
2 int
3 udp_server(const char *host, const char *serv, socklen_t *addrlenp)
4 {
5 int sockfd, n;
6 struct addrinfo hints, *res, *ressave;
7 bzero(&hints, sizeof(struct addrinfo));
8 hints.ai_flags = AI_PASSIVE;
9 hints.ai_family = AF_UNSPEC;
10 hints.ai_socktype = SOCK_DGRAM;
11 if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)
12 err_quit("udp_server error for %s, %s: %s",
13 host, serv, gai_strerror(n));