news 2026/5/13 21:59:38

Qt笔记-使用SSH2进行远程连接linux服务器并上传文件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qt笔记-使用SSH2进行远程连接linux服务器并上传文件

知识点

SSH的全称是Secure Shell,安全外壳协议,从中可以知道,其实就是套了个壳。

以前说过一个过时的协议FTP。但当加上这个SSH后就变成了SFTP,简单理解就说FTP套了一个SSH。

这里以SSH2开源库为例,演示了使用C++ Qt框架,连接服务器并上传文件的过程。

还有个重要的知识点:QTcpSocket中的socketDescriptor()返回的是底层操作系统的原始套接字描述符(socket descriptor)。可以将这个描述符给到SSH2框架中,进行外壳安全。

流程

使用SSH2的通用流程如下:

A. 初始化 libssh2

// 初始化 libssh2 int rc = libssh2_init(0);

B. 建立 TCP 连接

// 建立 TCP 连接 m_socket->connectToHost(host, port);

C. 初始化 SSH 会话

// 初始化 SSH 会话 m_session = libssh2_session_init();

D. 进行 SSH 握手

// 进行 SSH 握手 int rc = libssh2_session_handshake(m_session, m_socket->socketDescriptor());

E. 密码认证

// 密码认证 rc = libssh2_userauth_password(m_session, username.toUtf8().constData(), password.toUtf8().constData());

F. 初始化 SFTP 会话

// 初始化 SFTP 会话 LIBSSH2_SFTP *sftp = libssh2_sftp_init(m_session);

G. 创建远程文件(写模式,权限 0644)

// 创建远程文件(写模式,权限 0644) LIBSSH2_SFTP_HANDLE *sftpHandle = libssh2_sftp_open(sftp, remoteFilePath.toUtf8().constData(), LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC, 0644);

H. 上传写入远程文件

// 写入远程文件 ssize_t bytesWritten = libssh2_sftp_write(sftpHandle, buffer, bytesRead);

I. 清理资源

// 清理资源 libssh2_sftp_close(sftpHandle); libssh2_sftp_shutdown(sftp); libssh2_session_disconnect(m_session, "文件上传完成"); libssh2_session_free(m_session);

代码及运行

SFTPDemo.pro

QT -= gui QT += network CONFIG += c++11 console CONFIG -= app_bundle # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 INCLUDEPATH += D:\Github\libssh2-1.11.1\libssh2-1.11.1\include SOURCES += \ SftpUploader.cpp \ main.cpp # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target # 区分debug和release模式,链接不同版本的库 CONFIG(debug, debug|release) { # Debug模式配置 message("Configuring for Debug mode") # Debug库路径(通常包含debug目录或带d后缀的库) LIBS += -L"D:/Github/libssh2-1.11.1/libssh2-1.11.1/build/src/Debug" # 替换为debug库路径 # Debug版本库(示例:libssh的debug版本可能名为libssh_d或ssh_d) LIBS += -llibssh2 # 假设debug库带d后缀 } else { # Release模式配置 message("Configuring for Release mode") # Release库路径 LIBS += -L"D:/Github/libssh2-1.11.1/libssh2-1.11.1/build/src/Release" # 替换为release库路径 # Release版本库(无后缀) LIBS += -llibssh2 } HEADERS += \ SftpUploader.h

SftpUploader.h

#ifndef SFTP_UPLOADER_H #define SFTP_UPLOADER_H #include <QObject> #include <QTcpSocket> #include <libssh2.h> #include <libssh2_sftp.h> class SftpUploader : public QObject { Q_OBJECT public: explicit SftpUploader(QObject *parent = nullptr); ~SftpUploader(); // 上传文件到 SFTP 服务器 bool uploadFile(const QString &host, int port, const QString &username, const QString &password, const QString &localFilePath, const QString &remoteDir); private: // 创建远程目录(递归创建多级目录) bool createRemoteDir(LIBSSH2_SFTP *sftp, const QString &remoteDir); LIBSSH2_SESSION *m_session; // SSH 会话 QTcpSocket *m_socket; // TCP 连接 }; #endif // SFTP_UPLOADER_H

SftpUploader.cpp

