Winsock Programmer's FAQ
第2章: Winsock 初心者のための情報

2.1 - ネット上で入手できるサンプルプログラムはありますか?

はい。情報源のページにいくつか、ま た、FAQ の サンプルプログラムの章にもさ らにいくつかリストアップされています。まだ Winsock を始めたばかりであれば、 特に こちらの例が有用でしょう。

2.2 - WSAStartup 呼び出しの前に WSAData 構造体を初期化する必要はあるのですか?

いいえ、WSAStartup() 自身が必要なデータを埋めてくれます。

2.3 - Winsock プログラムをコンパイルしようとするとリンクエラーが出ちゃいました。何が悪いんでしょう?

おそらく、正しい Winsock インポートライブラリをリンクしていないのでしょ う。16bit の Windows システムでは、winsock.lib が正しいライブラリです。 32bit Windows システムで Winsock 1.1 の機能しか使っていないものでは wsock32.lib です。そして Winsock2 のサポートが必要プログラムでは ws2_32.lib をリンクする必要があります。

2.4 - Winsock を使って書いたプログラムは、Unix のソケットプログラムと通信できるのですか?

もっちろん! この質問は、プロトコルとAPIとがごっちゃになっていると起こる 質問です。通信プログラムは、同じトランスポート層で同じプロトコルを使用して いる限り、同じAPIを使用して作成する必要はありません。

複数プラットフォーム間のネットワーク処理を扱う前に、FAQ の論説記事「TCPを効率よく使うために」を参照して ください。この記事では、構造体のパディングやデータ表現といった、プラットフォー ムをまたがる場合にひっかかる問題点をいくつか論じています。

2.5 - Winsock は『私の好きな言語』で使うことができますか?

昨今のプログラム言語ではたいてい Winsock にアクセスするための何らかの方 法が用意されていますが、C か C++ 以外で直接使用されることはほとんどありませ ん。これにはいくつかの理由があります。

理由 1: 単に Winsock API を呼び出すための機能を持っていない。 Winsock API を使いこなすには、その言語に以下の機能がなければなりません。

  1. ポインタ。(メモリの特定の一部分をアドレスでアクセスできる能力。)
  2. ビット操作。(1バイトの中の特定のビットを操作する能力。)
  3. 構造体またはレコード。(2文字とそれに続く16bit整数、といったような、単純 データ要素が集まったものを一個のメモリブロックとして扱う能力。この機能によっ て、メモリの中にデータがどのように配置されるかをある程度制御できなくてはな らない。)

理由 2: 多くの言語では、何らかのコンポーネントアーキティクチャ(た とえば ActiveX)上で、ネットワークアクセスのような外部サービスを提供している。 その言語環境に付属する基本的なネットワークコンポーネントだけで、たいていの 仕事には十分である。もし必要なコンポーネントがついてこない、あるいは十分な 機能を持っていなかったとしても、それを自分で Winsock を使って書かなくても、 サードパーティ製のライブラリを探すと 必要なものは見つかるかもしれない。

理由 3: より新しい言語 − 特にクロスプラットフォームなスクリプト 言語 − には、ネットワーク用の言語機能が含まれている。(例えば、Java, Perl Python, Tcl などが含まれる。)プログラマーの視点からは、これらの言語で書いて いるときに Winsock を気にしなければならないことはまずない。

これらの理由その他により、この FAQ はC++ 寄りに偏っています。

もし Winsock API に直接アクセスできるような言語を使用しているのであれば、 この FAQ の C++ のコードを、その言語で等価なコードに変換できるでしょう。で すが、変換を始める前に、この FAQ の Web上 の情報源の章から、その言語でのサンプルコードを探して、実際に動作してい るコードをまず勉強してみることをお勧めします。

2.6 - Winsock プログラムをデバッグするのに何か良いツールはありませんか?

デバッグ用ツールには二つのカテゴリがあります。ひとつはネットワークアナラ イザ(スニファー、snifferとも呼ばれる)、もうひとつは Winsock shim(訳注: くさ び) と呼ばれるものです。

