Auf dieser Seite

SCXML-FTP-Client

Implementiert einen einfachen FTP-Client mithilfe einer Zustandsmaschine.

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

RFC 959 spezifiziert Zustandsdiagramme für die Befehlsverarbeitung des FTP-Clients. Diese lassen sich leicht in SCXML übersetzen, um die Vorteile der verschachtelten Zustände von SCXML zu nutzen. Die Verbindungen zwischen Client und Server sowie die Datenübertragung werden mithilfe von C++ implementiert. Darüber hinaus kommen Qt-Signale und -Slots zum Einsatz.

Die Zustandsmaschine verfügt über die folgenden Zustände:

Zustandsdiagramm eines FTP-Clients mit den Zuständen I, S, F und P sowie den Teilsystemen B und W für Anmelde- und Übertragungsabläufe

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

Die Zustandsmaschine ist in der Datei „simpleftp.scxml“ definiert und in die Klasse „ FtpClient “ kompiliert, die die Logik des FTP-Protokolls implementiert. Sie reagiert auf Benutzereingaben und auf Antworten aus dem Kontrollkanal, indem sie Zustände wechselt und externe Ereignisse sendet. Darüber hinaus implementieren wir eine Klasse „ FtpControlChannel “ und eine Klasse „ FtpDataChannel “, die TCP-Sockets und -Server verwalten und Zeilenenden konvertieren.

Ausführen des Beispiels

Sie können das Beispiel ausführen unter:

Kompilieren der Zustandsmaschine

Wir verknüpfen das Modul „ Qt SCXML “, indem wir die folgende Zeile zu den Build-Dateien des Projekts hinzufügen.

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

QT = core scxml network

Anschließend legen wir die zu kompilierende Zustandsmaschine fest:

STATECHARTS += simpleftp.scxml

Mit CMake fügen wir Folgendes zur Datei „CMakeLists.txt“ hinzu

find_package(Qt6 REQUIRED COMPONENTS Core Network Scxml)

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

Anschließend legen wir die zu kompilierende Zustandsmaschine fest:

qt6_add_statecharts(ftpclient
    simpleftp.scxml
)

Der „ Qt SCXML “-Compiler ( qscxmlc) wird automatisch ausgeführt, um die Dateien „simpleftp.h“ und „simpleftp.cpp“ zu generieren und sie dem Projekt entsprechend als Header- und Quelldateien hinzuzufügen.

Instanziierung der Zustandsmaschine

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 wandeln die Antworten des Servers in Zustandsmaschinenereignisse um:

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

Wir wandeln Befehle aus der Zustandsmaschine in FTP-Steuerungsnachrichten um:

    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, einen Port für die Datenverbindung anzugeben und 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 legen fest, dass der FTP-Client den nächsten Befehl senden soll, sobald der Zustand B erreicht wird:

    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 Zeichenfolge als Passwort senden soll, falls der Server danach fragt:

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

Abschließend stellen wir eine Verbindung zu dem FTP-Server her, der als erstes Argument der Methode angegeben wurde, 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();
    });

Beispielsweise gibt der folgende Aufruf die angegebene Datei vom angegebenen Server aus: ftpclient <server> <file>.

Beispielprojekt @ code.qt.io

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