| GETADDRINFO_A(3) | Руководство программиста Linux | GETADDRINFO_A(3) |
getaddrinfo_a, gai_suspend, gai_error, gai_cancel - асинхронная трансляция сетевого адреса и службы
#define _GNU_SOURCE /* См. feature_test_macros(7) */ #include <netdb.h>
int getaddrinfo_a(int mode, struct gaicb *list[],
int nitems, struct sigevent *sevp);
int gai_suspend(const struct gaicb * const list[], int nitems,
const struct timespec *timeout);
int gai_error(struct gaicb *req);
int gai_cancel(struct gaicb *req);
Компонуется при указании параметра -lanl.
Функция getaddrinfo_a() выполняет ту же задачу что и getaddrinfo(3), но позволяет выполнять поиск нескольких имён асинхронно, с дополнительным уведомлением о завершении операций поиска.
В аргументе mode указывается одно из следующих значений:
В массиве list задаются запросы на обработку. В аргументе nitems задаётся количество элементов в list. Запрашиваемые операции поиска начинаются параллельно. Элементы NULL в списке list игнорируются. Каждый запрос описывается структурой gaicb, которая определена следующим образом:
struct gaicb {
const char *ar_name;
const char *ar_service;
const struct addrinfo *ar_request;
struct addrinfo *ar_result;
};
Элементы данной структуры совпадают с аргументами getaddrinfo(3). То есть ar_name соответствует аргументу node, а ar_service аргументу service (определяют узел Интернета и службу). Элемент ar_request соответствует аргументу hints; им задаётся критерий выбора структуры возвращаемого адреса сокета. И, наконец, ar_result соответствует аргументу res; вам не нужно инициализировать этот элемент, он будет заполнен автоматически в результате запроса. Структура addrinfo, на которую ссылаются последние два элемента, описана в getaddrinfo(3).
Если значение mode равно GAI_NOWAIT, то уведомления о обработанных запросах можно получить из структуры sigevent, на которую указывает аргумент sevp. Определение и описание данной структуры приведено в sigevent(7). Поле sevp->sigev_notify может иметь следующие значения:
При SIGEV_SIGNAL и SIGEV_THREAD, может быть полезно, чтобы sevp->sigev_value.sival_ptr указывала на list.
Функция gai_suspend() приостанавливает выполнение вызывающей нити, ожидая завершения поиска одного или более запросов из массива list. В аргументе nitems задаётся размер массива list. Вызов блокирует выполнение пока не произойдёт одно из следующего:
При выполнении явно не указывается какие запросы завершены; для определения вам нужно обойти весь список запросов с помощью gai_error().
Функция gai_error() возвращает состояние запроса req: EAI_INPROGRESS — запрос пока не выполнен, 0 — обработан успешно, код ошибки — запрос невозможно обработать.
Функция gai_cancel() отменяет запрос req. При успешной отмене состояние ошибки устанавливается в EAI_CANCELED и выполняется обычное асинхронное уведомление. Запрос не может быть отменён, если он начал обрабатываться; в этом случае действие будет доведено до конца, как если бы вызова gai_cancel() не происходило. Если req равно NULL, то будет предпринята попытка отменить все имеющиеся запросы.
Функция getaddrinfo_a() возвращает 0, если все запросы были успешно обработаны или один из следующих ненулевых кодов ошибки:
Функция gai_suspend() возвращает 0, если завершён хотя бы один из запросов. В противном случае возвращается один из следующих ненулевых кодов ошибки:
Функция gai_error() может вернуть EAI_INPROGRESS для незаконченных запросов поиска, 0 при успешном поиске (как описано выше), один из кодов ошибок, которые может вернуть getaddrinfo(3) или код ошибки EAI_CANCELED, если запрос был отменён явно до завершения.
Функция gai_cancel() может вернуть одно из следующих значений:
Функция gai_strerror(3) транслирует эти коды ошибок в читаемый формат, подходящий для сообщений об ошибке.
Описание терминов данного раздела смотрите в attributes(7).
| Интерфейс | Атрибут | Значение |
| getaddrinfo_a(), gai_suspend(), gai_error(), gai_cancel() | Безвредность в нитях | MT-Safe |
Эти функции являются расширениями GNU, доступными в glibc начиная с версии 2.2.3.
Интерфейс getaddrinfo_a() был создан после интерфейса lio_listio(3).
Вот два примера: простой пример выполнения нескольких запросов синхронно одновременно, и сложный пример, показывающий асинхронные возможности.
Эта программа определяет несколько имён узлов параллельно, что быстрее по сравнению с определением имён последовательно с помощью getaddrinfo(3). Результат работы программы:
$ ./a.out ftp.us.kernel.org enoent.linuxfoundation.org gnu.cz ftp.us.kernel.org: 128.30.2.36 enoent.linuxfoundation.org: Name or service not known gnu.cz: 87.236.197.13
Исходный код программы
#define _GNU_SOURCE
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int i, ret;
struct gaicb *reqs[argc - 1];
char host[NI_MAXHOST];
struct addrinfo *res;
if (argc < 2) {
fprintf(stderr, "Использование: %s УЗЕЛ...\n", argv[0]);
exit(EXIT_FAILURE);
}
for (i = 0; i < argc - 1; i++) {
reqs[i] = malloc(sizeof(*reqs[0]));
if (reqs[i] == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
memset(reqs[i], 0, sizeof(*reqs[0]));
reqs[i]->ar_name = argv[i + 1];
}
ret = getaddrinfo_a(GAI_WAIT, reqs, argc - 1, NULL);
if (ret != 0) {
fprintf(stderr, "ошибка getaddrinfo_a(): %s\n",
gai_strerror(ret));
exit(EXIT_FAILURE);
}
for (i = 0; i < argc - 1; i++) {
printf("%s: ", reqs[i]->ar_name);
ret = gai_error(reqs[i]);
if (ret == 0) {
res = reqs[i]->ar_result;
ret = getnameinfo(res->ai_addr, res->ai_addrlen,
host, sizeof(host),
NULL, 0, NI_NUMERICHOST);
if (ret != 0) {
fprintf(stderr, "ошибка getnameinfo(): %s\n",
gai_strerror(ret));
exit(EXIT_FAILURE);
}
puts(host);
} else {
puts(gai_strerror(ret));
}
}
exit(EXIT_SUCCESS);
}
Данный пример — простая интерактивная оболочка к getaddrinfo_a(). Возможности уведомления не используются.
Результат работы программы:
$ ./a.out > a ftp.us.kernel.org enoent.linuxfoundation.org gnu.cz > c 2 [2] gnu.cz: Request not canceled > w 0 1 [00] ftp.us.kernel.org: Выполнено > l [00] ftp.us.kernel.org: 216.165.129.139 [01] enoent.linuxfoundation.org: Processing request in progress [02] gnu.cz: 87.236.197.13 > l [00] ftp.us.kernel.org: 216.165.129.139 [01] enoent.linuxfoundation.org: Name or service not known [02] gnu.cz: 87.236.197.13
Исходный код программы:
#define _GNU_SOURCE
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static struct gaicb **reqs = NULL;
static int nreqs = 0;
static char *
getcmd(void)
{
static char buf[256];
fputs("> ", stdout); fflush(stdout);
if (fgets(buf, sizeof(buf), stdin) == NULL)
return NULL;
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = 0;
return buf;
}
/* добавление запросов задаваемых имён */
static void
add_requests(void)
{
int nreqs_base = nreqs;
char *host;
int ret;
while ((host = strtok(NULL, " "))) {
nreqs++;
reqs = realloc(reqs, nreqs * sizeof(reqs[0]));
reqs[nreqs - 1] = calloc(1, sizeof(*reqs[0]));
reqs[nreqs - 1]->ar_name = strdup(host);
}
/* очередь запросов nreqs_base..nreqs. */
ret = getaddrinfo_a(GAI_NOWAIT, &reqs[nreqs_base],
nreqs - nreqs_base, NULL);
if (ret) {
fprintf(stderr, "ошибка getaddrinfo_a(): %s\n",
gai_strerror(ret));
exit(EXIT_FAILURE);
}
}
/* ждём, пока один из запросов не выполнится */
static void
wait_requests(void)
{
char *id;
int i, ret, n;
struct gaicb const **wait_reqs = calloc(nreqs, sizeof(*wait_reqs));
/* элементы NULL игнорируются gai_suspend(). */
while ((id = strtok(NULL, " ")) != NULL) {
n = atoi(id);
if (n >= nreqs) {
printf("Неправильный номер запроса: %s\n", id);
return;
}
wait_reqs[n] = reqs[n];
}
ret = gai_suspend(wait_reqs, nreqs, NULL);
if (ret) {
printf("gai_suspend(): %s\n", gai_strerror(ret));
return;
}
for (i = 0; i < nreqs; i++) {
if (wait_reqs[i] == NULL)
continue;
ret = gai_error(reqs[i]);
if (ret == EAI_INPROGRESS)
continue;
printf("[%02d] %s: %s\n", i, reqs[i]->ar_name,
ret == 0 ? "Выполнено" : gai_strerror(ret));
}
}
/* отменяет заданные запросы */
static void
cancel_requests(void)
{
char *id;
int ret, n;
while ((id = strtok(NULL, " ")) != NULL) {
n = atoi(id);
if (n >= nreqs) {
printf("Неправильный номер запроса: %s\n", id);
return;
}
ret = gai_cancel(reqs[n]);
printf("[%s] %s: %s\n", id, reqs[atoi(id)]->ar_name,
gai_strerror(ret));
}
}
/* перечислим все запросы */
static void
list_requests(void)
{
int i, ret;
char host[NI_MAXHOST];
struct addrinfo *res;
for (i = 0; i < nreqs; i++) {
printf("[%02d] %s: ", i, reqs[i]->ar_name);
ret = gai_error(reqs[i]);
if (!ret) {
res = reqs[i]->ar_result;
ret = getnameinfo(res->ai_addr, res->ai_addrlen,
host, sizeof(host),
NULL, 0, NI_NUMERICHOST);
if (ret) {
fprintf(stderr, "ошибка getnameinfo(): %s\n",
gai_strerror(ret));
exit(EXIT_FAILURE);
}
puts(host);
} else {
puts(gai_strerror(ret));
}
}
}
int
main(int argc, char *argv[])
{
char *cmdline;
char *cmd;
while ((cmdline = getcmd()) != NULL) {
cmd = strtok(cmdline, " ");
if (cmd == NULL) {
list_requests();
} else {
switch (cmd[0]) {
case 'a':
add_requests();
break;
case 'w':
wait_requests();
break;
case 'c':
cancel_requests();
break;
case 'l':
list_requests();
break;
default:
fprintf(stderr, "Неверная команда: %c\n", cmd[0]);
break;
}
}
}
exit(EXIT_SUCCESS);
}
getaddrinfo(3), inet(3), lio_listio(3), hostname(7), ip(7), sigevent(7)
| 2019-03-06 | GNU |