通过 libcurl 以二进制形式传输的文件已损坏

Posted

技术标签:

【中文标题】通过 libcurl 以二进制形式传输的文件已损坏【英文标题】:File transferred as binary via libcurl is corrupted 【发布时间】:2019-11-13 13:34:32 【问题描述】:

还有许多其他线程在谈论二进制文件损坏,但它们似乎都与我的问题无关。

我有一个下载/读取文件的 C 程序。

因为我不会将我得到的所有文件都写入一个文件,所以我与 curl 一起使用的函数宁愿将数据存储到一个字符串中。以后我可以随意将这个字符串写入文件,也可以不写入。

我有一个二进制文件。 我把它放在 FTP 上。

如果我通过像 filezilla 这样的 ftp 客户端下载它,它会包含正确的内容(也就是说,我在编译编译的二进制文件时得到的字符相同) 如果我使用 curl 命令行下载文件,它也包含正确的内容。

如果我使用我的程序下载这样的文件,它只会包含一个类似“ELF”的字符串,后跟 3 个不可写/不可读的字符。

请务必注意,这只发生在二进制文件中。文本文件被传输/只读文件。 同样重要的是要知道从 curl 传递给我的函数的数据似乎已经是错误的:如果我将数据的 printf 放在我的 write 函数中,我会看到相同的 ELF + 3 unreadable chars 字符串,因此我稍后将其写入文件的方法没有问题。

当我使用详细时,curl 说它处于二进制模式但二进制文件没有正确传输。

这是我目前所拥有的,适用于任何非二进制文件,否则将永远是垃圾。提前致谢:

struct string 

  char *ptr;
  size_t len;
;

char *usr_psswd(char *user, char *psswd)

    char *usrpsswd;

    usrpsswd = (char *)malloc(strlen(user) + strlen(psswd) + 2);
    int i = 0;
    int j = 0;

    while (user[i])
    
        usrpsswd[i] = user[i];
        ++i;
    
    usrpsswd[i++] = ':';
    while (psswd[j])
    
        usrpsswd[i] = psswd[j];
        ++i;
        ++j;
    
    usrpsswd[i] = 0;
    return usrpsswd;


void init_string(struct string *s) 

  s->len = 0;
  s->ptr = malloc(s->len+1);
  if (s->ptr == NULL) 
  
    fprintf(stderr, "malloc() failed\n");
    exit(EXIT_FAILURE);
  
  s->ptr[0] = '\0';


size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s)

    size_t new_len = s->len + size*nmemb;
    s->ptr = realloc(s->ptr, new_len+1);
    if (s->ptr == NULL) 
    
        fprintf(stderr, "realloc() failed\n");
        exit(EXIT_FAILURE);
    
    memcpy(s->ptr+s->len, ptr, size*nmemb);
    s->ptr[new_len] = '\0';
    s->len = new_len;
    return size*nmemb;


char *curl_get(char *addr, t_data *data)

  CURL *curl;
  CURLcode res;
  char *rtrn;
  curl = curl_easy_init();
  if(curl) 
  
    struct string s;
    init_string(&s);
    curl_easy_setopt(curl, CURLOPT_URL, addr);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
    curl_easy_setopt(curl, CURLOPT_PORT, 21);
    curl_easy_setopt(curl, CURLOPT_USERPWD, usr_psswd(data->login, data->password));
    res = curl_easy_perform(curl);
    if(res != CURLE_OK)
    
        printf("curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        free(s.ptr);
        curl_easy_cleanup(curl);
        return NULL;
    
    rtrn = strdup(s.ptr);
    free(s.ptr);
    curl_easy_cleanup(curl);
  
  return rtrn;

【问题讨论】:

【参考方案1】:

您的问题是您将二进制数据视为字符串。

strdup 函数与任何其他字符串函数一样工作:它查找字符串终止符以找到源字符串的结尾。而字符串终止符'\0' 是字节值0。因此,如果二进制数据包含任何零字节(很可能),那么这将被视为“字符串”的结尾。

简单的解决方案?只需执行return s.ptr; 但请注意,无法使用返回的指针找出数据的长度。因此,更好的解决方案可能是返回 s 本身(因为它包含指向数据的指针以及数据的大小)。

【讨论】:

感谢您的关注,但是,正如我所说,这发生在 strdup 之前:当我在 write 函数中显示 curl 传递的指针时,它已经是“elf+unreadable chars” @Tohkai 你如何打印数据?使用printf%s 格式?然后,对于可以在自身内部任何位置包含零字节的二进制数据,您也会遇到同样的问题,printf 会认为它是字符串终止符。您只是不能将任意二进制数据视为字符串,根本 但事情是这样的:一开始我也认为这是问题所在,但最初的二进制文件在它的开头不包含任何 ELF+不可读的字符,所以我什至不认为它停在 0 处,因为这些初始的第一个字符首先是无处可寻的 编辑:nvm 确实如此,我一定打开了错误的文件 好吧...我还认为 printf 不会在 '\0' 处停止,因为在过去,当我想通过在要剪切的位置放置 0 来剪切字符串时,有时会无论如何都要显示整个字符串。 "我还认为 printf 不会在 '\0' 处停止,因为在过去,当我想通过在要剪切的位置放置 0 来剪切字符串时,有时会无论如何都要显示整个字符串”:我不相信你。【参考方案2】:

您看到的大多数问题是由于使用了为处理字符串而设计的技术,但它们正在应用于二进制文件。

在编写必须在某些时候与二进制数据和文件内容一起使用的代码时,最好遵循几条规则

1) 用于包含二进制数据的变量应该首选unsigned char 而不是char。例如:

char *usr_psswd(char *user, char *psswd)...  

应该写成

unsigned char *usr_psswd(unsigned char *user, size_t lenUser, unsigned char *psswd, size_t lenPsswd)...  

注意:包含数组长度的原因如下所述。

More on the rational of using unsigned char with binary data

2) 避免使用strdup()strlen()等字符串函数。它们都是为了寻找终止空字节来指示C string的结束。例如:>

usrpsswd = (char *)malloc(strlen(user) + strlen(psswd) + 2);

应该写成:

 usrpsswd = malloc(lenUser + lenPasswd + 1);//No need for null terminator. (+1 for delimiter, per comments)
                                        //usrpasswrd should be unsigned char *
                                        //Casting return of malloc not recommended. in C.  

More on reliable ways to get array lengths in C.

【讨论】:

usr_psswd 也包含一个分隔符。所以肯定必须至少有 + 1 才能分配给 ':' @Tohkai - 这是有道理的。在 malloc 语句中添加了 +1。谢谢。

以上是关于通过 libcurl 以二进制形式传输的文件已损坏的主要内容,如果未能解决你的问题,请参考以下文章

libcurl.dll的介绍

计算机libcurl.dll文件丢失怎么解决

每次电脑现出 libcurl.dll丢示,咋办

2.1 LibCurl编程流程(转)

Curl常用函数介绍

blob 上的错误内容类型无法打开 img / 文件已损坏