getaddrinfo_a(3) асинхронная трансляция

Other Alias

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 указывается одно из следующих значений:

GAI_WAIT
Выполнять поиск синхронно. Вызов блокирует выполнение пока поиск не завершится.
GAI_NOWAIT
Выполнять поиск асинхронно. Вызов сразу завершается и запросы обрабатываются в фоновом режиме. Смотрите далее описание параметра sevp.

В массиве 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_NONE
Отключить уведомление.
SIGEV_SIGNAL
При завершении поиска послать процессу сигнал sigev_signo. Подробности смотрите в sigevent(7). Полю si_code структуры siginfo_t присваивается значение SI_ASYNCNL.
SIGEV_THREAD
При завершении поиска вызвать sigev_notify_function, как если бы с этой функции начиналась бы новая нить. Подробности смотрите в sigevent(7).

При SIGEV_SIGNAL и SIGEV_THREAD, может быть полезно, чтобы sevp->sigev_value.sival_ptr указывала на list.

Функция gai_suspend() приостанавливает выполнение вызывающей нити, ожидая завершения поиска одного или более запросов из массива list. В аргументе nitems задаётся размер массива list. Вызов блокирует выполнение пока не произойдёт одно из следующего:

*
Завершится операция для одного или более запросов из list.
*
Вызов прервётся пойманным сигналом.
*
Временной интервал ожидания задаётся в timeout. В данном аргумента указывается промежуток в секундах плюс наносекундах (о структуре timespec смотрите nanosleep(2)). Если timeout равно NULL, то вызов блокирует выполнение навсегда (пока не произойдёт одно из событий выше).

При выполнении явно не указывается какие запросы завершены; для определения вам нужно обойти весь список запросов с помощью gai_error().

Функция gai_error() возвращает состояние запроса req: EAI_INPROGRESS — запрос пока не выполнен, 0 — обработан успешно, код ошибки — запрос невозможно обработать.

Функция gai_cancel() отменяет запрос req. При успешной отмене состояние ошибки устанавливается в EAI_CANCELLED и выполняется обычное асинхронное уведомление. Запрос не может быть отменён, если он начал обрабатываться; в этом случае действие будет доведено до конца, как если бы вызова gai_cancel() не происходило. Если req равно NULL, то будет предпринята попытка отменить все имеющиеся запросы.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

Функция getaddrinfo_a() возвращает 0, если все запросы были успешно обработаны или один из следующих ненулевых кодов ошибки:
EAI_AGAIN
Недоступны ресурсы, необходимые, чтобы постановки запросов поиска в очередь. Для нахождения проблемного приложение может проверить состояние ошибки каждого запроса.
EAI_MEMORY
Не хватает памяти.
EAI_SYSTEM
Неверное значение mode.

Функция gai_suspend() возвращает 0, если завершён хотя бы один из запросов. В противном случае возвращается один из следующих ненулевых кодов ошибки:

EAI_AGAIN
Указанный интервал истёк до завершения хотят бы одного из запросов.
EAI_ALLDONE
В функцию не было передано никаких фактических запросов.
EAI_INTR
Функция прервана сигналом. Заметим, что такое прерывание может быть вызвано сигналом уведомления о каком-то выполненном запросе.

Функция gai_error() может вернуть EAI_INPROGRESS для незаконченных запросов поиска, 0 при успешном поиске (как описано выше), один из кодов ошибок, которые может вернуть getaddrinfo(3) или код ошибки EAI_CANCELLED, если запрос был отменён явно до завершения.

Функция gai_cancel() может вернуть одно из следующих значений:

EAI_CANCELLED
Запрос успешно отменён.
EAI_NOTCANCELLED
Запрос не был отменён.
EAI_ALLDONE
Запрос уже выполнен.

Функция 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);
}