FTP クライアントを作ってみよう (1)

前へ << echo サーバを作ってみよう (4) FTP クライアントを作ってみよう (2) >> 次へ

まずは ftp コマンドで

ここまで理解できたでしょうか。 今度はちょっとレベルを上げて、ftp クライアントを作ってみましょう。 ちょっと難しいですよ。

まずは ftp コマンドを使って、ftp する際の挙動をおさらいしましょう。 詳しい使い方は説明しませんが、あなたは今から ftp クライアントを作ろう としている人ですから、大丈夫ですよね?

UNIX にも Windws にも ftp コマンドは標準で用意されているはずです。 以下は、ftp.hoge.com にログインして、file1.txt を get (ダウンロード) し、 program を put (アップロード) する例です。

% ftp ftp.hoge.com
Connected to ftp.hoge.com.
220 ftp FTP server (Version 6.00) ready.
Name (ftp.hoge.com:username): username
331 Password required for username.
Password: password
230 User username logged in.
Remote system type is UNIX.
Using binary mode to transfer files.
ユーザ名とパスワードを入力して、ログインすることができました。 lsでファイル・ディレクトリの一覧を表示してみます。
ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for '/bin/ls'.
total 464
-rw-r--r--  1 user  user        130 Jan  9 10:36 file.txt
drwxr-xr-x  3 user  user        512 Dec 14 00:29 dir
226 Transfer complete.
ファイル file.txt と、ディレクトリ dir が存在することがわかりました。 ではfile.txtをダウンロードします。ただし、file.txt はテキストファイルなので、 ASCII モードにしましょう。その後 get file.txt とすることで、 ローカルへのファイル転送ができます。
ftp> ascii
200 Type set to A.
ftp> get file.txt
local: file.txt remote: file.txt
200 PORT command successful.
150 Opening BINARY mode data connection for 'file.txt' (130 bytes).
226 Transfer complete.
今度はローカルから ftp.hoge.com へ、ファイル program をアップロードします。 これはコンパイル済のバイナリファイルですから、binary でバイナリ転送モードに 切り替えます。
ftp> binary
200 Type set to I.
ftp> put program
local: program remote: program
200 PORT command successful.
150 Opening ASCII mode data connection for 'program'.
226 Transfer complete.
転送はうまくいったはずですが、念のためもう一度 ls を実行して、 本当に program が転送されたかどうか調べましょう。
ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for '/bin/ls'.
total 464
-rw-r--r--  1 user  user        130 Jan  9 10:36 file.txt
drwxr-xr-x  3 user  user        512 Dec 14 00:29 dir
-rw-r--r--  1 user  user      65536 Dec  4 08:09 program
OK ですね。では bye で ftp コマンドを終了します。
ftp> bye
221 Goodbye.

ftp -d でプロトコルの流れを調べる

今度は同じことを -d オプションを付けて行います。 -d オプションを付けるとデバッグモードになり、 どのような FTP プロトコルが送受信されたのかがわかります。

先ほどはタイプする部分を太字で表記しましたが、今度は デバッグ表示もあわせて bold 表記します。

% ftp -d ftp.hoge.com
Connected to fto.hoge.com.
220 ftp FTP server (Version 6.00) ready.
Name (ftp.hoge.com:username): username
---> USER username
331 Password required for username.
Password: password
---> PASS XXXX
230 User username logged in.
---> SYST
215 UNIX Type: L8 Version: BSD-199506
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
---> PORT 10,0,0,1,156,87
200 PORT command successful.
---> LIST
150 Opening ASCII mode data connection for '/bin/ls'.
total 466
-rw-r--r--  1 user  user        130 Dec 18 03:42 file.txt
drwxr-xr-x  3 user  user        512 Dec 14 00:29 dir
226 Transfer complete.
ftp> ascii
---> TYPE A
200 Type set to A.
ftp> get file.txt
 local: file.txt remote: file.txt
---> SIZE file.txt
213 132
---> PORT 10,0,0,1,156,88
200 PORT command successful.
---> RETR file.txt
150 Opening ASCII mode data connection for 'file.txt' (130 bytes).
226 Transfer complete.
---> MDTM file.txt
213 19981217184250
ftp> binary
---> TYPE I
200 Type set to I.
ftp> put program
 local: program remote: program
