如何使用C / C ++套接字从HTTP读取二进制文件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用C / C ++套接字从HTTP读取二进制文件相关的知识,希望对你有一定的参考价值。

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

这里是我的类HttpClient的2个主要方法。 HttpClient :: get-将Http请求发送到主机(URL中提到),并调用第二个主要方法-HttpClient :: receive,它定义存在的数据类型-二进制或文本,并编写整个Http请求正文在使用二进制或文本模式的文件中。我决定不显示所有其他方法,但是如果有人需要,我可以。

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 + "
";
    request += "Host: " + host_name + "
";
    request += "Accept-Encoding: gzip
";
    request += "Connection: close
";
    request += "
";

    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] = '';
        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("

");
                file << response.substr(start_file + 4);
            }
        }
        else
            file << buf;
        if (bytes_read == 0) {
            file.close();
            break;
        }
    }
    return true;
}

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

答案

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

另一答案

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

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

request += "Accept-Encoding: gzip
";

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

此外,以下行也可能引起问题:

request += url + " HTTP/" + HTTP_VERSION + "
";

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

以上是关于如何使用C / C ++套接字从HTTP读取二进制文件的主要内容,如果未能解决你的问题,请参考以下文章

C# Begin/EndReceive - 我如何读取大数据?

如何在 C++/QT 中使用 TCP 服务器套接字创建 Http 服务器

C错误从流式套接字读取数据包

C:你如何通过套接字读取和解包消息?

C++ read()-ing 从一个套接字到一个 ofstream

如何通过 C 中的 HTTP POST 请求发送图像或二进制数据