【问题标题】:How to read binary files from HTTP using C/C++ sockets如何使用 C/C++ 套接字从 HTTP 读取二进制文件
【发布时间】:2020-02-25 03:09:33
【问题描述】:

我正在编写 Http-Client,它在某个文件上获取 URL,下载它并将其保存在磁盘上。就像 curl 一样。 我只能将 C/C++ 与 std:: 和 libc 一起使用。我下载 XML、CSV 或 txt 等文本文件没有问题,因为它们按应有的方式保存,如果在编辑器中打开它们 - 没关系,有预期的文本。但是当我下载 tar 或 pdf 并尝试打开它们时,它会告诉我文件已损坏。

这是我的类 HttpClient 的 2 个主要方法。 HttpClient::get - 向主机发送 Http-request,在 URL 中提到,并调用第二个主要方法 - HttpClient::receive,它定义了什么样的数据 - 二进制或文本,并写入整个 Http-request 正文在使用二进制或文本模式的文件中。 我决定不展示所有其他方法,但如果有人需要,我可以展示。

HttpClient::get:

bool HttpClient::get() {
    std::string protocol = getProtocol();
    if (protocol != "http://") {
        std::cerr << "Don't support no HTTP protocol" << std::endl;
        return false;
    }
    std::string host_name = getHost();

    std::string request = "GET ";
    request += url + " HTTP/" + HTTP_VERSION + "\r\n";
    request += "Host: " + host_name + "\r\n";
    request += "Accept-Encoding: gzip\r\n";
    request += "Connection: close\r\n";
    request += "\r\n";

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        std::cerr << "Can't create socket" << std::endl;
        return false;
    }
    addr.sin_family = AF_INET;
    addr.sin_port = htons(HTTP_PORT);

    raw_host = gethostbyname(host_name.c_str());
    if (raw_host == NULL) {
        std::cerr << "No such host: " << host_name << std::endl;
        return false;
    }

    if (!this->connect()) {
        std::cerr << "Can't connect" << std::endl;
        return false;
    } else {
        std::cout << "Connection established" << std::endl;
    }

    if (!sendAll(request)) {
        std::cerr << "Error while sending HTTP request" << std::endl;
        return false;
    }

    if (!receive()) {
        std::cerr << "Error while receiving HTTP response" << std::endl;
        return false;
    }

    close(sock);
    return true;
}

HttpClient::receive:

bool HttpClient::receive() {
    char buf[BUF_SIZE];
    std::string response = "";
    std::ofstream file;
    FILE *fd = NULL;

    while (1) {
        size_t bytes_read = recv(sock, buf, BUF_SIZE - 1, 0);

        if (bytes_read < 0)
            return false;

        buf[bytes_read] = '\0';
        if (!file.is_open())
            std::cout << buf;

        if (!file.is_open()) {
            response += buf;
            std::string content = getHeader(response, "Content-Type");

            if (!content.empty()) {
                std::cout << "Content-Type: " << content << std::endl;
                if (content.find("text/") == std::string::npos) {
                    std::cout << "Binary mode" << std::endl;
                    file.open(filename, std::ios::binary);
                }
                else {
                    std::cout << "Text mode" << std::endl;
                    file.open(filename);
                }

                std::string::size_type start_file = response.find("\r\n\r\n");
                file << response.substr(start_file + 4);
            }
        }
        else
            file << buf;
        if (bytes_read == 0) {
            file.close();
            break;
        }
    }
    return true;
}

我找不到帮助,但我认为二进制数据是以某种方式编码的,但是如何解码呢?

【问题讨论】:

  • buf[bytes_read] = '\0'; -- 除非我弄错了你是如何读取文件的,如果文件是二进制文件,你为什么要人为地在数据中粘贴一个空值?这会破坏二进制数据。
  • response += buf 如果您的二进制数据中有 nul 字符(很可能是这种情况),也将不起作用。
  • 您的 receive() 未正确解析 HTTP 响应。它只是盲目地读取任意数据块,直到断开连接,然后尝试解析。您需要读取 HTTP 标头,直到到达终止 \r\n\r\n,然后解析标头以了解正文的传输格式,然后相应地读取正文。有关正确读取 HTTP 响应的伪代码,请参阅我对 Receiving only necessary data with C++ SocketWhen is an HTTP response finished? 的回答。

标签: c++ sockets http tcp


【解决方案1】:

我找不到帮助,但我认为二进制数据是以某种方式编码的,但是如何解码呢?

您没有解释为什么会这样想,但您请求中的以下行可能会导致您无法处理某些编码:

request += "Accept-Encoding: gzip\r\n";

在这里您明确表示您愿意接受使用 gzip 编码(压缩)的内容。但是查看您的代码,您甚至没有通过分析 Content-Encoding 标头来检查声明为 gzip 编码的内容。

除此之外,以下行也可能导致问题:

request += url + " HTTP/" + HTTP_VERSION + "\r\n";

您没有显示HTTP_VERSION 是什么,但假设它是1.1,您还必须处理Transfer-Encoding: chunked

【讨论】:

    【解决方案2】:

    谢谢大家。 我通过将response += buf; 更改为response.append(buf, bytes_read);file &lt;&lt; buf; 更改为file.write(buf, bytes_read); 解决了这个问题。 编写像空终止字符串这样的二进制数据是愚蠢的。

    【讨论】:

    • 即使有了这些修复,您的整体实现仍然由于其他原因是错误的,正如我在另一条评论中提到的那样
    猜你喜欢
    • 1970-01-01
    • 2011-07-26
    • 2016-10-21
    • 2011-02-03
    • 2021-07-16
    • 2017-10-01
    • 2011-09-03
    • 2016-01-15
    • 1970-01-01
    相关资源
    最近更新 更多