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/。