スニファーは通常ソフトウェアパッケージで、LAN上のどこかのワークステーショ ンで動作させます。通常のLANの動作原理に従って、LAN上を流れる全てのトラフィッ クをキャプチャーします。たいていのスニファーはある程度はトラフィックのデコー ドも行います。スニファーを利用することの利点は、文字通り通信の全てを見るこ とができる点です。Winsock レイヤからは利用できない低レベルの詳細なプロトコ ルも含めて、です。また、優れたスニファーは非常に強力にカスタマイズ可能です。 例えば、自分で開発した独自プロトコルなど、どんなプロトコルでもデコードでき る「プロトコルプラグイン」が作成できるものもあります。

もう一つのデバッグツールのカテゴリは「Winsock くさび」です。くさびは、 あなたの作ったプログラムと Winsock の間に挟まって、Winsock API を「フック (引っ掛ける)」します。こういったツールができることは、Winsock レイヤでのイ ベントをモニタすることや、単一のホスト上でのトラフィックをモニタすることの みに限定されます。ですが、このデメリットを相殺するものとして、スニファーで は見ることができないものも見ることができます。例えば、一連の Winsock 呼び 出しの正確な順序であるとか、それぞれの呼び出しで渡されたパラメタ値といった ものです。

ネットワークプログラムのデバッグを行う際にスニファーを使うかくさびを使 うべきか、についての明確な選択基準はありません。ある場合には、あなたのプロ グラム中のどこに問題があるかを探すために、そのプログラムが Winsock をどの ように使用しているかが正確にわかるくさびの方が役に立つ場合があります。逆に、 マシンから送出されるデータを正確に見る必要がある場合もあります。また、PPP や WAN 接続のような点対点接続の場合、ある種のスニファーは動作しない、とい う問題もあります。くさびはどのような状況でも利用できます。

とりあえず、フリーのスニファーを利用することから始めるのをお勧めします。 そして製品のスニファーのデモ版をいくつか試してみて、何か欲しい機能がついて いるかどうかを見てみるとよいでしょう。このFAQ のデバッグ用ツールの章には、現在市販され ているスニファーのリンクがあります。何かお気に入りのスニファーを見つけたな ら、さらにこのページから、くさびやその他のデバッグツールへのポインタを探す ことができます。

また、TCP/IP のデバッグ の FAQ 記事でも、TCPプログラムを手動でデバッグするのに便利な 手法を紹介しています。

使えない方法:動作しない、またはあてにならないデバッグ ツールも存在します。一つは SO_DEBUG ソケットオプションです。これは Microsoft のスタックでは単に動作しません。もう一つは Winsock DLL のデバッ グ用プラグイン dt_dll.dll で、この方法はあてになりません。Bob Quinn がこの 詳細について述べた記事があ ります。

2.7 - Winsock のエラー番号からエラーメッセージを得るにはどうすればいいの?

この質問に関する問題として、全ての状況について出来合いの「適した」エラー メッセージが存在すると仮定している、という点があります。現実は多くの場合、 プログラムの文脈がわからないと、エラー値から何か意味のあるエラーメッセージ には変換できません。例えば、WSAEFAULTの値は、「不正なポインタ 値が渡された」ということもあるし、「渡されたバッファが小さすぎる」場合もあ るし、場合によっては「そのバージョンのAPIはサポートされていない」という意 味でさえあったりします。Winsock 仕様には、それぞれの関数が返すもっともそれ らしいエラー値が記述されているので、この情報を元にもっと賢いエラー処理を自 分で行わなければなりません。

それでも、API呼び出しが何か予想外の値を返すことがありえるので、意味不明 なエラーメッセージでも何も出さないよりはましです。そういう場合のために、エ ラー番号とエラーメッセージの対応付けを行う文字列テーブルをリソースファイル 中に持つことができます。Winsock 1.1 のエラー値に対応するそのような RC ファ イルが ここから取得できます。あるいは、このFAQ 中のWinsock プログラムの基本チュートリアルには、 Winsock エラー番号から文字列に変換する関数が定義されているユーティリティモ ジュール(ws_util.cpp)が含まれています。

もしかしたら、Win32 の FormatMessage() API をWinsock のエ ラー番号からエラーメッセージを返させるようにできる、という人がいるかもしれ ません。しかしそれは、特定の Winsock の実装でしか動かない、文書化されてい ない振舞いであるに過ぎません。私自身、この問題に多大な時間をつぎ込んだにも かかわらず、これを動作させることができませんでした。私のアドバイスとしては、 たとえそれが文書化されていたとしても、ろくに動かない問題を追いかけ回すより は、意味のあるエラーメッセージを作成することに時間を費やしたほうがよっぽど ましだ、ということです。

