SCXML FTP-Client

Implementiert einen einfachen FTP-Client unter Verwendung eines Zustandsautomaten.

FTP Client verwendet Qt SCXML, um einen FTP-Client zu implementieren, der mit einem FTP-Dienst kommunizieren kann, indem er FTP-Kontrollnachrichten sendet, die aus Zustandsmaschinenereignissen übersetzt werden, und indem er Serverantworten in Zustandsmaschinenereignisse übersetzt. Die vom FTP-Server empfangenen Daten werden auf der Konsole ausgegeben.

RFC 959 spezifiziert Zustandsdiagramme für die Befehlsbearbeitung des FTP-Clients. Sie lassen sich leicht in SCXML übersetzen, um von den verschachtelten SCXML-Zuständen zu profitieren. Die Verbindungen zwischen dem Client und dem Server sowie die Datenübertragung werden mit C++ implementiert. Darüber hinaus werden Qt-Signale und -Slots verwendet.

Der Zustandsautomat hat die folgenden Zustände:

  • I als Ausgangszustand.
  • B für das Senden von Befehlen.
  • S für Erfolg.
  • F für Misserfolg.
  • W für das Warten auf eine Antwort.
  • P für die Eingabe eines Passworts auf Anfrage des Servers.

Der Zustandsautomat wird in der Datei simpleftp.scxml angegeben und in die Klasse FtpClient kompiliert, die die Logik des FTP-Protokolls implementiert. Er reagiert auf Benutzereingaben und auf Antworten aus dem Kontrollkanal, indem er Zustände ändert und externe Ereignisse sendet. Darüber hinaus implementieren wir eine Klasse FtpControlChannel und eine Klasse FtpDataChannel, die TCP-Sockets und -Server handhaben und Zeilenenden konvertieren.

Ausführen des Beispiels

Zum Ausführen des Beispiels von Qt Creatorauszuführen, öffnen Sie den Modus Welcome und wählen Sie das Beispiel aus Examples. Weitere Informationen finden Sie unter Erstellen und Ausführen eines Beispiels.

Kompilieren des Zustandsautomaten

Wir linken gegen das Qt SCXML Modul, indem wir die folgende Zeile zu den Projekt-Build-Dateien hinzufügen.

Mit qmake fügen wir folgendes zu ftpclient.pro hinzu

QT = core scxml network

Anschließend geben wir den zu kompilierenden Zustandsautomaten an:

STATECHARTS += simpleftp.scxml

Mit CMake fügen wir der Datei CMakeLists.txt Folgendes hinzu

find_package(Qt6 REQUIRED COMPONENTS Core Network Scxml)

target_link_libraries(ftpclient PRIVATE
    Qt6::Core
    Qt6::Network
    Qt6::Scxml
)

Dann geben wir den zu kompilierenden Zustandsautomaten an:

qt6_add_statecharts(ftpclient
    simpleftp.scxml
)

Der Qt SCXML Compiler, qscxmlc, wird automatisch ausgeführt, um simpleftp.h und simpleftp.cpp zu erzeugen und sie dem Projekt als Header und Sourcen hinzuzufügen.

Instanziierung des Zustandsautomaten

Wir instanziieren die generierte Klasse FtpClient, sowie die Klassen FtpDataChannel und FtpControlChannel in der Datei main.cpp:

#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;
    ...

Kommunikation mit einem FTP-Server

Wir geben alle vom Server abgerufenen Daten auf der Konsole aus:

    QObject::connect(&dataChannel, &FtpDataChannel::dataReceived,
                     [](const QByteArray &data) {
        std::cout << data.constData() << std::flush;
    });

Wir übersetzen die Antworten des Servers in Ereignisse des Zustandsautomaten:

    QObject::connect(&controlChannel, &FtpControlChannel::reply, &ftpClient,
                     [&ftpClient](int code, const QString &parameters) {
        ftpClient.submitEvent(QString("reply.%1xx")
                              .arg(code / 100), parameters);
    });

Wir übersetzen Befehle aus dem Zustandsautomaten in FTP-Kontrollmeldungen:

    ftpClient.connectToEvent("submit.cmd", &controlChannel,
                             [&controlChannel](const QScxmlEvent &event) {
        controlChannel.command(event.name().mid(11).toUtf8(),
                               event.data().toMap()["params"].toByteArray());
    });

Wir senden Befehle, um uns als anonymer Benutzer beim FTP-Server anzumelden, um einen Port für die Datenverbindung anzukündigen und um eine Datei abzurufen:

    QList<Command> commands({
        {"cmd.USER", "anonymous"},// login
        {"cmd.PORT", ""},         // announce port for data connection,
                                  // args added below.
        {"cmd.RETR", file}        // retrieve a file
    });

Wir geben an, dass der FTP-Client beim Eintritt in den Zustand B den nächsten Befehl senden soll:

    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); }));

Wir legen fest, dass der FTP-Client eine leere Zeichenkette als Kennwort senden soll, wenn der Server ein solches verlangt:

    ftpClient.connectToState("P", QScxmlStateMachine::onEntry([&ftpClient]() {        qDebug() << "Sending password";
        ftpClient.submitEvent("cmd.PASS", QString()); }));

Schließlich stellen wir eine Verbindung zu dem FTP-Server her, der als erstes Argument der Methode angegeben ist, und rufen die als zweites Argument angegebene Datei ab:

    controlChannel.connectToServer(server);
    QObject::connect(&controlChannel, &FtpControlChannel::opened, &dataChannel,
                     [&](const QHostAddress &address, int) {
        dataChannel.listen(address);
        commands[1].args = dataChannel.portspec();
        ftpClient.start();
    });

Der folgende Aufruf gibt zum Beispiel die angegebene Datei vom angegebenen Server aus: ftpclient <server> <file>.

Beispielprojekt @ code.qt.io

© 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.