SCXML FTPクライアント
ステートマシンを使用したシンプルな FTP クライアントを実装しています。
FTP ClientはQt SCXML を使用して FTP クライアントを実装します。 FTP クライアントは FTP サービスと通信することができ、 ステートマシンのイベントから変換された FTP 制御メッセージを送信し、 サーバの応答をステートマシンのイベントに変換します。FTPサーバーから受信したデータはコンソールに表示されます。
RFC 959は FTP クライアントのコマンド処理に関するステートチャートを規定している。これらは SCXML に簡単に変換することができ、SCXML のネストされた状態を利用することができます。クライアントとサーバ間の接続およびデータ転送は C++ を使用して実装されています。さらに、Qt のシグナルとスロットが使用されています。
ステートマシンには以下の状態があります:
- I:初期状態。
- Bはコマンドの送信。
- Sは成功。
- Fは失敗。
- Wは応答を待つ。
- Pはサーバーの要求に応じてパスワードを提供する。
ステートマシンはsimpleftp.scxmlファイルで指定され、FTP プロトコルのロジックを実装するFtpClient
クラスにコンパイルされる。このクラスは、状態を変更したり外部イベントを送信したりすることで、 ユーザの入力や制御チャネルからの応答に反応する。さらに、TCPソケットとサーバーを処理し、行末を変換するFtpControlChannel
クラスとFtpDataChannel
クラスを実装している。
例の実行
からサンプルを実行するには Qt Creatorからサンプルを実行するには、Welcome モードを開き、Examples からサンプルを選択します。詳細については、Building and Running an Exampleを参照してください。
ステート・マシンのコンパイル
プロジェクトのビルド・ファイルに以下の行を追加して、Qt SCXML モジュールにリンクします。
qmakeを使って、ftpclient.proに以下を追加する。
QT = core scxml network
次に、コンパイルするステートマシンを指定します:
STATECHARTS += simpleftp.scxml
CMakeでは、CMakeLists.txtに以下を追加する。
find_package(Qt6 REQUIRED COMPONENTS Core Network Scxml) target_link_libraries(ftpclient PRIVATE Qt6::Core Qt6::Network Qt6::Scxml )
そして、コンパイルするステートマシンを指定する:
qt6_add_statecharts(ftpclient simpleftp.scxml )
Qt SCXML コンパイラqscxmlc
が自動的に実行され、simpleftp.hとsimpleftp.cpp が生成され、ヘッダとソースとしてプロジェクトに適切に追加されます。
ステートマシンのインスタンス化
生成されたFtpClient
クラスと、main.cppファイル内のFtpDataChannel
およびFtpControlChannel
クラスをインスタンス化します:
#include "ftpcontrolchannel.h" #include "ftpdatachannel.h" #include "simpleftp.h" ... int main(int argc, char *argv[]) { ... QCoreApplication app(argc, argv); FtpClient ftpClient; FtpDataChannel dataChannel; FtpControlChannel controlChannel; ...
FTPサーバーとの通信
サーバーから取得したすべてのデータをコンソールに表示します:
QObject::connect(&dataChannel, &FtpDataChannel::dataReceived, [](const QByteArray &data) { std::cout << data.constData() << std::flush; });
サーバーからの応答をステートマシンのイベントに変換します:
QObject::connect(&controlChannel, &FtpControlChannel::reply, &ftpClient, [&ftpClient](int code, const QString ¶meters) { ftpClient.submitEvent(QString("reply.%1xx") .arg(code / 100), parameters); });
ステートマシンからのコマンドをFTP制御メッセージに変換する:
ftpClient.connectToEvent("submit.cmd", &controlChannel, [&controlChannel](const QScxmlEvent &event) { controlChannel.command(event.name().mid(11).toUtf8(), event.data().toMap()["params"].toByteArray()); });
匿名ユーザーとしてFTPサーバーにログインし、データ接続用のポートをアナウンスし、ファイルを取得するコマンドを送信する:
QList<Command> commands({ {"cmd.USER", "anonymous"},// login {"cmd.PORT", ""}, // announce port for data connection, // args added below. {"cmd.RETR", file} // retrieve a file });
FTPクライアントは、Bステートに入ったときに次のコマンドを送信するように指定する:
ftpClient.connectToState("B")、 QScxmlStateMachine::onEntry([&]() {if(commands.isEmpty()) { app.quit();return; } Command command=commands.takeFirst(); qDebug() << "Posting command" << command.cmd << command.args; ftpClient.submitEvent(command.cmd,command.args); });
FTPクライアントは、サーバーがパスワードを要求した場合、空の文字列をパスワードとして送信するように指定します:
ftpClient.connectToState("P"、 QScxmlStateMachine::onEntry([&ftpClient]() {) qDebug() << "Sending password"; ftpClient.submitEvent("cmd.PASS"、 QString()); });
最後に、メソッドの第1引数として指定されたFTPサーバーに接続し、第2引数として指定されたファイルを取得します:
controlChannel.connectToServer(server); QObject::connect(&controlChannel, &FtpControlChannel::opened, &dataChannel, [&](const QHostAddress &address, int) { dataChannel.listen(address); commands[1].args = dataChannel.portspec(); ftpClient.start(); });
たとえば、次の呼び出しは、指定されたサーバーから指定されたファイルを表示します:ftpclient <server> <file>
.
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.