ホスト名とアドレス


コネクト・ネットワーク

WinSock の初期化が終われば、いよいよネットワークへの接続を試みます
しかし、他のコンピュータに接続するためには、名前の問題があります
つまり、コンピュータを一意に識別する情報が必要なのです

TCP/IP において、コンピュータを検出するには IP を用います
IP は ***.***.***.*** という形式の32ビットで表現される識別子です
それぞれの *** には 0 〜 255 までのいずれかの数値が入ります

もしくは、ホスト名を用いてコンピュータを探す方法があります
これは、お馴染みの microsoft.com というような文字による識別です
この場合、一度 DNS サーバーに接続して IP に変換する必要があります

まずは、ホスト名を使ってコンピューターの情報を取得しましょう
幸い、DNS との接続や名前の解決は、全て WinSock が担当してくれます
名前の解決は gethostbyname() 関数を使います

struct hostent FAR * gethostbyname (const char FAR * name);

name には解決するべきホスト名へのポインタを指定します
関数は、成功すればホストの情報を格納する構造体へのポインタを
失敗すれば NULL を返します

hostent 構造体は、ホストのエイリアスや IP を受け取ります
hostent は別名として HOSTENT としても利用することができます
この構造体は、次のように定義されています
struct hostent {
    char FAR *       h_name;
    char FAR * FAR * h_aliases;
    short            h_addrtype;
    short            h_length;
    char FAR * FAR * h_addr_list;
};
通常、hostent の変数を作ろうとすると struct を指定する必要があるため
別名の HOSTENT や LPHOSTENT を使って変数を宣言します

h_name にはホストの公式名が格納されます
h_aliases は、ホストの別名が格納されている配列へのポインタです
この配列は NULL で終わります

h_addrtype は、要求したアドレスの種類を表す定数です
通常は、インターネットアドレスファミリを表す AF_INET が格納されます
h_length はアドレスのサイズをバイト単位で表します
通常の IP であれば 4 バイト、IPv6 であれば 16 バイトとなるでしょう

h_addr_list には、ネットワークアドレスの情報へのポインタの配列です
この配列も h_aliases 同様に NULL で終了することを保障しています

ホストの別名やアドレスは、ひとつのホストが複数保有する可能性があります
実際に、複数の IP やホスト名を保有する企業が存在します
そのため、h_aliases や h_addr_list は配列へのポインタとなっているのです
ただし、別名を持たないコンピュータも存在します

h_addr_list メンバは、char 型のポインタとなっていますが、文字列ではありません
このメンバの配列が表す情報は、数バイトのアドレスを表す数列です
ただし、アドレスサイズは h_addrtype で異なるので注意してください
#include <stdio.h>
#include <winsock2.h>

int main(int argc , char *argv[]) {
	LPHOSTENT host;
	WSADATA wsaData;
	int iCount;

	if (argc == 1) return 0;
	
	WSAStartup(2 , &wsaData);
	host = gethostbyname(argv[1]);
	if (host == NULL) {
		fprintf(stderr , "ホスト名の取得に失敗しました : %s" , argv[1]);
		return 0;
	}

	printf("公式名 = %s\n" , host->h_name);
	for(iCount = 0 ; host->h_aliases[iCount] ; iCount++) {
		printf("別名 = %s\n" , host->h_aliases[iCount]);
	}

	for(iCount = 0 ; host->h_addr_list[iCount] ; iCount++) {
		printf("IP = %d.%d.%d.%d\n" ,
			(BYTE)*((host->h_addr_list[iCount])) ,
			(BYTE)*((host->h_addr_list[iCount]) + 1) ,
			(BYTE)*((host->h_addr_list[iCount]) + 2) ,
			(BYTE)*((host->h_addr_list[iCount]) + 3)
		);
	}

	WSACleanup();
	return 0;
}
このプログラムは、コマンドライン引数からホスト名を指定します
すると、指定されたホストの別名や公式名、アドレスを表示します
C:\...>test www.age-soft.co.jp
公式名 = ns.age-soft.co.jp
別名 = www.age-soft.co.jp
IP = 210.143.102.133
これは、ゲーム会社アージュさんのホスト名を入力した例です
公式名は ns.age-soft.co.jp で、IP が 210.143.102.133 だということが分かります
実際に、Web サービスを行っているコンピュータであれば
プロトコル://IP/ とすることで、サービスを受けることができるでしょう
例えば、http://210.143.102.133/ というような形でアクセスできます

逆に、アドレスを使ってホスト名を得ることも可能です
これは gethostbyaddr() 関数から取得します
struct HOSTENT FAR * gethostbyaddr (
	const char FAR * addr,  
	int len , int type
);
addr には、アドレスを格納した数値へのポインタを指定します
len にはアドレスの長さ、type にはアドレスのタイプを指定します
関数が成功すれば HOSTENT へのポインタが、失敗すれば NULL が返ります

通常、IP を扱うのであれば len には 4 を、type には AF_INET を指定します
ただし、今後は IPv6 の普及が考えられるので、対策が必要です

通常、ユーザーの入力から得られるアドレス表記は
ドット表記を用いた文字配列であり、数値ではありません
これを数値に変換するには inet_addr() 関数を使うと便利です

