Cliente FTP SCXML
Implementa un cliente FTP simple utilizando una máquina de estados.
Cliente FTP utiliza Qt SCXML para implementar un cliente FTP que puede comunicarse con un servicio FTP enviando mensajes de control FTP traducidos a partir de eventos de máquina de estados y traduciendo las respuestas del servidor a eventos de máquina de estados. Los datos recibidos del servidor FTP se imprimen en la consola.
RFC 959 especifica diagramas de estado para el manejo de comandos del cliente FTP. Pueden traducirse fácilmente a SCXML para beneficiarse de los estados anidados de SCXML. Las conexiones entre el cliente y el servidor y la transferencia de datos se implementan utilizando C++. Además, se utilizan señales y ranuras Qt.
La máquina de estados tiene los siguientes estados:

- I como estado inicial.
- B para el envío de comandos.
- S para éxito.
- F para fallo.
- W para esperar una respuesta.
- P para proporcionar una contraseña a petición del servidor.
La máquina de estados se especifica en el archivo simpleftp.scxml y se compila en la clase FtpClient que implementa la lógica del protocolo FTP. Reacciona a las entradas del usuario y a las respuestas del canal de control cambiando de estado y enviando eventos externos. Además, implementamos una clase FtpControlChannel y una clase FtpDataChannel que manejan sockets y servidores TCP y convierten los finales de línea.
Ejecución del ejemplo
Para ejecutar el ejemplo desde Qt Creatorabra el modo Welcome y seleccione el ejemplo de Examples. Para más información, consulte Qt Creator: Tutorial: Construir y ejecutar.
Compilación de la máquina de estados
Enlazamos con el módulo Qt SCXML añadiendo la siguiente línea a los archivos de compilación del proyecto.
Con qmake, añadimos lo siguiente a ftpclient.pro
QT = core scxml network
Luego especificamos la máquina de estados a compilar:
STATECHARTS += simpleftp.scxml
Con CMake, añadimos lo siguiente a CMakeLists.txt
find_package(Qt6 REQUIRED COMPONENTS Core Network Scxml)
target_link_libraries(ftpclient PRIVATE
Qt6::Core
Qt6::Network
Qt6::Scxml
)Luego especificamos la máquina de estados a compilar:
qt6_add_statecharts(ftpclient
simpleftp.scxml
)El compilador Qt SCXML, qscxmlc, se ejecuta automáticamente para generar simpleftp.h y simpleftp.cpp, y añadirlos adecuadamente al proyecto como cabeceras y fuentes.
Instanciar la máquina de estados
Instanciamos la clase FtpClient generada, así como las clases FtpDataChannel y FtpControlChannel en el archivo 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; ...
Comunicación con un Servidor FTP
Imprimimos todos los datos recuperados del servidor en la consola:
QObject::connect(&dataChannel, &FtpDataChannel::dataReceived, [](const QByteArray &data) { std::cout << data.constData() << std::flush; });
Traducimos las respuestas del servidor en eventos de la máquina de estados:
QObject::connect(&controlChannel, &FtpControlChannel::reply, &ftpClient, [&ftpClient](int code, const QString ¶meters) { ftpClient.submitEvent(QString("reply.%1xx") .arg(code / 100), parameters); });
Traducimos los comandos de la máquina de estados en mensajes de control FTP:
ftpClient.connectToEvent("submit.cmd", &controlChannel, [&controlChannel](const QScxmlEvent &event) { controlChannel.command(event.name().mid(11).toUtf8(), event.data().toMap()["params"].toByteArray()); });
Enviamos comandos para iniciar sesión en el servidor FTP como usuario anónimo, para anunciar un puerto para la conexión de datos y para recuperar un archivo:
QList<Command> commands({ {"cmd.USER", "anonymous"},// login {"cmd.PORT", ""}, // announce port for data connection, // args added below. {"cmd.RETR", file} // retrieve a file });
Especificamos que el cliente FTP debe enviar el siguiente comando al entrar en el estado B:
ftpClient.connectToState("B", QScxmlStateMachine::onEntry([&]() { if (commands.isEmpty()) { app.quit(); return; } Comando command = commands.takeFirst(); qDebug() << "Posting command" << command.cmd << command.args; ftpClient.submitEvent(command.cmd, command.args); }));
Especificamos que el cliente FTP debe enviar una cadena vacía como contraseña si el servidor pide una:
ftpClient.connectToState("P", QScxmlStateMachine::onEntry([&ftpClient]() { qDebug() << "Sending password"; ftpClient.submitEvent("cmd.PASS", QString(); }));
Finalmente, nos conectamos al servidor FTP especificado como primer argumento del método y recuperamos el fichero especificado como segundo argumento:
controlChannel.connectToServer(server); QObject::connect(&controlChannel, &FtpControlChannel::opened, &dataChannel, [&](const QHostAddress &address, int) { dataChannel.listen(address); commands[1].args = dataChannel.portspec(); ftpClient.start(); });
Por ejemplo, la siguiente invocación imprime el archivo especificado desde el servidor especificado: 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.