SSH连接SFTP传输:如何使用libssh库在Linux环境下进行(文件、文件夹)传输到远端服务器

  • 建立SSH会话并连接远端服务器
  • SSH身份验证
    • 密码验证
    • 密钥验证
      • 生成密钥
      • 查看密钥
      • 拷贝密钥
      • 验证密钥是否正确
  • SFTP子系统构建
  • 传输普通文件
  • 递归传输文件夹
  • 完整传输小demo

建立SSH会话并连接远端服务器

  • target_host:远端主机IP
  • target_username:远端主机用户名
ssh_session session;

// 连接SSH会话
session = ssh_new();
if (!session) {
    fprintf(stderr, "Failed to create SSH session\n");
    return -1;
}

ssh_options_set(session, SSH_OPTIONS_HOST, target_host);
ssh_options_set(session, SSH_OPTIONS_USER, target_username);

//建立连接
if (ssh_connect(session) != SSH_OK) {
    fprintf(stderr, "Failed to connect to SSH session: %s\n", ssh_get_error(session));
    ssh_free(session);
    return -1;
}

ssh_options_set() 函数设置会话的选项。最重要的选项是:

  • SSH_OPTIONS_HOST:要连接到的主机的名称
  • SSH_OPTIONS_PORT:使用的端口(默认为端口 22)
  • SSH_OPTIONS_USER:要连接的系统用户
  • SSH_OPTIONS_LOG_VERBOSITY:打印的消息数量

SSH身份验证

密码验证

直接传输密码即可 target_password即密码

if (ssh_userauth_password(session, NULL, target_password) != SSH_AUTH_SUCCESS) {
        fprintf(stderr, "Failed to authenticate with password\n");
        ssh_disconnect(session);
        ssh_free(session);
        return -1;
    }

密钥验证

生成密钥

  • 基于ED25519算法,生成密钥对命令如下:
ssh-keygen -t ed25519 -C "<注释内容>"
  • 基于RSA算法,生成密钥对命令如下:
ssh-keygen -t rsa -C "<注释内容>"

默认回车即可

查看密钥

ED25519 算法

cat ~/.ssh/id_ed25519.pub

RSA 算法

cat ~/.ssh/id_rsa.pub

拷贝密钥

Windows(WSLGit Bash):

cat ~/.ssh/id_ed25519.pub | clip

Mac:

tr -d '\n' < ~/.ssh/id_ed25519.pub | pbcopy

GNU/Linux (requires xclip):

xclip -sel clip < ~/.ssh/id_ed25519.pub

复制到所需传入的服务器端的~/.ssh目录的authorized_keys文件里面

验证密钥是否正确

if (ssh_userauth_publickey_auto(session, NULL, NULL) != SSH_AUTH_SUCCESS) {
        fprintf(stderr, "Authentication failed: %s\n", ssh_get_error(session));
        ssh_disconnect(session);
        ssh_free(session);
        return -1;
    }

SFTP子系统构建

sftp_session sftp;

// 打开SFTP通道
sftp = sftp_new(session);
if (!sftp) {
    fprintf(stderr, "Failed to create SFTP session\n");
    ssh_disconnect(session);
    ssh_free(session);
    return -1;
}

if (sftp_init(sftp) != SSH_OK) {
    fprintf(stderr, "Failed to initialize SFTP session\n");
    sftp_free(sftp);
    ssh_disconnect(session);
    ssh_free(session);
    return -1;
}

传输普通文件

  • local_file_path:本地需传文件路径
  • remote_file_path:传送至远端服务器路径
int sftp_normal_upload(sftp_session sftp, const char* local_file_path, const char* remote_file_path)
{
    sftp_file file = sftp_open(sftp, remote_file_path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
    if (!file) {
        fprintf(stderr, "Failed to open remote file: %s\n", remote_file_path);
        return -1;
    }

    FILE* local_file = fopen(local_file_path, "rb");
    if (!local_file) {
        fprintf(stderr, "Failed to open local file: %s\n", local_file_path);
        sftp_close(file);
        return -1;
    }

    // 上传文件内容
    char buffer[1024];
    size_t bytes_read;
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), local_file)) > 0) {
        sftp_write(file, buffer, bytes_read);
    }

    fclose(local_file);
    sftp_close(file);
}

递归传输文件夹

sftp没有直接的传输文件夹的接口,我们需要递归目录来传输

假设有下面文件夹

           +-- file1
   +-- B --+
   |       +-- file2
   A        
   |       +-- file3
   +-- C --+
           +-- file4

打开目录 A -》进入子目录 B -》在 B 中创建文件 file1 -》在 B 中创建 file2 -》离开目录 B -》进入子目录 C -》在 C 语言中创建 file3 -》 在 C 语言中创建 file4 -》离开目录 C -》离开目录 A