2.8 - Winsock がずっとWSAEWOULDBLOCKばかり返すんですが、何が悪いんでしょう?

何にも悪くありません。WSAEWOULDBLOCKは、非ブロックあるいは 非同期ソケットを使用しているプログラムでは、まったく正常な状態です。この値 は、「私は今すぐにはそれはできません。それをするためには私はブロックしなくちゃいけないんです」という 意味の Winsock 流表現なのです。

すると次には、「次にいつそれを実行すればよいのかを、どうやって知ればい いの?」という質問が出てきます。非同期ソケットにおいて Winsock は、 send()呼出し失敗後に再び書き込みができるようになった時に FD_WRITE メッセージを送ります。またrecv()呼び出 し後、ソケット上にデータが到着したときには FD_READ メッセージ を送ります。同様に、非ブロック型ソケットに対して select()を使っ ているプログラムでは、書き込みOKの時にwritefdsがセットされ、 読み込むデータが存在する時はreadfdsがセットされます。

注意点として、Win9x には非ブロック型ソケットをブロックすると、 select() が失敗することがあるという バグがある、という点があります。あるソケットが利用可能である と通知されると、それによってプログラムが recv()send() か何かを呼び出そうとするでしょう。すると驚い たことに、その関数が WSAEWOULDBLOCK を返してくるの です。ですので、Win9x 上ではselect() を使ったプログ ラムは、このエラーを常に扱うことができるようになっていなければな りません。

この問題はより広範囲に関係してきます: 非ブロック型ソケットの 類を使用するときは常にいつでも、WSAEWOULDBLOCK に対 する準備をしておかなくてはならないということです。これは単に、 NULL ポインタチェックをすることと同様な、防衛的プログラミングの 問題です。

2.9 - ネットワークの設定なしで Winsock アプリケーションのテストをすることができますか?

ループバックもしくはlocalhostと呼ばれる特別なア ドレス 127.0.0.1 というものが存在します。これを使って一つのマシ ン上で実行されている二つのプログラムがお互いに通信することができ ます。サーバは普通全てのインターフェースからの接続に対して listen するので、クライアントは localhost アドレスを使って接続す ることができます。(基本的なサーバとクライアントのコードはのセクションを参照して下さい。)

もし開発用のマシンがインターネットあるいは LAN に接続されてい るのであれば、ループバックアドレスは既に設定されています。

ネットワーク接続を持っていないマシン上では、「ダミー」のネッ トワークを設定する必要があります。Windows NT/2000 では正にこの目 的のために "Microsoft Loopback Device" というものがあります -- ネットワークのコントロールパネルからこれを追加するだけで、ループ バックアドレスが利用可能になります。

Windows 9x では、使っていないシリアルポートに向けてダイアルアッ プ接続をインストールするという方法があります。これで何とか動くの ですが、この方法ではときたま変な振舞いを起こすことがあります。一 番の問題は、ダイアルアップ接続がモデムからダイアルする必要がある と判断しても、設定したポートにはモデムは繋がっていないということ です。この問題の影響を避けるには、gethostbyname()の ような名前解決関数は使用せず、ダイアルアップ接続の「自動ダイアル」 機能をオフにしておくことです。

注意: ループバックインターフェースを通した場合の振舞いと、ネッ トワークを通した場合の振舞いがちょっと異なる場合があります。LAN や WAN 越しよりも、単一のマシン上での方がいろんな状況が単純だか らです。基本的には一台のマシン上でしか開発をしていなくても、実際 のネットワークを使ってテストをするようにした方が良いでしょう。

2.10 - TCP ソケットを正しく close するにはどうすれば良いのですか?

TCPコネクションを close する正しい手順は以下のようになります:

  1. データ送信を完了する。
  2. shutdown()howパラメータを 1 に設定して 呼び出す。
  3. recv()が 0 を返却するまでループする。
  4. closesocket()を呼び出す。

上記のうち一番目と三番目のステップを省略すると、データを喪失 してしまうことがあります。

非ブロック型ソケットあるいは非同期型ソケットでは、一番目と三 番目のステップが複雑になります。「送信・受信の完了」ロジックを通 常の I/O ループの中で行う方法か、もしくは I/O の最後の部分でソケッ トを一時的にブロックモードにして処理を行う方法があります。どちら が良いかはそのプログラムの構造や制限事項に依存します。

