SCXML FTPクライアント
ステートマシンを用いて、シンプルなFTPクライアントを実装しています。
このFTPクライアントは、Qt SCXML を用いて実装されており、ステートマシンのイベントから変換された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 からサンプルを選択してください。詳細については、Qt Creator の「チュートリアル:ビルドと実行」を参照してください。
- Qt Extension for Visual Studio Code
Command Palette から `Qt: Open Qt examples ` コマンドを実行し、リストからサンプルを選択します。詳細については、Qt Extension for Visual Studio Code の「チュートリアル:ビルドと実行」を参照してください。```
ステートマシンのコンパイル
プロジェクトのビルドファイルに以下の行を追加することで、Qt SCXML モジュールに対してリンクを行います。
qmakeを使用する場合、ftpclient.proに以下を追加します。
QT = core scxml network次に、コンパイルするステートマシンを指定します:
STATECHARTS += simpleftp.scxmlCMake を使用する場合、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が生成されるとともに、これらがヘッダーファイルおよびソースファイルとしてプロジェクトに適切に追加されます。
ステートマシンのインスタンス化
main.cppファイル内で、生成されたFtpClient クラスに加え、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());
}));最後に、このメソッドの最初の引数として指定された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> 。
© 2026 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.