FTP のことはもう知ってて、SFML での使い方を早く知りたいってお友達は、このセクションは飛ばしてね。
さて、FTP は "File(ファイル) Transfer(転送) Protcol(手順)" の略です。
離れたコンピューター同士でファイルをやりとりする方法です。
「ディレクトリを作成」「ファイルを削除」「ファイルをダウンロード」などのコマンドがあります。
FTP を使うには、これらの FTPコマンドを処理するための FTPサーバーソフトがサーバーマシンで動いている必要があります。
FTP を使うとプログラム上で何が出来て、どんなふうに便利なのか?
FTP を使うとリモートマシン上のファイルを取得したり作成したりできます。
オンライン対応のゲームを作って、マップや画像などのデータをサーバーから取得したり、
クライアントソフトを自動でアップデートしたりできます。
FTPそのものについてもっと詳しく知りたい場合は、
Wikipedia を見てね。
SFML の FTP 機能のクラスは、
sf::Ftp です(そのまんまだ!)。
これはクライアントとして動作します。つまり、FTPサーバーに接続して、ファイル送信やダウンロードなどのコマンドを送信します。
このクラスの関数は、FTPのコマンドをラッピングしたものです。
コマンドを送信した後は、FTP標準仕様に準拠したレスポンスが得られます。
レスポンスには次のようなものがあります。
・ステータスコード(HTTP と同じではないけれど、似たようなものです)。
・何が起きたのかをユーザーに知らせるメッセージ。
これらのレスポンスは
sf::Ftp::Response クラスにカプセル化された状態で得られます。
#include <SFML/Network.hpp>
sf::Ftp ftp;
...
sf::Ftp::Response response = ftp.login(); // 例です。他のコマンドのときも同じ。
std::cout << "Response status: " << response.getStatus() << std::endl;
std::cout << "Response message: " << response.getMessage() << std::endl;
ステータスコードを見ると、コマンドが成功したのか失敗したのかを判断できます。
400未満のコードなら成功。それ以外はエラーを表しています。
エラーを確認するだけなら isOk() 関数が便利です。
sf::Ftp::Response response = ftp.login();
if (response.isOk())
{
// 成功!
}
else
{
// エラーでした……
}
レスポンスの詳細を気にしないなら、もっと簡単に書くことができます。
if (ftp.login().isOk())
{
// 成功!
}
else
{
// エラーでした……
}
以降のサンプルコードでは、あくまでチュートリアルなので、エラーチェックは省略してます。
でも、実際のコーディングのときにはエラーチェックを忘れないでね!
それでは、FTPクラスの使い方がわかったところで、どんなことができるのかを見ていきましょう。
まずは、FTP サーバーに接続しましょう。
sf::Ftp ftp;
ftp.connect("ftp.myserver.org");
サーバーのアドレスは、URL でも IP アドレスでも、ネットワーク名でも可です。
(
sf::IpAddressクラスの値として有効であればOK)
ポート番号は 21 が FTP での標準です。
が、サーバーの都合で別の数字になっていることもあります。その場合は引数で明示的に指定しましょう。
sf::Ftp ftp;
ftp.connect("ftp.myserver.org", 45000);
引数はもう1つ指定できます。タイムアウトの時間です。
これを指定しておくと、サーバーがレスポンスを返さないとき、長時間待ち続けてしまうことを防げます。
sf::Ftp ftp;
ftp.connect("ftp.myserver.org", 21, sf::seconds(5));
接続したら、次は認証(ログイン)です。
// 名前とパスワードで認証
ftp.login("username", "password");
// サーバーが許可していれば、匿名でのログインも可能です。
ftp.login();
以下、
sf::Ftp クラスで使える全コマンドを概説します。
注意点が1つ。
これらのコマンドは、カレントディレクトリからの相対パスで動作します。
ちょうど、ローカルマシンのコンソール上でファイルやディレクトリにアクセスしているのと同じ感覚です。
カレントディレクトリを取得:
sf::Ftp::DirectoryResponse response = ftp.getWorkingDirectory();
if (response.isOk()) {
std::cout << "Current directory: " << response.getDirectory() << std::endl;
}
sf::Ftp::DirectoryResponse は
sf::Ftp::Response を継承している子クラスです。
ディレクトリ名1つ取得するようなコマンドのレスポンスを受け取るための専用のクラスです。
カレントディレクトリ内のファイルおよびディレクトリのリストを取得:
sf::Ftp::ListingResponse response = ftp.getDirectoryListing();
if (response.isOk())
{
const std::vector& listing = response.getListing();
for (std::vector::const_iterator it = listing.begin(); it != listing.end(); ++it)
{
std::cout << "- " << *it << std::endl;
}
}
// サブディレクトリを指定して、リストを取得することもできます
response = ftp.getDirectoryListing("subfolder");
sf::Ftp::ListingResponse も
sf::Ftp::Response を継承している子クラスです。
ファイルやディレクトリのリストを受け取るようなコマンドのレスポンスを受け取るための専用のクラスです。
カレントディレクトリを変更:
// 引数にはカレントディレクトリからの相対パスを指定する
ftp.changeDirectory("path/to/new_directory");
親ディレクトリに移動する:
ftp.parentDirectory();
ディレクトリを作成(カレントディレクトリ内に):
// 引数には作成するディレクトリ名を指定
ftp.createDirectory("name_of_new_directory");
ディレクトリを削除:
// 引数には削除したいディレクトリの名前を指定
ftp.deleteDirectory("name_of_directory_to_delete");
ファイル名を変更する:
// 第1引数は旧ファイル名。第2引数は新ファイル名
ftp.renameFile("old_name.txt", "new_name.txt");
ファイルを削除:
// 引数には、削除するファイル名を指定
ftp.deleteFile("file_name.txt");
ファイルをサーバーからダウンロード:
ftp.download (
"remote_file_name.txt", // ダウンロードするファイル
"local/destination/path", // どこにダウンロードするか?(ローカルマシン上のパス)
sf::Ftp::Ascii
);
最後の引数は転送モードです。
テキストファイルなら、Ascii。
EBCDIC キャラクターセットを使っているファイルなら、Ebcdic(今でも使ってる人がいるのかな?)。
テキストファイル以外なら Binary。
ファイルをアップロード(サーバーに転送):
ftp.upload (
"local_file_name.pdf", // アップロードするファイル
"remote/destination/path", // どこにアップするか?(サーバー上のパス)
sf::Ftp::Binary
);
接続後、しばらく何もしないでいると、FTP サーバーは自動的に接続を切ります。
勝手に切られてしまうのを防ぐには、定期的に NOPコマンドを送信します。
ftp.keepAlive();
接続終了のコマンドで、いつでもサーバーとの接続を切ることができます。
ftp.disconnect();