2.11 - コネクションを「異常終了」させることはできますか?

はい、できます。それはとても邪悪なことですけど :)。最も簡単な 方法は、closesocket()を呼び出す前に、 setsockopt()SO_LINGER フラグを 0 に 設定する方法です。もう一つの方法は、shutdown()howパラメタを 2(「双方向」)に設定して呼び出す方法で す。その後に続いて closesocket()を呼び出すことにな ります。

「いきなりコネクションを切る」のはごく限られた場面でしか正当 と認められません。このテクニックを使うかどうかを正しく決定できる ためには、TCPの動作について非常に深い知識を持っていることが前提 となります。一般的に言って、コネクションをいきなり切る必要性が出 てくるのは、プログラムの作りが悪いからです(あなたのプログラム、 あるいは通信相手のプログラムが)。私としては、その壊れたプログラ ムを修正して、こんなアヤシゲなテクニックに頼らなくても済むように することをお勧めします。

2.12 - TCPコネクションが close されたことをどうやって検出できますか?

I/O戦略の記事で議論 されている全ての I/O 戦略において、何らかの方法でコネクションが close されたことを知る方法があります。

まず、TCP は全二重のネットワークプロトコルであることを忘れな いでください。これはつまり、コネクションの一方向を close しても、 もう一方の方向ではまだデータを送信することができるということです。 一例としてウェブブラウザの場合: ウェブブラウザは短いリクエストを ウェブサーバに送り、そのコネクションの半分を close します。そし てウェブサーバはもう一方向のコネクションを使って要求されたデータ を送り返します。そのデータを送信した側がコネクションを close し て、TCP セッションを終了させます。

通常の TCP プログラムは送信側半分のみ、すなわち通信相手にとっ ては受信側半分のみを close します。つまり普通あなたが知りたいと 思うのは、通信相手にとっての送信側が close したかどうかというこ とで、すなわちあなた側ではもうそれ以上データを受信することは無い という意味になります。

非同期型のソケットでは、Winsock はコネクションが切れたときに は FD_CLOSEメッセージを送ります。イベントオブジェク トにおいても同様です: システムはFD_CLOSEのイベント オブジェクトを通知します。

ブロック型、および非ブロック型ソケットでは、そのソケットに対 して recv()を呼び出すループがあると思います。通信相 手がコネクションを閉じると recv() は 0 を返却します。 select()を使っている場合には、もうお判りかもしれま せんが、read_fds パラメタ中の対応する SOCKET のディ スクリプタが、コネクションが切れたときにセットされます。そして通 常の場合と同じように recv()を呼び出すと、その返却値 として 0 が返されることになります。

上記の議論を読んでもう判ったかもしれませんが、コネクションの 受信側半分を close することも可能です。もし close した後 に通信相手がこちら側にデータを送ってこようとした場合には、プロト コルスタックはそのデータをどっかに捨ててしまい、通信相手には TCP RST を送り返します。

異常切断の扱いについての情報は次の質 問を参照して下さい。

2.13 - ネットワークの異常切断はどのようにして検出できますか?

一つ前の質問では接続が正常に切断さ れたときの検出方法を扱いましたが、もしその他の問題、例えばネット ワークケーブルを抜いてしまったりワークステーションがクラッシュし たときのような問題を検出したいときはどうすればよいのでしょうか? これらの場合には、通信相手に何か問題が起こったことを通知すること さえできません。私の感覚ではこれは「仕様」です。だって、そのどこ かで壊れた部品を誰も気づかないうちに直すこともできるのに、なんで いちいち全部再起動し直さなくちゃいけないの?

もし、あなたがいかなるネットワークの故障でも検出しなければな らない状況に陥ってしまったのであれば、二つの選択肢があります:

一つ目の選択肢は、プロトコルに「コマンド/応答」の構造を持たせ ることです。一方のホストがコマンドを送り、そしてもう一方のホスト からコマンドの受信もしくは実行後に応答が返されるのを待ちます。応 答が返ってこなければそのコネクションは死んでいるか、少なくとも何 か問題があると言うことができます。

二つ目の選択肢は、プロトコルに "echo" 機能を追加することです。 これは一方のホスト(通常クライアント側)が一定時間ごとに「ねえ、ま だ居る?」というパケットを他方に送信することにしておき、その確認 を即座に行うということです。もし echo を送る側のホストが応答を受 信しなかったり、受信側のホストが echo 要求を一定時間内に受信する ことが無かったのなら、プログラムはコネクションに問題が発生したか、 相手側のホストが落ちていると仮定することができます。

もし "echo" の方を選んだ場合は、ICMP "ping" 機能を使いたいと いう誘惑に負けてはいけません。もし使おうとするならば、両側からお 互いに ping する必要があります。なぜなら、Microsoft のスタックで は、相手側から echo 要求があったことを知る術はなく、自分自身の要 求した echoに対する応答しか知ることができないからです。他の問題 としては、ping はあなたのプロトコルの外側のものであるということ です。つまりハードウェア的なコネクションが生きていれば、TCP コネ クションが切れてもそれを検出することができないのです。最後に ping テクニックの問題としては、ICMP は信頼性のないプロトコルであ るという点があげられます。あるプロトコルの信頼性を保証するために、 信頼性のないプロトコルを使うなんて、一体全体何の意味があるってん だい?

別の選択肢としては、あまり気にすることはありませんが TCP keepalive 機構というものがあります。これは実際のデータ送信がある 無しに関わらず、そのコネクションにおいて一定時間ごとにパケットを 送信するように、プロトコルスタックに指示するという方法です。もし 通信相手のホストが生きているのであれば、同様の応答パケットを送り 返してきます。もし TCP コネクションがもはや有効でなければ(例えば、 相手のホストが、前回の keepalive 以降に再起動していたときなと)、 相手のホストはリセットパケットを送り返して、こちら側のコネクショ ンを切断します。また相手側のホストが落ちている場合は、こちら側の ホストの TCP スタックは相手からの応答にタイムアウトしてコネクショ ンを切ります。

keepalive には二つの問題があります:

  1. keepalive の時間をプロセス単位で変更できるのは Windows 2000 のみです。それより古い Windows においては、keepalive 時間を変更 すると、そのマシン上で keepalive を使っている全てのアプリケーショ ンの時間が変更されてしまいます。(keepalive 時間のデフォルトは 2 時間 なので、keepalive 時間の変更はほとんどの場合必須です。)
  2. 一つの keepalive パケットは 40 bytes 程度の大して役に立たな いデータで、コネクションが有効である限り、双方向に送られます。こ れに対して「コマンド/応答」型のプロトコルは、実質的に無意味なデー タはありません: つまり全てのパケットには意味があります。一応公平 のために言っておくと、TCP keepalive は Windows 2000 においては、 上記の「ねえ、まだ居る?」方式よりは無駄は少ないです。

なお、ネットワークの種類が異なると、物理的な切断の扱い方も異 なるということに注意してください。例えば Ethernet では、リンクレ ベルの接続が無くなってしまう、つまりネットワークケーブルを抜いて しまった場合、相手側のホストは物理的に通信不可能となったことを知 ることができません。一方 PPP のリンクの場合は、リンクレイヤで切 れてしまったことを検出することができ、これは Winsock レイヤに伝 わるので、プログラムから検出することができるのです。

2.14 - Winsock 関数のタイムアウトを変更するにはどうするのですか?

ブロック型の Winsock 関数(例えば connect())のい くつかは、タイムアウト時間は中に埋め込まれています。こうなってい る理由の背景には、正しいタイムアウト時間を設定するために必要な情 報を全て持っているのはプロトコルスタックだけであるから、というこ とがあります。それでもなお、プロトコルスタックが使う値(一分位の こともあればそれ以上のこともある)は自分のアプリケーションにとっ ては長すぎると思う人たちもいます。

Winsock 2 においては、setsockopt()において SO_SNDTIMEO または SO_RCVTIMEO オプショ ンを設定することで、send()recv() のタイムアウトを変更することができます。

残念ながら、Winsock の仕様ではその他多くのタイムアウト値を変 更する方法は記述されていません。また上記の方法は Winsock 1.1 で は使用できません。

これの解決方法は、ブロック型ソケットの利用を完全に止めること です。非ブロック型ソケットの関数は全て、タイムアウトを行うことが できるようにできています。

  • select() を使った非ブロック型ソケット - select()関数の5番目のパラメータはタイムアウト値です。
  • 非同期型ソケット - Windows API の SetTimer() を使ってください。
  • イベントオブジェクト - WSAWaitForMultipleEvents()にはタイムアウトの引数が あります。
  • 待ち受けタイマー - Windows 98 か NT 4.0 SP3 以降でのみ。待ち受けタイマー(waitable timer)はセマフォのようなオブジェクトですが、指定した時刻に OS が シグナルを発生させるものです。Win32 関数 CreateWaitableTimers() で作成することができます。こ れを使って、5秒のタイマーをイベントオブジェクトと同じように待 つことができます。もし5秒内にソケットに対して何も起こらなければ Windows はタイマーにシグナルを発生させ、これにより WaitForMultipleObjects() 呼出しが中断されます。

非同期型および非ブロック型ソケットでは、タイムアウトの処理を 全く行わないこともできます。Winsock がビジーである間ずっと自分の プログラムを動作させておくことさえできます。したがって、ある操作 の時間がかかりすぎているときのキャンセルをユーザに任せることも、 Winsock のタイムアウトの機能を独自に置き換えたりせず、単に Winsock にタイムアウトさせることも可能です。

2.15 - ピーク(MSG_PEEK)とは何者で、なぜこれが良くないの?

ピークは TCP データストリームの先頭を先読みすることです。 recv() にMSG_PEEK フラグを使用したときは、プロトコ ルスタック中のバッファから、バッファのデータを削除することなしに、 そのデータを返却します。(同じことを ioctlsocket() の FIONREAD オプションでも行うことができます。)

ピークは本来は全く不必要なものです。読み出したデータを常に自 分のバッファに入れて、その中で処理をすればよいからです。実際これ は良い方法です。なぜならピークはときおり問題を引き起こすことがあ るからです。いや全く、あまりに問題があるので ザ・間違いリスト に位 置を占めたり、Microsoft's Knowledge Base に載ったりするわけです。 ピークが Winsock スタックにおいてどのような問題があるのか、特定 の情報については Q192599 の記事を参照して下さい。

2.16 - 帯域外データ(MSG_OOB)とは何者で、なぜこれが良くないの?

帯域外(Out-of-band, OOB)データは、二番目のデータチャンネルの ようなものです。通常のTCPデータストリームにほとんどのデータを流 し、OOB ストリームには「緊急」のメッセージを流す、というのがその 目的です。telnet プロトコルにおいてはこれを Ctrl-C のような「割 り込み」操作のために使っています。これによって、割り込みを発生さ せるのに、相手側で通常のTCP データの操作を全て完了するのを待つ必 要がなくなります。OOB データは send() に MSG_OOB フ ラグを渡すことで送信することができ、また recv() に MSG_OOB を渡すことで受信することができます。また setsockopt() に SO_OOBINLINE フラグを設定することに よって OOB データを得ることもできます。

OOB データは有用な概念ではあるのですが、残念なことに、プロト コルスタックレベルにおいてどのように OOB データが扱われるべきか、 二つの矛盾する解釈があります。TCP プロトコル仕様(RFC 793)における OOB の 元々の記述は、「ホスト要件」仕様(RFC 1122)によって置き換 えられていますが、RFC 793 の OOB 実装を持つマシンが未だに多く存 在しているのです。Winsock2 仕様(これを書いている時点では version 2.2.2)の Section 3.5 では OOB について議論しており、RFC 793 対 RFC 1122 がなぜ問題となるかの詳細は section 3.5.2 で議論されてい ます。

また、OOB は、二番目のデータチャンネルとして完全に機能するわ けでもありません。かなり制限されています。ですから、OOB は、 telnet のようにこれを必要としているレガシーなプロトコルを実装す るとき以外には使用するべきではありません。信頼性のある OOB 風の 振舞いが欲しければ、単に二つのデータコネクションを使えばよいので す。つまり一方を通常のデータ用に、二番目を緊急用のデータに。

2.17 - MSG_PEEK も MSG_OOB も悪者なのであれば、send() や recv() のフラグの引数には何を渡せばよいのですか?

send()recv() のフラグの引数には 0 を渡せば全く問題ありません。


<< Winsock の一般的な情報 Winsock 中級者向けの議論 >>
Last modified: $Id: newbie.html,v 1.5 2002/11/09 20:40:30 ksk Exp $ Go to the original FAQ page
< Go to the main FAQ page << Go to the Home Page