GETADDRINFO_A - Linux手册页

时间:2019-08-20 18:00:25  来源:igfitidea点击:

Linux程序员手册 第3部分
更新日期: 2020-06-09

名称

getaddrinfo_a,gai_suspend,gai_error,gai_cancel-异步网络地址和服务转换

语法

#define _GNU_SOURCE         /* See 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);

Link with -lanl.

说明

getaddrinfo_a()函数执行与getaddrinfo(3)相同的任务,但是允许异步执行多个名称查找,并在查找操作完成时提供可选通知。

mode参数具有以下值之一:

GAI_WAIT
同步执行查找。呼叫将阻塞,直到查找完成为止。
GAI_NOWAIT
异步执行查找。调用立即返回,并且在后台解决请求。请参阅下面有关sevp参数的讨论。

数组列表指定要处理的查找请求。 nitems参数指定列表中元素的数量。所请求的查找操作并行开始。列表中的NULL元素将被忽略。每个请求均由gaicb结构描述,该结构定义如下:

struct gaicb {
    const char            *ar_name;
    const char            *ar_service;
    const struct addrinfo *ar_request;
    struct addrinfo       *ar_result;
};

此结构的元素对应于getaddrinfo(3)的参数。因此,ar_name对应于节点参数,ar_service对应于服务参数,标识Internet主机和服务。 ar_request元素对应于hints参数,用于指定选择返回的套接字地址结构的条件。最后,ar_result对应于res参数;您不需要初始化此元素,它将在请求解决后自动设置。最后两个元素引用的addrinfo结构在getaddrinfo(3)中进行了描述。

将mode指定为GAI_NOWAIT时,可以通过使用sevp参数指向的sigevent结构来获取有关已解决请求的通知。有关此结构的定义和一般详细信息,请参见sigevent(7)。 sevp-> sigev_notify字段可以具有以下值:

SIGEV_NONE
不提供任何通知。
SIGEV_SIGNAL
查找完成后,为该过程生成信号sigev_signo。有关常规详细信息,请参见sigevent(7)。 siginfo_t结构的si_code字段将设置为SI_ASYNCNL。
SIGEV_THREAD
查找完成后,将sigev_notify_function当作新线程的启动函数来调用。有关详细信息,请参见sigevent(7)。

对于SIGEV_SIGNAL和SIGEV_THREAD,将sevp-> sigev_value.sival_ptr指向列表可能会很有用。

gai_suspend()函数挂起调用线程,等待数组列表中一个或多个请求的完成。 nitems参数指定数组列表的大小。呼叫将阻塞,直到发生以下情况之一:

*
列表中的一项或多项操作已完成。
*
呼叫被捕获的信号中断。
*
经过超时中指定的时间间隔。此参数指定以秒加纳秒为单位的超时(有关timespec结构的详细信息,请参见nanosleep(2))。如果超时为NULL,则调用将无限期阻塞(直到发生以上事件之一)。

没有明确指出已完成哪个请求;您必须通过遍历请求列表中的gai_error()来确定完成了哪些请求。

gai_error()函数返回请求请求的状态:如果请求尚未完成,则为EAI_INPROGRESS;如果成功处理,则为0;如果请求无法解决,则返回错误代码。

gai_cancel()函数取消请求要求。如果请求已成功取消,则请求的错误状态将设置为EAI_CANCELED并执行正常的异步通知。如果当前正在处理请求,则无法取消该请求;在这种情况下,它将像从未调用过gai_cancel()一样进行处理。如果req为NULL,则尝试取消该进程提出的所有未完成的请求。

返回值

如果所有请求都已成功入队,则getaddrinfo_a()函数将返回0,或者返回以下非零错误代码之一:

EAI_AGAIN
排队查询请求所需的资源不可用。应用程序可以检查每个请求的错误状态,以确定哪些请求失败。
EAI_MEMORY
内存不足。
EAI_SYSTEM
模式无效。