本地遍历的时候,远端同时创建

  • local_path:本地路径
  • remote_path:远端路径
int sftp_recursive_upload(ssh_session session, sftp_session sftp,
				 const char* local_path, const char* remote_path) {
    // 打开本地目录
    DIR* local_dir = opendir(local_path);
    if (!local_dir) {
        fprintf(stderr, "Failed to open local directory: %s\n", local_path);
        return -1;
    }

    // 创建服务器目录
    sftp_mkdir(sftp, remote_path, S_IRWXU);

    // 遍历本地目录项
    struct dirent* entry;
    while ((entry = readdir(local_dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue; // Skip "." and ".."
        }

        // 构造全路径
        char local_file_path[1024];
        snprintf(local_file_path, sizeof(local_file_path), "%s/%s", local_path, entry->d_name);
        char remote_file_path[1024];
        snprintf(remote_file_path, sizeof(remote_file_path), "%s/%s", remote_path, entry->d_name);

        // 如果本地条目是一个目录,递归上传它
        if (entry->d_type == DT_DIR) {
            sftp_recursive_upload(session, sftp, local_file_path, remote_file_path);
        }
        else { // 如果本地条目是一个普通文件,上传它
            sftp_normal_upload(sftp, local_file_path, remote_file_path);
        }
    }

    closedir(local_dir);
    return 0;
}

完整传输小demo

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libssh/libssh.h>
#include <libssh/sftp.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>

const char* target_host = "xxx.xx.xxx.xxx";
const char* target_username = "AMY";
const char* target_password = "xxxxx";

int sftp_normal_upload(sftp_session sftp, const char* local_file_path, const char* remote_file_path)
{
    sftp_file file = sftp_open(sftp, remote_file_path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
    if (!file) {
        fprintf(stderr, "Failed to open remote file: %s\n", remote_file_path);
        return -1;
    }

    FILE* local_file = fopen(local_file_path, "rb");
    if (!local_file) {
        fprintf(stderr, "Failed to open local file: %s\n", local_file_path);
        sftp_close(file);
        return -1;
    }

    // 上传文件内容
    char buffer[1024];
    size_t bytes_read;
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), local_file)) > 0) {
        sftp_write(file, buffer, bytes_read);
    }

    fclose(local_file);
    sftp_close(file);
}

