GETADDRINFO_A - Linux手册页
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)。
| Interface | Attribute | Value |
| getaddrinfo_a(),gai_suspend(),gai_error(),gai_cancel() | Thread safety | MT-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/。