#include "SftpUploader.h" #include <QFile> #include <QDebug> #include <QDir> SftpUploader::SftpUploader(QObject *parent) : QObject(parent), m_session(nullptr), m_socket(new QTcpSocket(this)) { // 初始化 libssh2 int rc = libssh2_init(0); if (rc != 0) { qCritical() << "libssh2 初始化失败: " << rc; } } SftpUploader::~SftpUploader() { // 清理资源 if (m_session) { libssh2_session_disconnect(m_session, "Normal shutdown"); libssh2_session_free(m_session); } libssh2_exit(); } bool SftpUploader::createRemoteDir(LIBSSH2_SFTP *sftp, const QString &remoteDir) { if (remoteDir.isEmpty() || remoteDir == "/") return true; // 按 '/' 分割路径,递归创建 QStringList dirs = remoteDir.split('/', QString::SkipEmptyParts); QString currentPath; foreach (const QString &dir, dirs) { currentPath += "/" + dir; // 检查目录是否存在 LIBSSH2_SFTP_HANDLE *handle = libssh2_sftp_opendir(sftp, currentPath.toUtf8().constData()); if (handle) { libssh2_sftp_closedir(handle); continue; // 目录已存在,继续下一级 } // 目录不存在,创建目录(权限 0755) int rc = libssh2_sftp_mkdir(sftp, currentPath.toUtf8().constData(), 0755); if (rc != 0) { qCritical() << "创建远程目录失败: " << currentPath << " 错误码: " << rc; return false; } } return true; } bool SftpUploader::uploadFile(const QString &host, int port, const QString &username, const QString &password, const QString &localFilePath, const QString &remoteDir) { // 1. 建立 TCP 连接 m_socket->connectToHost(host, port); if (!m_socket->waitForConnected(5000)) { qCritical() << "TCP 连接失败: " << m_socket->errorString(); return false; } // 2. 初始化 SSH 会话 m_session = libssh2_session_init(); if (!m_session) { qCritical() << "SSH 会话初始化失败"; m_socket->close(); return false; } // 3. 进行 SSH 握手 int rc = libssh2_session_handshake(m_session, m_socket->socketDescriptor()); if (rc != 0) { qCritical() << "SSH 握手失败: " << libssh2_session_last_error(m_session, nullptr, nullptr, 0); libssh2_session_free(m_session); m_session = nullptr; m_socket->close(); return false; } // 4. 密码认证 rc = libssh2_userauth_password(m_session, username.toUtf8().constData(), password.toUtf8().constData()); if (rc != 0) { qCritical() << "SSH 认证失败: " << libssh2_session_last_error(m_session, nullptr, nullptr, 0); libssh2_session_disconnect(m_session, "认证失败"); libssh2_session_free(m_session); m_session = nullptr; m_socket->close(); return false; } // 5. 初始化 SFTP 会话 LIBSSH2_SFTP *sftp = libssh2_sftp_init(m_session); if (!sftp) { qCritical() << "SFTP 初始化失败: " << libssh2_session_last_error(m_session, nullptr, nullptr, 0); libssh2_session_disconnect(m_session, "SFTP 初始化失败"); libssh2_session_free(m_session); m_session = nullptr; m_socket->close(); return false; } // 6. 创建远程目录 if (!createRemoteDir(sftp, remoteDir)) { libssh2_sftp_shutdown(sftp); libssh2_session_disconnect(m_session, "创建目录失败"); libssh2_session_free(m_session); m_session = nullptr; m_socket->close(); return false; } // 7. 打开本地文件 QFile localFile(localFilePath); if (!localFile.open(QIODevice::ReadOnly)) { qCritical() << "打开本地文件失败: " << localFile.errorString(); libssh2_sftp_shutdown(sftp); libssh2_session_disconnect(m_session, "打开本地文件失败"); libssh2_session_free(m_session); m_session = nullptr; m_socket->close(); return false; } // 8. 构建远程文件路径 QString fileName = QFileInfo(localFile).fileName(); QString remoteFilePath = remoteDir + "/" + fileName; if (remoteFilePath.startsWith("//")) { remoteFilePath = remoteFilePath.mid(1); // 处理路径拼接可能出现的双斜杠 } // 9. 创建远程文件(写模式,权限 0644) LIBSSH2_SFTP_HANDLE *sftpHandle = libssh2_sftp_open(sftp, remoteFilePath.toUtf8().constData(), LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC, 0644); if (!sftpHandle) { qCritical() << "创建远程文件失败: " << remoteFilePath << " 错误: " << libssh2_sftp_last_error(sftp); localFile.close(); libssh2_sftp_shutdown(sftp); libssh2_session_disconnect(m_session, "创建远程文件失败"); libssh2_session_free(m_session); m_session = nullptr; m_socket->close(); return false; } // 10. 上传文件内容 const int BUFFER_SIZE = 1024 * 8; char buffer[BUFFER_SIZE]; qint64 bytesRead; bool uploadSuccess = true; while ((bytesRead = localFile.read(buffer, BUFFER_SIZE)) > 0) { // 写入远程文件 ssize_t bytesWritten = libssh2_sftp_write(sftpHandle, buffer, bytesRead); if (bytesWritten != bytesRead) { qCritical() << "文件写入失败,已写: " << bytesWritten << " 需写: " << bytesRead; uploadSuccess = false; break; } } // 11. 清理资源 localFile.close(); libssh2_sftp_close(sftpHandle); libssh2_sftp_shutdown(sftp); libssh2_session_disconnect(m_session, "文件上传完成"); libssh2_session_free(m_session); m_session = nullptr; m_socket->close(); if (uploadSuccess && localFile.error() == QFile::NoError) { qInfo() << QString::fromLocal8Bit("文件上传成功: ") << remoteFilePath; return true; } else { qCritical() << QString::fromLocal8Bit("文件上传失败: ") << localFile.errorString(); return false; } }