---> PORT 10,0,0,1,156,89
200 PORT command successful.
---> STOR program
150 Opening BINARY mode data connection for 'h2z'.
226 Transfer complete.
ftp> ls
---> TYPE A
200 Type set to A.
---> PORT 10,0,0,1,156,90
200 PORT command successful.
---> LIST
150 Opening ASCII mode data connection for '/bin/ls'.
total 466
-rw-r--r--  1 user  user        130 Jan  9 10:36 file.txt
drwxr-xr-x  3 user  user        512 Dec 14 00:29 dir
-rw-r--r--  1 user  user      65536 Dec  4 08:09 program
226 Transfer complete.
ftp> bye
---> QUIT
221 Goodbye.
どうですか? FTP プロトコルを知らない人でも、
ユーザ認証は
USER ユーザ名
PASS パスワード
ファイル一覧は
LIST
ASCII モード、BINARY モードはそれぞれ
TYPE A
TYPE I
ファイルのダウンロード・アップロードはそれぞれ
RETR ファイル名
STOR ファイル名
終了するときは
QUIT
ではないかな、と推測できるのではないでしょうか。

ぱっと見た感じではよくわからないのが MDTM ですが、 MDTM はファイルのダウンロード時に使われており、さらに

---> MDTM file.txt
213 19981217184250
と、日付と時間 (1998/12/17 18:42:50) らしき返事がサーバから返って きています。file.txt のタイムスタンプが
-rw-r--r--  1 user  user        130 Dec 18 03:42 file.txt
であることから、これはファイルの最終更新時刻を取得しているようです。 9 時間ずれているのは、相手のマシンが日本標準時間 (JST) となっているから でしょう。

また、SIZE はファイルサイズを取得するコマンドのようです。

---> SIZE file.txt
213 132
130 ではなく 132 なのは、ASCII モードで転送したため、 改行コード2つ分のサイズが増えたものと思われます。

さて、あとわからないのは、PORT コマンドだけですね。

---> PORT 10,0,0,1,156,87
---> PORT 10,0,0,1,156,88
---> PORT 10,0,0,1,156,89
---> PORT 10,0,0,1,156,90
実は、これが FTP プロトコルの面倒なところです。

FTP クライアントはクライアント兼サーバ

ここまで HTTP クライアント、POP3 クライアントと作ってきて、 次に FTP クライアントではなく echo サーバを題材としました。 なぜサーバの作り方を間にはさんだかというと、 実は FTP クライアントはサーバとしての機能を持たなくてはならないからです。

FTP プロトコルは、2本のコネクションを使います。 一つはコマンド転送用、もう一つはデータ転送用です。 図で説明しましょう。上から順にプロトコルの流れを見ていってください。

FTP クライアント                                   FTP サーバ
        USER ------ コマンド用コネクション ------>
        PASS ------ コマンド用コネクション ------>
        PORT ------ コマンド用コネクション ------>
        LIST ------ コマンド用コネクション ------>
             <----- データ用コネクション(1) -----  ファイル一覧送信
        PORT ------ コマンド用コネクション ------>
        RETR ------ コマンド用コネクション ------>
             <----- データ用コネクション(2) -----  ファイル内容送信
        QUIT ------ コマンド用コネクション ------>
わかりますか? LIST やファイル一覧を取得したり、RETR でファイルを 取得する場合は、 FTP サーバが FTP クライアント側に接続してくる のです。そしてその FTP サーバ側からのコネクションの中で、データの送信が 行われるわけです。

しかし何度も書きましたが、サーバとしてクライアントを待つためには、 特定のポートを見張っておかなくてはなりません。しかしFTPサーバは FTP クライアント側のポート何番に接続すればいいのかわかりません。 そこで 「こっちはこのホストのポート何番で待っているから接続してきてね」と FTP クライアントがFTP サーバに連絡するのが PORT コマンドなのです。

---> PORT 10,0,0,1,156,87
この例では、前半4つの数字が IP アドレス、後半の2つがポート番号を表します。 前半の4つはそのまま IP アドレスになりますが、後半の2つの数字は、 「156×256+87」で計算される 40023 というポート番号を表しています。 つまり
---> PORT 10,0,0,1,156,87
は、「IP アドレス 10.0.0.1 のポート 40023 番を見張っているので、 データはそこに送ってね」と、「FTP クライアントが FTP サーバに連絡している」のです。 その後に LIST コマンドを送るか RETR コマンドを送るかで、データの内容は 変わってきますが、とにかく「そこで待ってるよ」というクライアントの 意志表示なわけです。

PORT 10,0,0,1,156,87
LIST
という内容をクライアントがサーバに送信したとき、 「IP アドレス10.0.0.1のポート40023番を見張っているので、 データはそこに送ってね」 「ファイルの一覧を送ってね」 という意味になります。当然ですが、クライアント側は PORT コマンドで宣言したホストの宣言したポートを 見張っていなくてはいけません

その後、FTP サーバは言われた通りの場所 (IP アドレス 10.0.0.1 のポート 40023 番)に connect し、データ(この場合はファイル一覧)を送ってくるわけです。

復習

もう一度 FTP プロトコルの流れを説明します。以下はすべて FTP クライアントが FTP サーバに送るコマンドです。

まずユーザ認証。

---> USER username
---> PASS XXXX
---> SYST
ls がタイプされたので、ファイル一覧を取得します。 IP アドレス 10.0.0.1 のポート 40023 番 (156×256+87) で待っていることを宣言し、 実際にそのポートを見張ります。
---> PORT 10,0,0,1,156,87
そしてファイル一覧を送るよう要求します。
---> LIST
ここで FTP サーバ側は IP アドレス 10.0.0.1 のポート 40023 番に接続し、 ファイル一覧を送信します。送信が終ると、そのコネクションは切断されます。

ascii とタイプされたので、TYPE A で ASCII モードにします。

---> TYPE A
次に get file.txt とタイプされたので、file.txt をダウンロードします。 まずファイルサイズを取得します。 次に IP アドレス 10.0.0.1 のポート 40024 番 (156×256+88) で待っていることを宣言し、 実際にそのポートを見張ります。
---> SIZE file.txt
---> PORT 10,0,0,1,156,88
そして file.txt を送るよう要求します。
---> RETR file.txt
ここで FTP サーバ側は IP アドレス 10.0.0.1 のポート 40024 番に接続し、 file.txt の内容を送信します。送信が終ると、そのコネクションは切断されます。
---> MDTM file.txt
binary とタイプされたので、BINARY モードに切替えます。
---> TYPE I
put program とタイプされたので、ファイル program を アップロードします。 IP アドレス 10.0.0.1 のポート 40025 番 (156×256+89) で待っていることを宣言し、 実際にそのポートを見張ります。
---> PORT 10,0,0,1,156,89
そして今からファイル program を送ることを伝えます。
---> STOR program
FTP サーバ側は IP アドレス 10.0.0.1 のポート 40025 番に接続します。 クライアント側は接続があると program の内容を送信します。 送信が終ると、そのコネクションは切断されます。

---> TYPE A
再度 ls がタイプされたので、ファイル一覧を取得します。 IP アドレス 10.0.0.1 のポート 40026 番 (156×256+90) で待っていることを宣言し、 実際にそのポートを見張ります。
---> PORT 10,0,0,1,156,90
そしてファイル一覧を送るよう要求します。
---> LIST
FTP サーバ側は IP アドレス 10.0.0.1 のポート 40026 番に接続し、 ファイル一覧を送信します。送信が終ると、そのコネクションは切断されます。

bye とタイプされたので、コネクション切断を知らせます。

---> QUIT
最初に張られたコネクションは QUIT が送られるまで 繋がったままで、コマンド用コネクションとして機能していることに 注意してください。途中で「切断されます」と書いたのは、 データ用コネクションのことです。

どうでしょう。理解できましたか?

前へ << echo サーバを作ってみよう (4) FTP クライアントを作ってみよう (2) >> 次へ

ご意見・ご指摘は Twitter: @68user までお願いします。