【问题标题】:File download with libssh SFTP is corrupt使用 libssh SFTP 下载的文件已损坏
【发布时间】:2021-09-17 13:49:48
【问题描述】:

我正在使用 libssh 从本地 Windows 机器上的 Debian Linux 服务器下载文件,程序运行没有错误,但下载的文件已损坏。当我通过pscp 下载时,它可以工作,我不知道我是否在缓冲区或其他方面做错了。

生成文件的大小是相同的,当我通过 FileZilla 或 WinSCP 等其他工具下载时,一切正常。该文件不再损坏。当我运行我的代码时,它会损坏

我正在使用 Visual Studio 2019 在 Windows 10 上运行此代码。在多次弄乱此代码之后,我认为问题出在 write 文件写入功能上,当我在十六进制编辑器中打开生成的文件时,我看到了差异仅以字节为单位的文件末尾保持不变。

我之前最终发布了 Dart 版本的代码,但 C 版本与 Dart 版本几乎相同。

#define _CRT_SECURE_NO_WARNINGS
#define _CRT_NONSTDC_NO_DEPRECATE
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <stdexcept>
#include <string.h>
#include <exception>
#include <fstream>
#include <libssh/callbacks.h>
#include <libssh/libssh.h>
#include <libssh/sftp.h>
#include "custom_exception.cpp"
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <io.h>
#include <time.h>
#include <chrono>

using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::duration;
using std::chrono::milliseconds;

//using namespace std;
using std::string;
using std::cout;
using std::endl;
using std::cin;


// Good chunk size
#define MAX_XFER_BUF_SIZE 16384
int sftpDownloadFileTo(ssh_session session, const char* fullRemotePath, const char* fullLocalPath)
{
     
    int access_type;
    sftp_file file;
    char buffer[MAX_XFER_BUF_SIZE * 8];//128KB
    ssize_t nbytes, nwritten, rc;
    int fd;
    access_type = O_RDONLY;

     sftp_session sftp = sftp_new(session);
    if (sftp == NULL)
    {
        fprintf(stderr, "Error allocating SFTP session: %s\n",
            ssh_get_error(session));
        return SSH_ERROR;
    }
    rc = sftp_init(sftp);
    if (rc != SSH_OK)
    {
        fprintf(stderr, "Error initializing SFTP session: %d.\n",
            sftp_get_error(sftp));
        sftp_free(sftp);
        return rc;
    }
        

    file = sftp_open(sftp, fullRemotePath, access_type, 0);

    if (file == NULL) {
        fprintf(stderr, "Can't open file for reading: %s\n", ssh_get_error(session));
        return SSH_ERROR;
    }

    fd = open(fullLocalPath, O_CREAT | O_RDWR, 0777);
    if (fd < 0) {
        fprintf(stderr, "Can't open file for writing: %s\n",
            strerror(errno));
        return SSH_ERROR;
    }

    for (;;) {
        nbytes = sftp_read(file, buffer, sizeof(buffer));
        if (nbytes == 0) {
            break; // EOF
        }
        else if (nbytes < 0) {
            fprintf(stderr, "Error while reading file: %s\n",
                ssh_get_error(session));
            sftp_close(file);
            return SSH_ERROR;
        }
        nwritten = write(fd, buffer, nbytes);
        if (nwritten != nbytes) {
            fprintf(stderr, "Error writing: %s\n",
                strerror(errno));
            sftp_close(file);
            return SSH_ERROR;
        }
    }
    rc = sftp_close(file);
    if (rc != SSH_OK) {
        fprintf(stderr, "Can't close the read file: %s\n",
            ssh_get_error(session));
        return rc;
    }

    sftp_free(sftp);
    return SSH_OK;
}

int main()
{

    ssh_session my_ssh_session;
    int rc;
    int port = 22;
    string password = "Ins257257";
    auto host = "192.168.133.13";
    auto username = "isaque.neves";

    int verbosity = SSH_LOG_PROTOCOL;
    // Abra a sessão e defina as opções
    my_ssh_session = ssh_new();
    if (my_ssh_session == NULL)
        exit(-1);
    ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, host);
    //ssh_options_set(my_ssh_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
    ssh_options_set(my_ssh_session, SSH_OPTIONS_PORT, &port);
    // Conecte-se ao servidor
    rc = ssh_connect(my_ssh_session);
    if (rc != SSH_OK)
    {
        
        fprintf(stderr, "Error connecting to host: %s\n",
            ssh_get_error(my_ssh_session));
        exit(-1);
        
    }
    // Autenticar-se

    rc = ssh_userauth_password(my_ssh_session, username, password.c_str());
    if (rc != SSH_AUTH_SUCCESS)
    {
        fprintf(stderr, "Error authenticating with password: %s\n",
            ssh_get_error(my_ssh_session));
        ssh_disconnect(my_ssh_session);
        ssh_free(my_ssh_session);
        exit(-1);
    }

    clock_t tic = clock();
    auto t1 = high_resolution_clock::now();
    sftpDownloadFileTo(my_ssh_session, "/home/isaque.neves/go1.11.4.linux-amd64.tar.gz", "D:/MyDartProjects/fsbackup/libssh_binding/go1.11.4.linux-amd64.tar.gz");
    auto t2 = high_resolution_clock::now();
    clock_t toc = clock();
    
    duration<double, std::milli> ms_double = t2 - t1;   
    std::cout << ms_double.count() << " ms\r\n";

    printf("Elapsed: %f seconds\n", (double)(toc - tic) / CLOCKS_PER_SEC);

    ssh_disconnect(my_ssh_session);
    ssh_free(my_ssh_session);

    std::cout << "End\n";

    return 0;
}

https://github.com/insinfo/fsbackup/tree/main/libssh_binding

【问题讨论】:

    标签: c++ c sftp libssh


    【解决方案1】:
     fd = open(fullLocalPath, O_CREAT | O_RDWR, 0777);
    

    这是你的问题。由于您在 Windows 上运行,因此我们需要在此处进行调整。

     fd = open(fullLocalPath, O_CREAT | O_RDWR | O_BINARY, 0777);
    

    否则 write() 将尝试调整行尾。

    【讨论】:

    • 非常感谢您通过添加 O_BINARY 完美运行的答案,关于传输速度仍然很糟糕传输 120MB 文件需要大约 12 秒,在 filezilla 中需要 3 秒
    • @IsaqueNeves:libssh 的名声很差。我不使用它。我转而使用 openssh 或 putty。
    • Martin Prikryl 是的,我提出了一个关于速度的单独问题。 @Joshua 你有 c 语言中的 openssh 示例吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-09
    • 2019-03-24
    • 1970-01-01
    • 1970-01-01
    • 2020-08-29
    • 1970-01-01
    相关资源
    最近更新 更多