main.cpp

#include <QCoreApplication> #include"SftpUploader.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 配置服务器信息(替换为实际信息) QString host = "xx.xx.xx.xx"; // Linux 服务器 IP int port = 22; // SSH 默认端口 QString username = "root"; // 用户名 QString password = "root"; // 密码 QString localFilePath = "D:/test.py"; // 本地文件路径 QString remoteDir = "/var/www/html/Demonstration.8/public/md"; // 远程目录 // 执行上传 SftpUploader uploader; bool success = uploader.uploadFile(host, port, username, password, localFilePath, remoteDir); return success ? 0 : 1; }

程序运行截图如下:

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/12 5:31:42

植物大战僵尸杂交版下载安装教程(v0.2最新重制版全平台图文详解)

前言 要说 2025 年最受玩家讨论的塔防游戏&#xff0c;《植物大战僵尸杂交版-最新重制版 v0.2》绝对是热度榜前列的一员。 这款版本在延续经典玩法的基础上&#xff0c;融入全新的杂交系统、变异关卡和高画质重制&#xff0c;让老玩家重新找回了当年的手感&#xff0c;也让新玩…

作者头像 李华
网站建设 2026/5/12 14:06:30

JupyterLab插件推荐:提升PyTorch开发效率的十大扩展

JupyterLab插件推荐&#xff1a;提升PyTorch开发效率的十大扩展 在深度学习项目中&#xff0c;你是否曾为调试模型时找不到关键代码段而焦头烂额&#xff1f;是否因为环境不一致导致“本地能跑、服务器报错”的尴尬局面&#xff1f;又或者&#xff0c;在训练过程中眼睁睁看着GP…

作者头像 李华
网站建设 2026/5/9 20:50:47

实时数据有哪些特点?企业该如何管理好实时数据?

目录 第一部分&#xff1a;实时数据&#xff0c;究竟“特”在哪里&#xff1f; 第二部分&#xff1a;管好实时数据&#xff0c;企业需要构建的四大能力 第三部分&#xff1a;踏上实时之路的务实建议 Q&A 常见问答 在我工作的这些年里&#xff0c;经历了一个特别明显的变…

作者头像 李华
网站建设 2026/5/11 1:47:21

什么是数据标准?数据标准有什么作用?

目录 第一部分&#xff1a;数据标准&#xff0c;究竟是什么&#xff1f; 第二部分&#xff1a;数据标准的作用&#xff0c;远不止“统一口径” 第三部分&#xff1a;数据标准的挑战与如何开始 Q&A 常见问答 大家好。在我这些年的数据工作经历里&#xff0c;见过太多因为…

作者头像 李华
网站建设 2026/5/12 7:16:51

AI全景之第八章第一节:语音识别、合成与声音生成

语音技术:语音识别、合成与声音生成核心技术解析 语音技术作为人工智能与人类自然交互的关键桥梁,在近十年中实现了从实验室研究到大规模商业落地的跨越式发展。从早期的基于隐马尔可夫模型的系统,到如今的端到端深度学习架构,语音技术正推动着智能助理、无障碍通信、内容…

作者头像 李华
网站建设 2026/5/9 13:40:14

汽车制造:DeepSeek辅助设备故障预测与停机时间降低方案

摘要 在现代汽车制造业中&#xff0c;生产设备的稳定运行是保障生产效率、产品质量和成本控制的核心要素。然而&#xff0c;设备故障导致的非计划停机时间一直是困扰制造企业的痛点&#xff0c;不仅造成巨大的产能损失和维修成本&#xff0c;还会影响订单交付&#xff0c;损害企…

作者头像 李华