SCXML FTP 클라이언트
상태 머신을 사용하여 간단한 FTP 클라이언트를 구현합니다.
FTP 클라이언 트는 Qt SCXML 을 사용하여 스테이트 머신 이벤트에서 변환된 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 에서 예제를 선택합니다. 자세한 내용은 예제 빌드 및 실행하기를 참조하세요.
상태 머신 컴파일하기
프로젝트 빌드 파일에 다음 줄을 추가하여 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.h와 simpleftp.cpp를 생성하고 헤더와 소스로 프로젝트에 적절하게 추가합니다.
스테이트 머신 인스턴스화
생성된 FtpClient
클래스와 FtpDataChannel
및 FtpControlChannel
클래스를 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; ...
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 서버에 연결하고 두 번째 인수로 지정된 파일을 검색합니다:
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>
.
© 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.