Скачать файл, winsock recv () для записи в fstream, файл поврежден

0

Я пытаюсь загрузить файл с моего сайта с помощью winsock. я столкнулся с бесчисленными проблемами и теперь смог загрузить файл, но его испортил.

Он не работает с любым расширением файла. Текст и картинки в конечном итоге повреждены, аудиофайлы тоже. С бинарными файлами я вижу эту ошибку при выполнении "слишком большой программы, чтобы вписаться в память".

Сначала я посылаю() запрос главы на сервер, чтобы узнать размер содержимого (размер загружаемого файла), затем я отправляю запрос Get и я возвращаю в буфер. После выполнения recv я напишу файл.

Я попытался написать простой пример кода здесь, я пробовал различные петлевые подходы, но в конце у меня все еще есть поврежденный файл, записанный на диск. размер тот же (файл 50 КБ на сервере, файл 50 КБ, загруженный и записанный на диск). Спасибо вам всем.

headrequest = "HEAD " + "/folder/file.asd" + " HTTP/1.1\r\nHost: " + "url.com" + "\r\n\r\n";
getrequest = "GET " + "/folder/file.asd" + " HTTP/1.1\r\nHost: " + "url.com" + "\r\n\r\n";

send(socket, headrequest, sizeof(headrequest), 0);
recv(socket, reply_buf_headrequest, sizeof(reply_buf_headrequest), 0); 
//two functions to get the header end and "Content-Lenght" data from header

send(socket, getrequest, sizeof(getrequest), 0);
while(1)
{    
 recv(socket, recvbuff, sizeof(recvbuff), 0);
 if (recv(socket, recvbuff, sizeof(recvbuff), 0) == 0) 
  break; 
}
out.write(recvbuff, content_lenght); // also tried --> out.write(recvbuff + header_end, content_lenght) //same errors.
out.close();

Я прикручиваю буфер/позицию, чтобы начать чтение/запись или что-то в этом роде. Я думал, что использование recvbuff + header_end будет работать, так как он начнет чтение с конца заголовка, чтобы получить файл. Это смущает. Надеюсь, одна любезная душа поможет мне разобраться, как справиться с этой ситуацией и правильно записать байты файлов. :)

Редактировать:

Я думал, что я переписываю данные. черт. content_length поступает из предыдущего запроса HEAD, функция считывает полученные данные и находит значение "Content-Length", которое является размером в байтах /folder/file.asd. я не смог получить его в запросе Get, поэтому я сделал это так: размер файла, который он получает, верен.

так,

while(1)
{
  if (recv(socket, recvbuff, sizeof(recvbuff), 0) == 0)
   break;
}
out.write(recvbuff, content_lenght);
out.close();

out.write следует после цикла или внутри цикла while (1)?

Спасибо за быстрый ответ. :)

Я пропустил часть проверки ошибок, чтобы код примера был коротким, извините. голова и запрос на получение - это символы, я тоже пытался использовать строки и не использовал для этого sizeof(). я не могу получить доступ к реальному коду до завтра, поэтому я пытаюсь исправить его дома, используя похожий фрагмент. Возможно, есть некоторые опечатки.

Редактировать 2: как тест с небольшим exe, который только порождает ящик сообщений im, используя буфер больше, чем файл, и это:

ofstream out("test.exe", ios::binary);

и используя этот цикл сейчас:

    int res;   // return code to monitor transfer
do {    
    res = recv(socket, recvbuff, sizeof(recvbuff), 0);   // look at return code
    if (res > 0)  // if bytes received 
        out.write(recvbuff, res ); // write them  
} while (res>0);   // loop as long as we receive something  
if (res==SOCKET_ERROR)  
    cerr << "Error: " << WSAGetLastError() << endl; 

