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.hsimpleftp.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 &parameters) {
        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>.

プロジェクト例 @ code.qt.io

©2024 The Qt Company Ltd. 本書に含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。