Как читать двоичные файлы из HTTP, используя сокеты C / C ++ - PullRequest
0 голосов
/ 29 октября 2019

Я пишу Http-клиент, который берет URL-адрес в некотором файле, загружает его и сохраняет на диске. Как и завиток. Я могу использовать только C / C ++ с std :: и libc. У меня нет проблем с загрузкой текстовых файлов, таких как XML, CSV или TXT, потому что они были сохранены, как и должно быть, и если открыть их в редакторе - все в порядке, есть тот текст, который ожидался. Но когда я загружаю tar или pdf и пытаюсь открыть их, он говорит, что файлы повреждены.

Вот 2 основных метода моего класса HttpClient. HttpClient :: get - отправляет Http-запрос на хост, который упоминается в URL, и вызывает 2-й основной метод - 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 + "\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;
}

Я не могу найти помощь, но я думаю, что двоичные данные каким-то образом кодируются, но как их декодировать?

Ответы [ 2 ]

1 голос
/ 30 октября 2019

Спасибо всем. Я решил эту проблему, изменив response += buf; на response.append(buf, bytes_read); и file << buf; на file.write(buf, bytes_read);. Было глупо записывать двоичные данные, такие как строка с нулевым символом в конце.

0 голосов
/ 29 октября 2019

Я не могу найти помощь, но я думаю, что двоичные данные каким-то образом кодируются, но как их декодировать?

Вы не объясняете, почему вы так думаетено следующая строка из вашего запроса может вызвать некоторую кодировку, которую вы не обрабатываете:

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

Здесь вы прямо говорите, что готовы принять контент, закодированный (сжатый) с помощью gzip. Но, глядя на ваш код, вы даже не проверяете, объявлено ли содержимое как закодированное с помощью gzip, путем анализа заголовка Content-Encoding.

Кроме этого, следующая строка может также вызвать проблему:

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

Вы не показываете, что такое HTTP_VERSION, но предполагая, что это 1.1, вам также придется иметь дело с Transfer-Encoding: chunked.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...