все еще имея ошибку "program too large to fit in memory" при исполнении.

  • 1
    Каков тип данных headrequest и getrequest? Если это std :: string или аналогичный класс строки, то sizeof (...) не возвращает длину строки.
  • 1
    Вы никогда не проверяете значения, возвращаемые send () или recv (), поэтому вы не знаете, сколько фактически было отправлено байтов или сколько байтов было фактически помещено в recvbuff. Это может быть меньше байтов, чем запрошенное вами число, в этом случае ваш буфер не будет заполнен полностью.
Показать ещё 4 комментария
Теги:
visual-c++
http
winsock

1 ответ

1

Это нормально! Ваш код действительно не заботится о содержимом, который вы получаете!

Смотрите мои комментарии:

while(1)  // Your original (indented) code commented: 
{    
    recv(socket, recvbuff, sizeof(recvbuff), 0);  // You read data in buffer 
    if (recv(socket, recvbuff, sizeof(recvbuff), 0) == 0)  // you read again, overwriting data you've received !! 
        break; 
}
out.write(recvbuff, content_lenght); // You only write the last thing you've received. 
                            // Where does the lengthe come from ?  Maybe you have buffer overflow as well.

Перепишите свой цикл следующим образом:

int res;   // return code to monitor transfer
do {    
    res = recv(socket, recvbuff, sizeof(recvbuff), 0);   // look at return code
    if (res > 0)  // if bytes received 
        out.write(recvbuff, res ); // write them  
} while (res>0);   // loop as long as we receive something  
if (res==SOCKET_ERROR)  
    cerr << "Error: " << WSAGetLastError() << endl; 

Преимущество состоит в том, что вам не нужно заботиться об общем размере, так как вы пишете каждый небольшой кусок, который вы получаете.

Редактировать:

После нашего обмена комментариями, здесь есть дополнительная информация. Как заметил кто-то, HTTP-протокол несколько сложнее управлять. См. Здесь, в главе 6, для получения дополнительной информации о формате ответа и заголовке, который вы должны пропустить.

Здесь некоторое обновленное доказательство концепции, чтобы пропустить заголовок:

ofstream out;
out.open(filename, ios::binary);
bool header_skipped=false;  // was header skiped (do it only once !!) 
int res;   // return code to monitor transfer
do {
    res = recv(mysocket, recvbuff, sizeof(recvbuff), 0);   // look at return code
    if (res > 0)     // if bytes received
    {
        size_t data_offset = 0;      // normally take data from begin of butter 
        if (!header_skipped) {    // if header was not skipped, look for its end
            char *eoh = "\r\n\r\n";
            auto it = search (recvbuff, recvbuff + res, eoh, eoh + 4); 
            if (it != recvbuff + res) {   // if header end found: 
                data_offset = it - recvbuff + 4;      // skip it
                header_skipped = true;              // and then do not care any longer
            }                             // because data can also containt \r\n\r\n
        }
        out.write(recvbuff + data_offset, res - data_offset); // write, ignoring before the offset
    }
} while (res > 0);   // loop as long as we receive something  
if (res == SOCKET_ERROR) cerr << "Error: " << WSAGetLastError() << endl;
out.close();

Внимание! Как сказано, это доказательство концепции. Это, вероятно, будет работать. Однако имейте в виду, что вы не можете быть уверены, как данные будут перегруппированы со стороны приемника. Вполне возможно, что конец заголовка разделяется между двумя последовательными чтениями (например, \r как последний байт одного recv() и \n\r\n качестве первых байтов следующего recv()). В таком случае этот простой код не найдет его. Так что это еще не код качества продукции. До вас, чтобы улучшить

  • 0
    попробовал цикл, который вы написали здесь. ошибки не возвращаются, файл загружается и записывается с точно таким же размером, но при выполнении он выдает «программа слишком большая, чтобы поместиться в памяти». Я пытаюсь загрузить небольшой exe-файл с всплывающим окном MessageBox для тестирования.
  • 0
    Хорошо ! Как отметил капитан Oblivious, вы должны убедиться, что файл был открыт как двоичный файл, чтобы избежать преобразования некоторых двоичных символов и возникновения проблем.
Показать ещё 12 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню