sftp 접속 및 xml 다운로드 후, 원하는 값 꺼내오기
- 최초 작성일: 2023년 6월 29일 (목)
목차
[TOC]
코드 (CksFtp 라이브러리 활용)
사용한 라이브러리
- sftp 접속: CksFtp
- xml 접속: pugixml
CksFtp 라이브러리는 상용(유료) 소프트웨어로 제공된다. 한 달 사용 후에는 해당 라이브러리를 계속 이용하려면 유료 라이선스를 구매해야 한다. 이 점을 참고하시기 바란다.
필자도 제대로 확인 안 하고 사용했다가 갑자기 작동이 안되어서 당황했다.
그래서, 아래에 무료인 libssh 라이브러리를 사용한 버전을 구현했다.
bool CExampleDlg::connectToServer(CkSFtp& sftp, const char* hostname, int port) {
if (sftp.Connect(hostname, port) != true) {
std::cerr << sftp.lastErrorText() << "\n";
return false;
}
if (sftp.AuthenticatePw("root", "fa") != true) {
std::cerr << sftp.lastErrorText() << "\n";
return false;
}
if (sftp.InitializeSftp() != true) {
std::cerr << sftp.lastErrorText() << "\n";
return false;
}
return true;
}
bool CExampleDlg::downloadRemoteFile(CkSFtp& sftp, const char* remoteFilePath, const char* localFilePath) {
const char* handle = sftp.openFile(remoteFilePath, "readOnly", "openExisting");
if (sftp.get_LastMethodSuccess() != true) {
std::cerr << sftp.lastErrorText() << "\n";
return false;
}
if (sftp.DownloadFile(handle, localFilePath) != true) {
std::cerr << sftp.lastErrorText() << "\n";
return false;
}
if (sftp.CloseHandle(handle) != true) {
std::cerr << sftp.lastErrorText() << "\n";
return false;
}
return true;
}
pugi::xml_document CExampleDlg::loadAndParseXml(const char* xmlFilePath) {
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(xmlFilePath);
if (!result) {
std::cerr << "Parsing failed with description: " << result.description() << "\n";
return {};
}
return doc;
}
std::string CExampleDlg::findAndValidateNode(pugi::xml_document& doc, const char* nodeName) {
pugi::xml_node node = doc.child("Root").child(nodeName);
if (!node) {
std::cerr << nodeName << " node not found." << std::endl;
return "";
}
std::string nodeValue = node.text().get();
try {
if (std::stoi(nodeValue) >= 100 || std::stoi(nodeValue) <= -100) {
std::cerr << "Invalid value for " << nodeName << ": " << nodeValue << std::endl;
return "";
}
}
catch (std::exception& e) {
std::cerr << "Exception caught trying to convert " << nodeName << " to integer: " << e.what() << std::endl;
return "";
}
return nodeValue;
}
std::pair<std::string, std::string> CExampleDlg::connectSftp() {
CkSFtp sftp;
if (!connectToServer(sftp, "192.168.8.1", 22)) {
return { "", "" };
}
if (!downloadRemoteFile(sftp, "/home/pi/test/test.xml", "./xml/test.xml")) {
return { "", "" };
}
pugi::xml_document doc = loadAndParseXml("./xml/test.xml");
if (!doc) {
return { "", "" };
}
std::string setPhaseCalW = findAndValidateNode(doc, "SetPhaseCalW");
std::string setPhaseCalH = findAndValidateNode(doc, "SetPhaseCalH");
if (setPhaseCalW.empty() || setPhaseCalH.empty()) {
return { "", "" };
}
std::cout << "setPhaseCalW: " << setPhaseCalW << std::endl;
std::cout << "setPhaseCalH: " << setPhaseCalH << std::endl;
return { setPhaseCalW, setPhaseCalH };
}
코드2 (libssh2 라이브러리 활용)
bool CExampleDlg::connectToServer(LIBSSH2_SESSION*& session, const char* hostname, int port, const char* username, const char* password) {
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
// Use inet_pton instead of inet_addr
if (inet_pton(AF_INET, hostname, &sin.sin_addr) <= 0) {
std::cerr << "Invalid address format\n";
return false;
}
if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0) {
std::cerr << "Failed to connect to " << hostname << "\n";
return false;
}
session = libssh2_session_init();
if (libssh2_session_handshake(session, sock)) {
std::cerr << "Failed to establish SSH session\n";
return false;
}
if (libssh2_userauth_password(session, username, password)) {
std::cerr << "Authentication failed\n";
return false;
}
return true;
}
bool CExampleDlg::downloadRemoteFile(LIBSSH2_SESSION*& session, const char* remoteFilePath, const char* localFilePath)
{
LIBSSH2_SFTP* sftp_session = libssh2_sftp_init(session);
if (!sftp_session) {
std::cerr << "Failed to initialize SFTP session\n";
return false;
}
unsigned int filename_len = static_cast<unsigned int>(strlen(remoteFilePath));
LIBSSH2_SFTP_HANDLE* sftp_handle = libssh2_sftp_open_ex(sftp_session, remoteFilePath, filename_len, LIBSSH2_FXF_READ, 0, LIBSSH2_SFTP_OPENFILE);
if (!sftp_handle) {
std::cerr << "Failed to open file with SFTP\n";
libssh2_sftp_shutdown(sftp_session);
return false;
}
FILE* localFile;
errno_t err = fopen_s(&localFile, localFilePath, "wb");
if (err != 0 || !localFile) {
std::cerr << "Failed to open local file\n";
libssh2_sftp_close(sftp_handle);
libssh2_sftp_shutdown(sftp_session);
return false;
}
char buffer[1024];
while (true) {
ssize_t amount = libssh2_sftp_read(sftp_handle, buffer, static_cast<unsigned int>(sizeof(buffer))); // explicit cast to unsigned int
if (amount > 0) {
size_t result = fwrite(buffer, 1, static_cast<size_t>(amount), localFile);
if (result != static_cast<size_t>(amount)) {
std::cerr << "Failed to write to local file\n";
fclose(localFile);
libssh2_sftp_close(sftp_handle);
libssh2_sftp_shutdown(sftp_session);
return false;
}
}
else if (amount == 0) {
break; // EOF reached
}
else {
std::cerr << "Failed to read from remote file\n";
fclose(localFile);
libssh2_sftp_close(sftp_handle);
libssh2_sftp_shutdown(sftp_session);
return false;
}
}
fclose(localFile);
libssh2_sftp_close(sftp_handle);
libssh2_sftp_shutdown(sftp_session);
return true;
}
pugi::xml_document CExampleDlg::loadAndParseXml(const char* xmlFilePath) {
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(xmlFilePath);
if (!result) {
std::cerr << "Parsing failed with description: " << result.description() << "\n";
return {};
}
return doc;
}
std::string CExampleDlg::findAndValidateNode(pugi::xml_document& doc, const char* nodeName) {
pugi::xml_node node = doc.child("Root").child(nodeName);
if (!node) {
std::cerr << nodeName << " node not found." << std::endl;
return "";
}
std::string nodeValue = node.text().get();
try {
if (std::stoi(nodeValue) >= 100 || std::stoi(nodeValue) <= -100) {
std::cerr << "Invalid value for " << nodeName << ": " << nodeValue << std::endl;
return "";
}
}
catch (std::exception& e) {
std::cerr << "Exception caught trying to convert " << nodeName << " to integer: " << e.what() << std::endl;
return "";
}
return nodeValue;
}
std::pair<std::string, std::string> CExampleDlg::connectSftp() {
LIBSSH2_SESSION* session;
if (!connectToServer(session, "192.168.8.1", 22, "root", "fa")) {
return { "", "" };
}
if (!downloadRemoteFile(session, "/home/pi/test/test.xml", "./xml/test.xml")) {
libssh2_session_disconnect(session, "Finished session");
libssh2_session_free(session);
return { "", "" };
}
pugi::xml_document doc = loadAndParseXml("./xml/test.xml");
if (!doc) {
libssh2_session_disconnect(session, "Finished session");
libssh2_session_free(session);
return { "", "" };
}
std::string setPhaseCalW = findAndValidateNode(doc, "SetPhaseCalW");
std::string setPhaseCalH = findAndValidateNode(doc, "SetPhaseCalH");
if (setPhaseCalW.empty() || setPhaseCalH.empty()) {
libssh2_session_disconnect(session, "Finished session");
libssh2_session_free(session);
return { "", "" };
}
std::cout << "setPhaseCalW: " << setPhaseCalW << std::endl;
std::cout << "setPhaseCalH: " << setPhaseCalH << std::endl;
libssh2_session_disconnect(session, "Finished session");
libssh2_session_free(session);
return { setPhaseCalW, setPhaseCalH };
}