gethostbynameの落とし穴
ここでは、gethostbynameを使ってはまりがちな落とし穴を紹介したいと思います。
変な結果が出るサンプル
以下の例では、二つのテストを行っています。 テストの中ではwww.yahoo.co.jpとwww.google.co.jpの両方に対してgethostbynameを連続的に行っています。 本当はwww.yahoo.co.jpとwww.google.co.jpの両方のIPアドレスが表示されて欲しいのですが、実際には片方しか表示されません。
#include <stdio.h>
#include <winsock2.h>
int
main()
{
WSADATA wsaData;
struct hostent *hostent1, *hostent2;
struct in_addr inaddr1, inaddr2;
WSAStartup(MAKEWORD(2,0), &wsaData);
//
// テスト1
hostent1 = gethostbyname("www.google.co.jp");
hostent2 = gethostbyname("www.yahoo.co.jp");
inaddr1.S_un.S_addr = **(unsigned int **)(hostent1->h_addr_list);
inaddr2.S_un.S_addr = **(unsigned int **)(hostent2->h_addr_list);
printf("test 1\n");
printf("hostent1 : %p\n", hostent1);
printf("hostent2 : %p\n", hostent2);
printf("hostent1 = %s\n", inet_ntoa(inaddr1));
printf("hostent2 = %s\n", inet_ntoa(inaddr2));
printf("\n");
//
// テスト2
hostent1 = gethostbyname("www.yahoo.co.jp");
hostent2 = gethostbyname("www.google.co.jp");
inaddr1.S_un.S_addr = **(unsigned int **)(hostent1->h_addr_list);
inaddr2.S_un.S_addr = **(unsigned int **)(hostent2->h_addr_list);
printf("test 2\n");
printf("hostent1 : %p\n", hostent1);
printf("hostent2 : %p\n", hostent2);
printf("hostent1 = %s\n", inet_ntoa(inaddr1));
printf("hostent2 = %s\n", inet_ntoa(inaddr2));
WSACleanup();
return 0;
}
サンプル実行結果
上記サンプルを実行すると、以下のような結果が出力されます。
C:\> a.exe
|
何故、このような事が発生するのか?
上記サンプルの出力結果は、後に行われたgethostbynameの結果が2度表示されるというものになっています。
gethostbynameは、struct hostent *を返しています。 問題は、このstruct hostent *がどこから来ているかです。 今までwinsockでプログラムを書いていて、gethostbynameが返すstruct hostent *をfree(解放)した覚えは無いと思います。 実は、このstruct hostent *の実体は毎回確保される動的なメモリ領域ではありません。 このstruct hostent *の実体はwinsockの内部にあります。 サンプルの中でhostent1とhostent2のポインタ値を出力していますが、それぞれが全く同じ値になっています。 そのため、このstruct hostent *の実体は最後にgethostbynameが利用された時の値に毎回書き換えられてしまいます。
この問題を回避するために
以上のように、gethostbynameの結果渡されるstruct hostent *の中身は変わってしまいます。 この問題は、Threadを使うと非常に発見しにくいbugにつながる可能性があります。
Windows XPを使っている場合には、gethostbynameの代わりにgetaddrinfoを利用するとこの問題を回避できます。 getaddrinfoの結果は毎回動的に確保され、freeaddrinfoで解放する必要があります。 そのため、gethostbynameのような問題は発生しません。