ホスト名とアドレス
コネクト・ネットワーク
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