C ++ HTTP Client recv () и проблема записи файла - PullRequest
1 голос
/ 10 января 2020

Я написал HTTP-клиент с c ++ для загрузки файлов в фоновом режиме, и он работает усердно, но он искажается, когда я загружаю pdf, который обычно не поврежден.

Существуют такие библиотеки, как libcurl poco для этого. Но так как я хотел увидеть основы, я написал чистый код сокета. Я посмотрел на исходный код библиотек (например, curl), но ничего из них не понял.

file.pdf адрес: https://www.axmag.com/download/pdfurl-guide.pdf

Это мой код:

#include <iostream>
#include <stdio.h>
#include <WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
void main()
{
setlocale(LC_ALL,
    "Turkish");




WSADATA wsdata;
WORD ver = MAKEWORD(2, 2);


int ws0k = WSAStartup(ver, &wsdata);

if (ws0k != 0) {
    return;
}// classic startup operations


SOCKET client = socket(AF_INET, SOCK_STREAM, 0);

if (client == INVALID_SOCKET) {
    return;
}
else {
    cout << "Soket oluşturuldu." << endl; // successfully created socket
}



struct hostent* host;
host = gethostbyname("www.axmag.com");


sockaddr_in target;
target.sin_addr.S_un.S_addr = *((unsigned long*)host->h_addr); //WEBSITE ADDRESS
target.sin_port = htons(80); // HTTP PORT : 80
target.sin_family = AF_INET; // I don't know the address family of http.

int conn = connect(client, (sockaddr*)&target, sizeof(target));

if (conn < 0) {
    return;
}
else {
    cout << "Bağlantı isteği başarılı." << endl; // successfully connected.
}


char HTTP_MESSAGE[] = "GET /download/pdfurl-guide.pdf HTTP/1.0\r\nHost: www.axmag.com\r\nConnection: close\r\n\r\n";


if (send(client, HTTP_MESSAGE, sizeof(HTTP_MESSAGE), 0) < 0) { // send http request.
    return;
}
else {
    cout << "Komut başarıyla gönderildi." << endl; // Successfully send http request.
}


FILE* file = NULL; // file

file = fopen("C:\\Users\\user\\Desktop\\test.pdf", "wb"); //Create file

if (file == NULL) {
    return;
}
else {
    cout << "Dosya oluşturuldu." << endl; //Successfully created file.
}

char recvbuf[1024];


int received;
while (received = recv(client, recvbuf, sizeof(recvbuf), 0) > 0) { // RECV
    cout << recvbuf << endl;
    fwrite(recvbuf, 1, received, file); // write data to file

}



fclose(file);
closesocket(client);
WSACleanup();

system("pause");
}   

Я успешно загружаю его. Проблема была с функцией fwrite. Я изменил параметр ElementSize на значение буфера, и это произошло.

Код решения:

char recvbuf[64];

int received;

while (received = recv(client, recvbuf, sizeof(recvbuf), 0) > 0) { // RECV
    cout << recvbuf << endl;
    fwrite(recvbuf, 64, received, file); // write data to file
}

Ответы [ 2 ]

1 голос
/ 11 января 2020

Если проблема в том, что приоритет оператора для > выше, чем для =, поэтому ваша переменная received получит либо 0, либо 1. Вы можете использовать дополнительный набор скобок, чтобы исправить это:

while ((received = recv(client, recvbuf, sizeof(recvbuf), 0)) > 0) {
...

Когда веб-сервер отправляет свой ответ, ответ будет содержать заголовок ответа HTTP. Вам нужно прочитать этот заголовок ответа и оставить его вне файла.

Упрощенно,

// Skip the HTTP header
std::string http_resp;
auto eoh = std::string::npos;
while ((received = recv(client, recvbuf, sizeof(recvbuf), 0)) > 0) {
    http_resp.append(recvbuf, received);
    auto eoh = http_resp.find("\r\n\r\n");
    if (eoh != std::string::npos) {
        if ((eoh + 4) < http_resp.size()) {
            auto rest = http_resp.substr(eoh + 4);
            fwrite(rest.c_str(), 1, rest.size(), file);
        }
        break;
    }
}

// Get the rest of the file
if (received > 0) {
    while ((received = recv(client, recvbuf, sizeof(recvbuf), 0)) > 0) {
        fwrite(recvbuf, 1 ,received, file);
    }
}
0 голосов
/ 10 января 2020

Ваша программа получает до 1024 байтов за раз. Каждый раз, когда запускается l oop:

while (received = recv(client, recvbuf, sizeof(recvbuf), 0) > 0) { // RECV
    cout << "Veriler alınıyor.." << endl;
}

ваша программа вызывает recv и получает до 1024 байтов.

Как только больше не осталось данных, ваша программа записывает последние кусок данных.

Вам нужно записать все порции данных, вызвав fwrite в l oop.

...