如果至少一个列出的请求已完成,则gai_suspend()函数将返回0。否则,它将返回以下非零错误代码之一:

EAI_AGAIN
给定的超时在任何请求都可以完成之前已过期。
EAI_ALLDONE
没有实际的功能要求。
EAI_INTR
信号中断了该功能。请注意,此中断可能是由于某些已完成的查找请求的信号通知引起的。

gai_error()函数可以为未完成的查找请求返回EAI_INPROGRESS,为成功完成的查找返回0(如上所述),getaddrinfo(3)可能返回的错误代码之一或错误代码EAI_CANCELED如果在完成请求之前已明确取消了该请求。

gai_cancel()函数可以返回以下值之一:

EAI_CANCELED
该请求已成功取消。
EAI_NOTCANCELED
该请求尚未取消。
EAI_ALLDONE
该请求已完成。

gai_strerror(3)函数将这些错误代码转换为人类可读的字符串,适用于错误报告。

属性

有关本节中使用的术语的说明,请参见attribute(7)。

InterfaceAttributeValue
getaddrinfo_a(),gai_suspend(),gai_error(),gai_cancel()Thread safetyMT-Safe

遵循规范

这些功能是GNU扩展。它们首先出现在2.2.3版的glibc中。

备注

getaddrinfo_a()的接口是根据lio_listio(3)接口建模的。

示例

提供了两个示例:一个简单的示例,该示例同步并行解决多个请求,另一个复杂的示例显示了一些异步功能。

Synchronous example

下面的程序仅简单地并行解析几个主机名,与使用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, "Usage: %s HOST...\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() failed: %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() failed: %s\n",
                        gai_strerror(ret));
                exit(EXIT_FAILURE);
            }
            puts(host);

        } else {
            puts(gai_strerror(ret));
        }
    }
    exit(EXIT_SUCCESS);
}

Asynchronous example

此示例显示了一个简单的交互式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: Finished
> 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] == aq\naq)
        buf[strlen(buf) - 1] = 0;

    return buf;
}

/* Add requests for specified hostnames */
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);
    }

    /* Queue nreqs_base..nreqs requests. */

    ret = getaddrinfo_a(GAI_NOWAIT, &reqs[nreqs_base],
                        nreqs - nreqs_base, NULL);
    if (ret) {
        fprintf(stderr, "getaddrinfo_a() failed: %s\n",
                gai_strerror(ret));
        exit(EXIT_FAILURE);
    }
}

/* Wait until at least one of specified requests completes */
static void
wait_requests(void)
{
    char *id;
    int i, ret, n;
    struct gaicb const **wait_reqs = calloc(nreqs, sizeof(*wait_reqs));
                /* NULL elements are ignored by gai_suspend(). */

    while ((id = strtok(NULL, " ")) != NULL) {
        n = atoi(id);

        if (n >= nreqs) {
            printf("Bad request number: %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 ? "Finished" : gai_strerror(ret));
    }
}

/* Cancel specified requests */
static void
cancel_requests(void)
{
    char *id;
    int ret, n;

    while ((id = strtok(NULL, " ")) != NULL) {
        n = atoi(id);

        if (n >= nreqs) {
            printf("Bad request number: %s\n", id);
            return;
        }

        ret = gai_cancel(reqs[n]);
        printf("[%s] %s: %s\n", id, reqs[atoi(id)]->ar_name,
               gai_strerror(ret));
    }
}

/* List all requests */
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() failed: %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 aqaaq:
                add_requests();
                break;
            case aqwaq:
                wait_requests();
                break;
            case aqcaq:
                cancel_requests();
                break;
            case aqlaq:
                list_requests();
                break;
            default:
                fprintf(stderr, "Bad command: %c\n", cmd[0]);
                break;
            }
        }
    }
    exit(EXIT_SUCCESS);
}

另外参见

getaddrinfo(3),inet(3),lio_listio(3),主机名(7),ip(7),sigevent(7)

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/