GETADDRINFO_A(3) | Linux Programmer's Manual | GETADDRINFO_A(3) |
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 引き数は以下の値のいずれかを指定する。
配列 list は処理すべき検索要求を指定する。 nitems 引き数は list の要素数を指定する。 要求された検索命令は並列に開始される。 list の 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 はインターネットホストを示す 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_SIGNAL と SIGEV_THREAD では、 sevp->sigev_value.sival_ptr が list を指すようにしておくと役立つことがある。
gai_suspend() 関数は呼び出し元のスレッドの実行を中断し、 配列 list 内の一つ以上の要求が完了するのを待つ。 nitems 引き数は配列 list の大きさを指定する。 呼び出しは以下のいずれかの状況になるまで停止する。
どの要求が完了したかは明示的な通知は行われない。 どの要求が完了したかを知るためには、 要求のリストに対して gai_error() を繰り返し呼び出す必要がある。
gai_error() 関数は要求 req のステータスを返す。 要求がまだ完了していない場合は EAI_INPROGRESS が、 要求が正常に処理された場合は 0 が、 要求を解決できなかった場合はエラーコードが返される。
gai_cancel() 関数は要求 req をキャンセルする。 要求が正常にキャンセルされた場合、 要求のエラーステータスに EAI_CANCELLED が設定され、 通常の非同期通知が実行される。 要求が現在処理中でキャンセルできない場合もある。 この場合 gai_cancel() が呼ばれなかったかのように処理が行われる。 req が NULL の場合、 そのプロセスが行ったすべての処理中の要求をキャンセルしようとする。
getaddrinfo_a() 関数はすべての要求が正常にキューに追加されると 0 を返す。 または、以下のいずれかの 0 でないエラーコードを返す。
gai_suspend() 関数はリストの要求の少なくともひとつが完了すると 0 を返す。 それ以外の場合、 以下の 0 でないエラーコードのいずれかを返す。
gai_error() 関数は、 完了していない検索要求に対して EAI_INPROGRESS を返し、 成功で完了した検索に対して 0 を返す。 getaddrinfo(3) が返すエラーコードのいずれかを返す場合もある。 要求の完了前に明示的に要求がキャンセルされた場合はエラーコード EAI_CANCELLED を返す。
gai_cancel() 関数はこれらの値のいずれかを返すことがある。
gai_strerror(3) 関数を使うと、 これらのエラーコードを、 エラーレポートに適した人間が読みやすい文字列に翻訳してくれる。
これらの関数は 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 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); }
この例は 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, 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 '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 プロジェクトのリリース 3.79 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。
2014-05-28 | GNU |