unsigned long inet_addr (const char FAR * cp);

cp には、アドレスの文字列表現が格納される char 配列へのポインタを指定します
戻り値は、成功すればアドレスの数値表現、失敗すれば INADDR_NONE が返ります
#include <stdio.h>
#include <winsock2.h>

int main(int argc , char *argv[]) {
	LPHOSTENT host;
	WSADATA wsaData;
	unsigned long addr;
	int iCount;

	if (argc == 1) return 0;
	
	WSAStartup(2 , &wsaData);
	addr = inet_addr(argv[1]);
	if (addr == INADDR_NONE) host = gethostbyname(argv[1]);
	else host = gethostbyaddr((const char *)&addr , 4 , AF_INET);

	if (host == NULL) {
		fprintf(stderr , "ホスト名の取得に失敗しました : %s" , argv[1]);
		return 0;
	}

	printf("公式名 = %s\n" , host->h_name);
	for(iCount = 0 ; host->h_aliases[iCount] ; iCount++) {
		printf("別名 = %s\n" , host->h_aliases[iCount]);
	}

	for(iCount = 0 ; host->h_addr_list[iCount] ; iCount++) {
		printf("IP = %d.%d.%d.%d\n" ,
			(BYTE)*((host->h_addr_list[iCount])) ,
			(BYTE)*((host->h_addr_list[iCount]) + 1) ,
			(BYTE)*((host->h_addr_list[iCount]) + 2) ,
			(BYTE)*((host->h_addr_list[iCount]) + 3)
		);
	}

	WSACleanup();
	return 0;
}
このプログラムは、入力された引数をアドレスに変換しようと試みます
変換に成功すれば、これはアドレスであると理解し gethostbyaddr() を
そうでなければ、ホスト名であると解釈し gethostbyname() を使います
C:\...>test 210.143.102.133
公式名 = ns.age-soft.co.jp
IP = 210.143.102.133
これは、引数にアドレスを指定した場合の結果です
これで、ホスト名とアドレス、両方を解決できるプログラムを作れました

ただし、アドレスは IN_ADDR 構造体でも表されます
将来の拡張に備え、こうした専用の型を使うべきでしょう
struct in_addr {
	u_long s_addr;
}
s_addr は、アドレスを表す数列を格納するメンバです
これを用いることで、アドレスの型を抽象化することができるため
将来の拡張などにも対応しやすくなるでしょう

また、アドレスを文字列として表示させたい場合は
inet_ntoa() 関数を使うと、変換処理を隠蔽できるので便利です

char FAR * inet_ntoa (struct in_addr in);

int には IN_ADDR 構造体を指定します
関数が成功すれば、ドット表記によるアドレスの文字列表現が
失敗すれば NULL が返ります
これらを使えば、移植性や保守が比較的容易なプログラムになるでしょう
#include <stdio.h>
#include <winsock2.h>

int main(int argc , char *argv[]) {
	LPHOSTENT host;
	WSADATA wsaData;
	IN_ADDR addr , *ip;
	int iCount;

	if (argc == 1) return 0;
	
	WSAStartup(2 , &wsaData);

	addr.s_addr = inet_addr(argv[1]);
	if (addr.s_addr == INADDR_NONE) host = gethostbyname(argv[1]);
	else host = gethostbyaddr((const char *)&addr , sizeof(IN_ADDR) , AF_INET);

	if (host == NULL) {
		fprintf(stderr , "ホスト名の取得に失敗しました : %s" , argv[1]);
		return 0;
	}

	printf("公式名 = %s\n" , host->h_name);
	for(iCount = 0 ; host->h_aliases[iCount] ; iCount++) {
		printf("別名 = %s\n" , host->h_aliases[iCount]);
	}

	for(iCount = 0 ; host->h_addr_list[iCount] ; iCount++) {
		ip = (IN_ADDR *)host->h_addr_list[iCount];
		printf("IP = %s\n" , inet_ntoa(*ip));
	}

	WSACleanup();
	return 0;
}
このプログラムは、先ほどと同じものですが、IN_ADDR 構造体を利用しています


gethostbyname()

struct hostent FAR * gethostbyname (const char FAR * name);
指定されたホスト名から hostent 構造体を返す
name
ホストを識別するキャラクタ配列
戻り値
hostent 構造体へのポインタ。失敗すると NULL

gethostbyaddr()

struct HOSTENT FAR * gethostbyaddr (
	const char FAR * addr,  
	int len , int type
);
指定されたアドレスから hostent 構造体を返す
addr
ネットワークバイトオーダーでのアドレスへのポインタ
len
addr の指すアドレスのバイト単位のサイズ
type
addr の指すアドレスのタイプを示す定数
戻り値
hostent 構造体へのポインタ。失敗すると NULL

inet_addr()

unsigned long inet_addr (const char FAR * cp);
文字列表現のアドレスを整数に変換する
cp
文字列表現のアドレスを格納する文字配列へのポインタ
戻り値
アドレスの数値表現。失敗すれば INADDR_NONE

inet_ntoa

char FAR * inet_ntoa (struct in_addr in);
in_addr を文字列に変換する
in
アドレスが格納されている in_addr 構造体
戻り値
文字列へのポインタ。失敗すれば NULL



前のページへ戻る次のページへ