こける Wired-Winsockを使ってみようぜ-3.住所と氏名 '97/11/23

3.住所と氏名


今回は、サンプルプログラムとして「IPアドレスと本名表示」を作ります。
少しネットワークプログラムらしくなってきましたが、通信はまだです。(^^;
しかし、PPP接続の場合は今回のプログラムを実行する前に接続しといてくださいね。

TCP/IPを使う場合、各コンピュータには「IPアドレス」が割り当てられます。
TCP/IPでは、コンピュータを指定する場合このIPアドレスで指定するわけです。
で、これを表す型がTInAddrです。IPアドレスは4バイトの整数、であればLongIntでも表せます。
WinsockAPIの中でIPアドレスを表す型はTInAddrですが、いくつかのwinsockAPIはLongIntとしているものもあります。
winsockユニットの中ではu_long=LongIntとなっていて、実際の宣言はこのu_longを使っています。

普通IPアドレスそのものを整数として扱ったりすることは無いのであまり気がつかないのですが、実は上位下位が逆に表現されています。
「インターネットではビッグエンディアン」というのを思い出しましたか?
この事は、実際に送信したり受信したりするときにも問題になりますから注意してください。
今は関係ありません。

ユーザが入力するのは文字列で、IPアドレスを表現する文字列というのはドット付き10進数ですね。
これをTInAddrなりLongIntに変換するのはIntToStrなどでがんばって変換してもいいのですが、ちゃんとwinsockAPIに変換するものが存在するのでそれを使いましょう。
function inet_addr(cp:PChar):u_long;
cpに文字列を渡せば、u_longつまりLongIntになってかえってきます。
変換に失敗した場合は、この値がINADDR_NONE(255.255.255.255)になります。
アルファベットでも入っていたか、数字が255よりも大きかったのか、とにかく入力ミスという奴です。
INADDR_NONE:「存在しない」でしたね。

inet_addrの逆の変換をするのがinet_ntoaです。
function inet_ntoa(inaddr:TInAddr):PChar;
どうやってかIPアドレスを手に入れてそれを表示したい場合は、inet_ntoaを使います。
inet_ntoaにはエラーはありません。StrToIntはエラーを発生するかもしれないけど、IntToStrがエラーしないのと理由はいっしょです。
この時返ってきたPChar文字列は早いところstring文字列などにコピーしときましょう。PChar文字列が関数の返り値である場合の基本です。
なぜって?
PChar文字列ってのはstringとは違って、メモリの確保を自分では行いません。このAPIの返してきたメモリは何処にあるのでしょう?
自分で確保した文字列ではないので、いつ壊されるか自分では制御できないのです。
なので、自分でメモリの確保を行うstring文字列にさっさとコピーしてしまう(代入する)のが基本的なテクニックです。

ところで、inet_addrを使うってことはユーザに「ドット付き10進数」を入力させる事になるんですが、各コンピュータのIPアドレスなんてユーザーに入力させられますか?
ちょっと無理ですよね。普通はユーザに「ホスト名」を入力してもらいます。
「ホスト名」ってのは、「http://www.asahi-net.or.jp/‾nk2w-ishr/」の「www.asahi-net.or.jp」の部分です。
これをなんとかIPアドレスに変換しないと、コンピュータを指定したことにはなりません。

その「ホスト名」からそのコンピュータの情報を取得するのが、gethostbynameです。
function gethostbyname(name:PChar):PHostEnt;
nameにホスト名を渡すと、そのコンピュータの情報(へのポインタ)が返ってきます。
注意事項として「ユーザは何故かドット付き10進数を入力するかもしれない」というのがあります。
もしかしたらホスト名が登録されていないコンピュータに接続したいのかもしれません。
この為、gethostbynameに文字列を渡す前にinet_addrで変換できるか調べておく方が良いでしょう。
inet_addrに失敗したときだけ、gethostbynameを呼び出すのです。
これは一つのパターンになっています。大抵のプログラムで、ホスト名を入力するところにドット付き10進数を入力してもちゃんと動くでしょ。
これをしないと「ホスト名の無い(公開されていない)」コンピュータに接続できないアプリケーションになっちゃいます。

ついでにIPアドレスからそのコンピュータの情報を取得するのが、gethostbyaddr。
function gethostbyaddr(addr: Pointer; len, struct: Integer): PHostEnt;
addrにTInAddr型の変数へのポインタ、lenに4、structにAF_INETを指定して呼び出すと、同じくそのコンピュータの情報が返ってきます。
lenとかstructってのは、TCP/IP以外の接続時に別の指定の仕方をすることになるわけですが、ここでは説明しません。
おまじないみたいなものだと思って、固定の値を入れておいてください。

gethostbynameもgethostbyaddrも、エラーのときはnilが返ってきます。
assignedを使って調べても良し、単純にnilと比較しても良し、好き好きで判定してください。

注意事項です。
環境にもよるのですが、gethostbynameもgethostbyaddrも通信を行って結果を得ます。
そして結果が得られるまで返ってきません。
ということは、「結果が返ってくるまでは、これを呼び出したプログラムは凍結したように見える」のです。
クリックしても反応しませんし、どんな操作もできません。
「スレッド使えば?」という声が聞こえてきそうですね。
そう、これらを使うときにはスレッドを使って、スレッドから呼び出すべきなのです。
しかし、今回のサンプルプログラムではそこまでしません。(^^;;
「固まらない住所調べ」で別の解決方法(非同期検索)を紹介しますので、それまではこれで我慢してください。

さて、gethostbynameやgethostbyaddrの返り値の型PHostEntには、この際要らない情報がけっこうあるのでいるとこだけ紹介します。
説明のために以下の記述があるとします。
var phe: PHostEnt;
まず「ホスト名」。型はPCharです。
phe^.h_name
gethostbyaddrの時は多分これを見たかったからでしょう。gethostbynameでも引数に使ったホスト名と違うものが出てきたりします。
ここで得られるのが「本名」で、一般には「ニックネーム」で通用している場合があるということです。
あちこちで知られているWebサーバのホスト名(www.XXXX.co.jpとか)の本名を見てみると面白いかもしれません。(^^)

「IPアドレス」。型は^PCharです。ちと使い方が難しいです。
phe^.h_addr_list
これを使うときは、キャストして使います。
PInAddr(phe^.h_addr_list^)^
間接演算は3つあるし途中にキャストという、かなり面倒な感じですが、おまじないと思ってこうしとけば大丈夫です。
また、Delphi3ではph^.h_addrというのが追加されていますが特に楽になるわけでもなく、Delphi2との互換を考えると使いにくいですね。
今回はh_addr_listを使います。

あまり使わないでしょうが、「別名リスト」。IPアドレスと同じく^PChar型です。こっちも判りにくいキャストになります。
phe^.h_aliases
これを使おうと思うと、まず^PCharに名前をつけて型宣言してやります。
type PPChar=^PChar
で、別名「リスト」ですから、複数有り得ます。(無いこともあります)
ループで情報を取り出すのです。
まず変数を定義します。
var
  ppc: PPChar;
で、ループしてとりだす部分。
  ppc:=PPChar(phe^.h_aliases);
  while assigned(ppc^) do begin
    { ppc^ をPChar型として使います。 }
    inc(ppc);
  end;
gethostbynameとgethostbyaddrで、ホスト名とIPアドレスを相互に変換できるようになりました。
inet_ntoaとinet_addrでドット付き10進数にも変換できます。
前回説明したgethostnameとgethostbynameを使えば、PPP接続であっても自分のコンピュータに割り当てられたIPアドレスが分かります。
このIPアドレスをgethostbyaddrを使ってホスト名を調べると、プロバイダが接続時に自分のコンピュータにつけたホスト名が判ります。
通常にgethostnameとgethostbynameだけ使って本名を調べても、gethostnameと同じ物が返ってくるだけです。一旦IPアドレスに変換しないとだめです。
何故かは私にも判りません。単にコンピュータ名を特別扱いするための処理がめぐりめぐってこうなるだけだとは思いますが。

これで、ホスト名とIPアドレスを表示する準備ができました。
で、出来上がったサンプルプログラムがこれ。
[ソースダウンロード]

実行するときは(必要ならば)先にPPP接続を行っておいてください。
そうでないとエラーになるかもしれません。
勝手に電話をかけようとするかもしれません。

-実行例-
ボーランドジャパンWebサーバーのIPアドレスと本名です。
実行イメージ

次の話
[表紙] [Program Files] [オブジェクト指向異聞] [プログラム未整理知識] [Winsockを使ってみようぜ] [だべり] [What's New] [書いた奴] [リンク]