名前
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 引数は以下の値のいずれかを指定する。- GAI_WAIT
- 検索を同期で実行する。 呼び出しは検索が完了するまで停止 (block) する。
- GAI_NOWAIT
- 検索を非同期で実行する。 呼び出しは直ちに返り、 要求はバックグラウンドで処理される。 以下の sevp 引数の議論を参照。
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_request は res 引数に対応する。 この要素を初期化する必要はなく、この要素は要求が解決されると自動的にセットされる。 最後の 2 つの要素が参照している 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) を参照。
- *
- list 内の一つ以上の操作が完了した。
- *
- 呼び出しが補足されたシグナルに割り込まれた。
- *
- timeout で指定された期間が経過した。 この引数は、秒とナノ秒でタイムアウトを指定する ( timespec 構造体の詳細は nanosleep(2) を参照)。 timeout が NULL の場合、 (上記のイベントのいずれかが発生するまで) 呼び出しは無限に停止する。
返り値
getaddrinfo_a() 関数はすべての要求が正常にキューに追加されると 0 を返す。 または、以下のいずれかの 0 でないエラーコードを返す。- EAI_AGAIN
- 検索要求をキューに入れるために必要なリソースがなかった。 アプリケーションは書く要求のエラーステータスを確認し、 どの要求が失敗したかを判定することができる。
- EAI_MEMORY
- メモリーが足りない。
- EAI_SYSTEM
- mode が無効である。
- EAI_AGAIN
- いずれかの要求が完了する前に指定されたタイムアウト時間が満了した。
- EAI_ALLDONE
- 指定された関数には実際には要求がなかった。
- EAI_INTR
- シグナルが関数に割り込んだ。 この割り込みは検索要求が完了したことを示すシグナル通知により起こる場合もある。
- EAI_CANCELED
- 要求は正常にキャンセルされた。
- EAI_NOTCANCELED
- 要求はキャンセルされていない。
- EAI_ALLDONE
- 要求はすでに完了している。
属性
この節で使用されている用語の説明は attributes(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) インターフェースの後にモデル化された。例
ここでは二つの例を示す。 一つは複数の要求を同期処理で並行して解決する例で、 もう一つは非同期機能を使った複雑な例である。同期型の例
以下のプログラムは単に複数のホスト名の解決を並行で行う。 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 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 (int 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 (int 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); }
非同期型の例
この例は 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] == '\n') 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, sizeof(reqs[0]) * nreqs); 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 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 (int 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 ret; char host[NI_MAXHOST]; struct addrinfo *res; for (int 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 'a': add_requests(); break; case 'w': wait_requests(); break; case 'c': cancel_requests(); break; case 'l': list_requests(); break; default: fprintf(stderr, "Bad command: %c\n", cmd[0]); break; } } } exit(EXIT_SUCCESS); }
関連項目
getaddrinfo(3), inet(3), lio_listio(3), hostname(7), ip(7), sigevent(7)この文書について
この man ページは Linux man-pages プロジェクトのリリース 5.10 の一部である。プロジェクトの説明とバグ報告に関する情報は https://www.kernel.org/doc/man-pages/ に書かれている。2020-11-01 | GNU |