news 2026/5/5 19:59:34

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/3 6:27:31

【Java毕设源码分享】基于springboot+vue的小型民营加油站管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/5/3 10:06:10

【Java毕设源码分享】基于springboot+vue的农业收成管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

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

计算机毕业设计springboot基于微信小程序的树洞系统 基于微信小程序的匿名树洞交流平台设计与实现 微信小程序环境下树洞信息管理系统的开发

计算机毕业设计springboot基于微信小程序的树洞系统zt81t9 &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。随着互联网的飞速发展&#xff0c;人们对于信息交流的需求日益多样化。…

作者头像 李华
网站建设 2026/5/1 8:28:33

港大团队提出 DeepCode ,让 Agent 真能“读论文写可运行代码”

港大团队提出 DeepCode &#xff0c;让 Agent 真能“读论文写可运行代码” 01 论文概述 这篇论文来自香港大学团队&#xff08;通讯作者&#xff1a;Chao Huang&#xff09;。论文提出并开源了 DeepCode &#xff1a;一个能“自动写项目”的智能体框架&#xff0c;想解决的不…

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

Ooder A2UI框架开源首发:构建企业级应用的全新选择

Ooder框架开源首发&#xff1a;构建企业级应用的全新选择 &#x1f389; 重磅消息&#xff01;Ooder框架正式开源发布&#xff0c;版本号0.5&#xff01; 作为一款专注于企业级应用开发的框架&#xff0c;Ooder以其独特的设计理念和强大的功能&#xff0c;为开发者提供了构建复…

作者头像 李华
网站建设 2026/4/30 23:24:33

通过SSH连接远程服务器运行长时间PyTorch任务

通过SSH连接远程服务器运行长时间PyTorch任务 在深度学习项目中&#xff0c;训练一个大型模型动辄需要数小时甚至数天。你是否经历过这样的场景&#xff1a;本地笔记本风扇狂转、温度飙升&#xff0c;结果刚跑完两个epoch&#xff0c;Wi-Fi断了&#xff0c;SSH终端一黑&#xf…

作者头像 李华