int sftp_recursive_upload(ssh_session session, sftp_session sftp, const char* local_path, const char* remote_path) {
    // 打开本地目录
    DIR* local_dir = opendir(local_path);
    if (!local_dir) {
        fprintf(stderr, "Failed to open local directory: %s\n", local_path);
        return -1;
    }

    // 创建服务器目录
    sftp_mkdir(sftp, remote_path, S_IRWXU);

    // 遍历本地目录项
    struct dirent* entry;
    while ((entry = readdir(local_dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue; // Skip "." and ".."
        }

        // 构造全路径
        char local_file_path[1024];
        snprintf(local_file_path, sizeof(local_file_path), "%s/%s", local_path, entry->d_name);
        char remote_file_path[1024];
        snprintf(remote_file_path, sizeof(remote_file_path), "%s/%s", remote_path, entry->d_name);

        // 如果本地条目是一个目录,递归上传它
        if (entry->d_type == DT_DIR) {
            sftp_recursive_upload(session, sftp, local_file_path, remote_file_path);
        }
        else { // 如果本地条目是一个普通文件,上传它
            sftp_normal_upload(sftp, local_file_path, remote_file_path);
        }
    }

    closedir(local_dir);
    return 0;
}

int main() {
    ssh_session session;
    sftp_session sftp;

    // 连接SSH会话
    session = ssh_new();
    if (!session) {
        fprintf(stderr, "Failed to create SSH session\n");
        return -1;
    }

    ssh_options_set(session, SSH_OPTIONS_HOST, target_host);
    ssh_options_set(session, SSH_OPTIONS_USER, target_username);

    if (ssh_connect(session) != SSH_OK) {
        fprintf(stderr, "Failed to connect to SSH session: %s\n", ssh_get_error(session));
        ssh_free(session);
        return -1;
    }

    // 身份验证
    if (ssh_userauth_password(session, NULL, target_password) != SSH_AUTH_SUCCESS) {
        fprintf(stderr, "Failed to authenticate with password\n");
        ssh_disconnect(session);
        ssh_free(session);
        return -1;
    }

    // 打开SFTP通道
    sftp = sftp_new(session);
    if (!sftp) {
        fprintf(stderr, "Failed to create SFTP session\n");
        ssh_disconnect(session);
        ssh_free(session);
        return -1;
    }

    if (sftp_init(sftp) != SSH_OK) {
        fprintf(stderr, "Failed to initialize SFTP session\n");
        sftp_free(sftp);
        ssh_disconnect(session);
        ssh_free(session);
        return -1;
    }


    // 得到当前 文件/目录 所在路径
    char local_dir[1024];
    getcwd(local_dir, sizeof(local_dir));
    printf("请输入所需传入的文件或目录的名字\n");
    int len = strlen(local_dir);
    local_dir[len] = '/';
    local_dir[len + 1] = '\0';

    char filename[100];
    scanf("%s", filename);
    strcat(local_dir, filename);

    struct stat file_stat;

    // 获取文件的详细信息
    if (stat(local_dir, &file_stat) != 0) {
        perror("stat");
        return -1;
    }

    printf("请输入所需传入远端路径\n");
    char target_path[1024];
    scanf("%s", target_path);

    // 判断文件类型

    //普通文件
    if (S_ISREG(file_stat.st_mode)) {
        sftp_normal_upload(sftp, local_dir, target_path);
    }
    //目录文件
    else if (S_ISDIR(file_stat.st_mode)) {
        // 递归上传本地目录到远程目录
        if (sftp_recursive_upload(session, sftp, local_dir, target_path) != 0) {
            fprintf(stderr, "Failed to recursively upload directory\n");
            sftp_free(sftp);
            ssh_disconnect(session);
            ssh_free(session);
            return -1;
        }
    }
    else {
        printf("%s 既不是普通文件,也不是目录,无法上传\n", local_dir);
    }

    // 释放
    sftp_free(sftp);
    ssh_disconnect(session);
    ssh_free(session);

    return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/495014.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Echarts之x轴,Y轴配置项大全

ECharts是一个强大的数据可视化库&#xff0c;提供了丰富的配置项来定制图表的x轴和y轴。下面是ECharts中x轴和y轴的配置项大全&#xff1a; xAxis配置项&#xff1a; type&#xff1a;轴类型&#xff0c;可选值有&#xff1a;“value”&#xff08;数值轴&#xff09;, “cat…

生产调度问题分类——机器视角

获取更多资讯&#xff0c;赶快关注上面的公众号吧&#xff01; 文章目录 单机调度并行机调度流水车间调度作业车间调度柔性作业车间开放车间总结 生产调度问题是实际工作中广泛存在的运筹学问题&#xff0c;可以描述为“给定若干加工任务&#xff0c;根据已有的生产条件&#…

ubuntu之搭建samba文件服务器

1. 在服务器端安装samba程序 sudo apt-get install samba sudo apt-get install smbclient 2.配置samba服务 sudo gedit /etc/samba/smb.conf 在文件末尾追加入以下配置 [develop_share] valid users ancy path /home/ancy public yes writable y…

Tuxera for Mac2024免费读写硬盘U盘工具

作为软件产品专家&#xff0c;我对各类软件都有较为深入的了解&#xff0c;下面介绍Tuxera for Mac这款读写硬盘/U盘工具的相关信息&#xff1a; Tuxera for Mac是一款高效稳定的NTFS读写工具&#xff0c;专为解决Mac系统无法直接读写NTFS格式驱动器的问题而设计。它提供了完整…

直接插入排序 希尔排序 选择排序 堆排序

目录 一. 排序的概念及应用 1.1 排序的概念 1.2 常见的排序算法 二. 常见排序算法的实现(从小到大排序) 2.1 插入排序 2.1.1基本思想&#xff1a; 2.1.2 直接插入排序 2.1.3 希尔排序( 缩小增量排序) 2.2 选择排序 2.2.1基本思想&#xff1a; 2.2.2 直接选择排序: 2…

保障校园网络安全用堡垒机的几个原因分析

校园&#xff0c;人人都熟悉的地方&#xff0c;梦想知识开始的地方。在互联网数字化快速发展的今天&#xff0c;网络安全的学习环境是非常必要的。所以采购保障校园网络安全工具是必要的。那为什么一定要用堡垒机呢&#xff1f;这里我们一起来简单分析一下原因。 保障校园网络…

iOS - Runtime-消息机制-objc_msgSend()

iOS - Runtime-消息机制-objc_msgSend() 前言 本章主要介绍消息机制-objc_msgSend的执行流程&#xff0c;分为消息发送、动态方法解析、消息转发三个阶段&#xff0c;每个阶段可以做什么。还介绍了super的本质是什么&#xff0c;如何调用的 1. objc_msgSend执行流程 OC中的…

OceanBase中NOT EXISTS是否需要被改写

作者简介 张瑞远&#xff0c;曾经从事银行、证券数仓设计、开发、优化类工作&#xff0c;现主要从事电信级IT系统及数据库的规划设计、架构设计、运维实施、运维服务、故障处理、性能优化等工作。 持有Orale OCM,MySQL OCP及国产代表数据库认证。 获得的专业技能与认证包括 Oce…

安卓玩机工具推荐----MTK芯片读写分区 备份分区 恢复分区 制作线刷包 从0开始 工具操作解析【三】

同类博文; 安卓玩机工具推荐----MTK芯片读写分区 备份分区 恢复分区 制作线刷包 工具操作解析 安卓玩机工具推荐----MTK芯片读写分区 备份分区 恢复分区 制作线刷包 工具操作解析【二】-CSDN博客 回顾以往 在以前的博文简单介绍了这款工具的rom制作全程。今天针对这款工具的…

Rust 02.控制、引用、切片Slice

1.控制流 //rust通过所有权机制来管理内存&#xff0c;编译器在编译就会根据所有权规则对内存的使用进行 //堆和栈 //编译的时候数据的类型大小是固定的&#xff0c;就是分配在栈上的 //编译的时候数据类型大小不固定&#xff0c;就是分配堆上的 fn main() {let x: i32 1;{le…

YoloV5改进策略:Neck和Head改进|ECA-Net:用于深度卷积神经网络的高效通道注意力|多种改进方法|附结构图

摘要 本文使用ECA-Net注意力机制加入到YoloV5Neck和Head中。我尝试了多种改进方法&#xff0c;并附上改进结果&#xff0c;方便大家了解改进后的效果&#xff0c;为论文改进提供思路。&#xff08;改进中。。。。&#xff09; 论文&#xff1a;《ECA-Net&#xff1a;用于深度…

Android中运动事件的处理

1.目录 目录 1.目录 2.前言 3.程序演示 4.第二种程序示例 5.扩展 2.前言 触摸屏&#xff08;TouchScreen&#xff09;和滚动球&#xff08;TrackBall&#xff09;是 Android 中除了键盘之外的主要输入设备。如果需要使用触摸屏和滚动球&#xff0c;主要可以通过使用运动事…

ruoyi-nbcio-plus基于vue3的flowable的流程条件的升级修改

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a…

JavaScript中的继承方式详解

Question JavaScript实现继承的方式&#xff1f; 包含原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合式继承和ES6 类继承 JavaScript实现继承的方式 在JavaScript中&#xff0c;实现继承的方式多种多样&#xff0c;每种方式都有其优势和适用场景。以下…

HarmonyOS(鸿蒙开发)入门篇

如果需要学习鸿蒙开发可以查看以下学习资源链接 OpenAtom OpenHarmony Develop applications - HUAWEI HarmonyOS APP 转载请注明出处HarmonyOS(鸿蒙开发&#xff09;入门篇-CSDN博客&#xff0c;谢谢&#xff01;

unity 数据的可视化

【Unity 实用插件篇】| 可视化图表插件XCharts (折线图、柱状图、饼图等)详细教学-腾讯云开发者社区-腾讯云 Package https://github.com/XCharts-Team/XCharts/releases 官方文档案例 入门教程&#xff1a;5分钟上手 XCharts 3.0 | XCharts (xcharts-team.github.io)

Linux 系统基础操作命令

当前市面上常见的系统&#xff1a;Windows、Linux、Mac OS、Android、IOS…… Linux 不太适合日常使用&#xff0c;但是非常适合用于开发。因此作为一个程序猿来说&#xff0c;Linux 都是务必要掌握的。 Linux 介绍 Linux 发行版 目前市面上比较知名的发行版有&#xff1a;R…

c(RGDfK)-FITC, 绿色荧光FITC标记细胞穿膜肽c(RGDfk

中文名称 &#xff1a;荧光标记c&#xff08;RGDfk&#xff09;环肽 英 文 名 &#xff1a;c(RGDfK)-FITC c(RGDfK(FITC)) 品 牌 &#xff1a;Tanshtech 单字母&#xff1a; c(RGD-DPhe-K&#xff08;Fitc&#xff09;) 三字母&#xff1a;Cyclo(Arg-Gly-Asp…

web学习笔记(四十六)

目录 1. path 路径模块 1.1 导入path模块 1.2 path.join()路径拼接 1.3 path.basename() 获取路径中的文件名 1.4 path.extname() 获取路径中的扩展名 2.服务器的相关概念 2.1 IP 地址 2.2 域名和域名服务器 2.3 端口号 3. http 模块 3.1使用http模块搭建服务器的步…

WIFI驱动移植实验:配置 Linux 内核

一. 简介 前面文章删除了Linux内核源码&#xff08;NXP官方的kernel内核源码&#xff09;自带的 WIFI驱动。 WIFI驱动移植实验&#xff1a;删除Linux内核自带的 RTL8192CU 驱动-CSDN博客 将正点原子提供的 rtl8188EUS驱动源码添加到 kernel内核源码中。文章如下&#xff1a…