Problem bei Dateidownload ueber simplen HTTP GET Request
-
mit deinem recv aufruf erhälst du maximal 512 bytes pro while-durchlauf. deinen puffer adressierst du jedoch mit einem offset von 1024 bytes, damit bekommst du lücken in deinen daten, die mit nicht-initialisiertem datenmüll bedingt durch malloc gefüllt sind. wenn du alles in einen ram-puffer schreiben willst, musst du deinen puffer-offset also immer um die erhaltene anzahl bytes inkrementieren, die nicht immer 512 sein müssen.
das hab ich ja in meinem letzte Post schon gesagt dass das nur zu Testzwecken war, aber auch mit der Initialisierung =0 funktioniert es nicht.
lästige pufferung kannst du umgehen, indem du die erhaltenen daten direkt in eine datei schreibst.
das is allerdings eine idee...
die daten einer 'binärdatei' mit cout anzeigen zu lassen, dürfte spätestens bei der ersten enthaltenen 0 enden.
das heisst ich soll das file einfach ohne "fstream::binary" oeffnen?!
bevor du daten irgendwo hin kopierst, solltest du den rückgabewert von recv prüfen, der kann und wird nämlich irgendwann 0 oder -1 werden, im letzteren fall zerfetzt dir memcpy den heap weil size_t unsigned ist.
ich ueberpruefe den Wert doch in der while schleife
while (readBytes = recv(sDownload, buffer, 512, 0)) {
sobald recv da 0 oder -1 zureuck gibt verlaesst er die while schleife oder???
also ich hab das ganze jetzt mal angepasst:
//read the response char *response = (char*)malloc(1024*1024); char buffer[512]; size_t readBytes = 0; size_t readCounter = 0; memset(response, 0, 1024*1024); memset(buffer, 0, sizeof(buffer)); ofstream downloadedFile; char fileOnDisk[512] = "D:\\"; strcat(fileOnDisk, "test.txt"); downloadedFile.open(fileOnDisk, fstream::app); while (readBytes = recv(sDownload, buffer, 512, 0)) { memcpy(response+readCounter, buffer, readBytes); downloadedFile << buffer; readCounter += readBytes; memset(buffer, 0, 512); cout << "copy data... " << readCounter << " Bytes copied... " << readBytes << " bytes read\n"; } downloadedFile.close();
ergebniss ist eine 20kb große datei... die exe die gezogen wird ist allerdings 476kb
-
sobald recv da 0 oder -1 zureuck gibt verlaesst er die while schleife oder???
Nein, nicht bei -1, nur bei 0.
-
Hmm, stimmt... hätte mir auch auffallen sollen ^^
Eine kleine Gegenfrage, die '\0' Terminierung gilt auch in C++? Woher weiß denn dein:
downloadedFile << response;
wielange response eingelesen werden darf?
Müsste dazu nicht iwie auch ein Ende ersichtlich sein. Vielleicht auch ein Ende wie '\0' sein? Und in Binaries kommen ja 00 Hex-Folgen gerne mal vor.
Wäre das nicht sinnvoller mit so einem read(datei, puffer, length) (<--Pseudofunktion) zu machen?
-
DaRe schrieb:
Hmm, stimmt... hätte mir auch auffallen sollen ^^
Eine kleine Gegenfrage, die '\0' Terminierung gilt auch in C++? Woher weiß denn dein:
downloadedFile << response;
wielange response eingelesen werden darf?
Müsste dazu nicht iwie auch ein Ende ersichtlich sein. Vielleicht auch ein Ende wie '\0' sein? Und in Binaries kommen ja 00 Hex-Folgen gerne mal vor.
Wäre das nicht sinnvoller mit so einem read(datei, puffer, length) (<--Pseudofunktion) zu machen?
Da hast du recht.
OP müsste
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
benutzen
-
bobbobbinson schrieb:
die daten einer 'binärdatei' mit cout anzeigen zu lassen, dürfte spätestens bei der ersten enthaltenen 0 enden.
das heisst ich soll das file einfach ohne "fstream::binary" oeffnen?!
neineinein, binär in eine datei schreiben ist völlig richtig, die aussage bezog sich auf das anzeigen in der console. ob dafür allerdings der << operator geegnet ist, dafür sind meine c++ kentnisse schon zu verstaubt.
bobbobbinson schrieb:
... was mir zu schaffen macht ist dass wenn ich eine binary Datei runterladen will ich nur den Header + ein paar Zeichen bekommen, wenn ich aber ein 4kb großes Textfile runterlade gibt es keine Probleme.
du weißt, das alles was nach dem header folgt, also alles nach \r\n\r\n nutzdaten sind?
das erklärt allerdings immer noch nicht den größenunterschied von rund 460kb, ich tippe mal auf den << operator - woher soll der wissen, wieviele bytes zu schreiben sind? die while bedingung musst du jedenfalls noch ändern.
-
das memset(buffer, 0, 512); kannst du auch rausschmeißen, daber darauf wurdest du ja schon hingewiesen.
-
Danke erst mal fuer die zahlreichen Posts...
Hab soweit mal alles angepasst, whileschleifen-unterbrechung, in file schreiben mit fwrite...
FILE *downloadedFile; char fileOnDisk[512] = "D:\\"; strcat(fileOnDisk, "test.txt"); remove(fileOnDisk); downloadedFile = fopen(fileOnDisk, "ab+"); while (readBytes = recv(sDownload, buffer, sizeof(buffer), 0)) { if (readBytes == -1) break; fwrite(buffer, 1, readBytes, downloadedFile); }
trotzdem gibts noch Probleme... wenn ich jetzt den Header aus dem File per Hand rausloesche stimmt die Dateigroesse auch perfekt, wenn ich das ganze dann aber ausfuehren will kommt nur kurz ein Konsolenfenster welches sich nach einer geschaetzen halben Sekunde wieder schliesst. In dem Fenster steht "Program too big to fit in memory".
Irgendwelche Ideen? ^^
(edit: hab gerade die exe mit dem texteditor geoeffnet, alles kopiert, in ein neues textfile eingefuegt, zu .exe umbenannt und siehe da.. der gleiche Fehler.. also reicht das bloße Kopieren des Inhalt nicht aus. Weiss jmd mehr?!)
-
Hab mich selber gerade mal ein wenig schlau auf Google gemacht, aber so eindeutig scheint der Fehler nicht zu sein.
Das was ich jetzt rausbekommen habe könnte iwie Problem
* bei 32 / 64 Bit Architektur sein.
* mit physikalischen Speicher oder sowasMeine Frage, die exe ist für dein System korrekt kompiliert... oder ist das Programm auf einem "64 Bit Rechner" erstellt/compiliert worden und wird nun auf einem "32 Bit Rechner" ausgeführt?
Ansonsten kann ich da so spontan nicht weiter helfen. Unter Ubuntu hat ich das Problem eben noch nicht
edit: MOOOOOOOOOOMENT
Hab gerade nochmal deinen Code überflogen, was macht da:remove(fileOnDisk);
Ist das zum freigeben von Speicher? Das sollte dir aber vermutlich auch einen Fehler ausgeben, da der Speicher nicht allokiert wurde und dementsprechend nicht in ieiner "Liste" für dynamische Speicherverwaltung auftaucht zum möglichen freigeben auftaucht(Ist jetzt ein wenig flappsig formuliert).
Kann mich jetzt auch irren, da ich remove() spontan mit free() verknüpft habe.edit edit: Zumal es bei einem freigeben an der Stelle auch ein logischer Fehler wäre, da du eine Zeile später noch auf "befreite" Variable zugreifen möchtest.
Wenn das zur Speicherfreigabe dient, dann entferne die ganze Zeile!
(edit)^3 : Weil ich gerade so unkonzentriert bin:
hier noch ein Tipp
while (readBytes = recv(sDownload, buffer, sizeof(buffer), 0)) { if (readBytes == -1) break; fwrite(buffer, 1, readBytes, downloadedFile); }
Sieht so besser aus:
while ((readBytes = recv(sDownload, buffer, sizeof(buffer), 0))>0) fwrite(buffer, 1, readBytes, downloadedFile);
BTW: Nicht auf die Idee kommen readBytes "unsigned" zu machen
-
Meine Frage, die exe ist für dein System korrekt kompiliert... oder ist das Programm auf einem "64 Bit Rechner" erstellt/compiliert worden und wird nun auf einem "32 Bit Rechner" ausgeführt?
Nein die Exe wurde auf meinem Rechner (32bit) kompeliert, ist ein stink normales hello world programm. Mehr macht das nicht, wenn ich das File aber auf den Server hochlade, dann mit meinem Programm es runterlade und den Header per Hand entferne so dass auf das byte genau das selbe drin steht (zumindest wenn ich es grob miteinandern vergleiche (anfang, ende)) dann laesst sich die exe nicht ausführen. Wie gesagt selbst wenn ich meine ausführbare Exe in ein Textfile ändere, den Inhalt in ein neues Textfile kopiere und sie in eine Exe umbenenne kommt der gleiche Fehler.
Ist das zum freigeben von Speicher? Das sollte dir aber vermutlich auch einen Fehler ausgeben, da der Speicher nicht allokiert wurde und dementsprechend nicht in ieiner "Liste" für dynamische Speicherverwaltung auftaucht zum möglichen freigeben auftaucht(Ist jetzt ein wenig flappsig formuliert).
Nein dass loescht nur das File auf meiner Festplatte, damit ich sicher sein kann dass das neue File das da hingespeichert wird "neu" ist... falls das File nicht vorhanden ist, passiert eben gar nichts.
http://www.cplusplus.com/reference/clibrary/cstdio/remove/Solangsam weiss ich nicht mehr weiter, auch meine Googlesuchanfrage-Möglichkeiten gehen zur Neige...
edit:
was mir gerade noch aufgefallen ist ist dass selbst wenn ich das funktionierende exe-File mit dem notepad öffne, ganz am schluss ein leerzeichen einfüge es wieder lösche und speichere (das file ist im grunde wie vorher) dann kommt der selbe fehler "program is too big to fit in memory"... crazyswayze!!
-
bobbobbinson schrieb:
(edit: hab gerade die exe mit dem texteditor geoeffnet, alles kopiert, in ein neues textfile eingefuegt, zu .exe umbenannt und siehe da.. der gleiche Fehler.. also reicht das bloße Kopieren des Inhalt nicht aus. Weiss jmd mehr?!)
Hä?
Dein Text-Editor ist halt nicht binär-safe. D.h. du machst die .exe damit kaputt. Deswegen geht sie dann nimmer. Alles logisch, welcher Teil ist jetzt nicht klar?
Schreib dein Programm so um, dass es die Header nicht mit abspeichert (oder von mir aus in ein anderes File speichert).
-
File so öffnen:
downloadedFile = fopen(fileOnDisk, "wb");
Dann wird ein evtl. schon existierendes zuerst geleert.
---
Wenn du eine Binärdatei mit irgendeinem Texteditor öffnest und änderst kannst du nicht sicher sein, daß es danach noch ein funktionierende .exe ist.
Nimm einen Hexeditor.Korrekterweise müsstest du zuerst das Headerende suchen und erst dann anfangen zu schreiben. Das ist aber auch nicht so einfach.
Wenn der Header z.B. 514 bytes (mit "\r\n\r\n") groß ist, findest du das wirkliche Ende nie in deinem buffer. Du müsstest also den ganzen buffer solange an einen anderen anhängen bis du dort das Ende findest, usw. usf.
-
Wenn du eine Binärdatei mit irgendeinem Texteditor öffnest und änderst kannst du nicht sicher sein, daß es danach noch ein funktionierende .exe ist.
Nimm einen Hexeditor.Ok, klingt logisch, wusst ich nicht...
Also mal fix den Quellcode geaendert, so dass der Header erst gar nicht mit in das File rueber kopiert wird. (zwar nicht so safe dass, falls das Ende des Headers direkt zwischen zwei recieves liegt, aber habe aus sicherheit mal den buffer auf 1024 byte erhöht, aber speziell bei diesem file weiss ich dass die nutzdaten im response bei ungefähr 505 Bytes anfangen)
Problem ist beim ersten Auslesen, also da wo der Header noch mit drin ist, stehen immer nur 3 byte Nutzdaten am Ende, egal wie groß ich den Buffer in den ausgelesen wird mache. Wenn ich alles (inklusive Header) in das File kopiere sind die Nutzdaten korrekt, will ich aber dass der header unbeachtet bleibt, schreibt er zwar die ersten Nutzdatenbytes in das File aber ein paar danach stehen nicht mit drin, eben dieses "can not run in DOS mode".
Wär super wenn mir jmd meinen hoffentlich letzten Fehler erklaeren könnte
//read the response char buffer[1024]; size_t readBytes = 0; memset(buffer, 0, sizeof(buffer)); FILE *downloadedFile; char fileOnDisk[512] = "D:\\"; strcat(fileOnDisk, "test.exe"); // hier muss spaeter filerequest benutzt werden = helloworld.exe remove(fileOnDisk); downloadedFile = fopen(fileOnDisk, "wb"); bool isBody = false; while ((readBytes = recv(sDownload, buffer, sizeof(buffer), 0)) > 0) { if (isBody) { fwrite(buffer, 1, readBytes, downloadedFile); } if (strstr(buffer, "\r\n\r\n") && !isBody) { char *tmp = strstr(buffer, "\r\n\r\n"); isBody = true; fwrite(tmp+4, 1, strlen(tmp+4), downloadedFile); } }
Gruß bob
-
fwrite(tmp+4, 1, strlen(tmp+4), downloadedFile);
recv liefert keinen '\0' am Ende des Strings... strlen sucht aber nach '\0':
while ((readBytes = recv(sDownload, buffer, sizeof(buffer), 0)) > 0) { buffer[readBytes]='\0'; // <-----Notwendig if (isBody) { fwrite(buffer, 1, readBytes, downloadedFile); } if (strstr(buffer, "\r\n\r\n") && !isBody) { char *tmp = strstr(buffer, "\r\n\r\n"); isBody = true; fwrite(tmp+4, 1, strlen(tmp+4), downloadedFile); } }
Auch möglich durch subtrahieren von Adressen:
while ((readBytes = recv(sDownload, buffer, sizeof(buffer), 0)) > 0) { if (isBody) { fwrite(buffer, 1, readBytes, downloadedFile); } if (strstr(buffer, "\r\n\r\n") && !isBody) { char *tmp = strstr(buffer, "\r\n\r\n"); isBody = true; // 3. Parameter angepasst fwrite(tmp+4, 1, readBytes-((tmp+4)-buffer), downloadedFile); } }
Ich vermute letzteres ist etwas performanter.
-
DaRe schrieb:
fwrite(tmp+4, 1, strlen(tmp+4), downloadedFile);
recv liefert keinen '\0' am Ende des Strings... strlen sucht aber nach '\0':
while ((readBytes = recv(sDownload, buffer, sizeof(buffer), 0)) > 0) { buffer[readBytes]='\0'; // <-----Notwendig if (isBody) { fwrite(buffer, 1, readBytes, downloadedFile); } if (strstr(buffer, "\r\n\r\n") && !isBody) { char *tmp = strstr(buffer, "\r\n\r\n"); isBody = true; fwrite(tmp+4, 1, strlen(tmp+4), downloadedFile); } }
Auch möglich durch subtrahieren von Adressen:
while ((readBytes = recv(sDownload, buffer, sizeof(buffer), 0)) > 0) { if (isBody) { fwrite(buffer, 1, readBytes, downloadedFile); } if (strstr(buffer, "\r\n\r\n") && !isBody) { char *tmp = strstr(buffer, "\r\n\r\n"); isBody = true; // 3. Parameter angepasst fwrite(tmp+4, 1, readBytes-((tmp+4)-buffer), downloadedFile); } }
Ich vermute letzteres ist etwas performanter.
WAAAAAH GEIL ES TUT!!! :-))) vielen Dank an die mitgeholfen haben
-
DaRe schrieb:
fwrite(tmp+4, 1, strlen(tmp+4), downloadedFile);
recv liefert keinen '\0' am Ende des Strings... strlen sucht aber nach '\0':
while ((readBytes = recv(sDownload, buffer, sizeof(buffer), 0)) > 0) { buffer[readBytes]='\0'; // <-----Notwendig if (isBody) { fwrite(buffer, 1, readBytes, downloadedFile); } if (strstr(buffer, "\r\n\r\n") && !isBody) { char *tmp = strstr(buffer, "\r\n\r\n"); isBody = true; fwrite(tmp+4, 1, strlen(tmp+4), downloadedFile); } }
Auch möglich durch subtrahieren von Adressen:
while ((readBytes = recv(sDownload, buffer, sizeof(buffer), 0)) > 0) { if (isBody) { fwrite(buffer, 1, readBytes, downloadedFile); } if (strstr(buffer, "\r\n\r\n") && !isBody) { char *tmp = strstr(buffer, "\r\n\r\n"); isBody = true; // 3. Parameter angepasst fwrite(tmp+4, 1, readBytes-((tmp+4)-buffer), downloadedFile); } }
Ich vermute letzteres ist etwas performanter.
Methode 1 funktioniert nicht für Binärdateien.
Methode 2 schon.Etwas optimiert:
while ((readBytes = recv(sDownload, buffer, sizeof(buffer), 0)) > 0) { if (isBody) { fwrite(buffer, 1, readBytes, downloadedFile); } else { char *tmp = strstr(buffer, "\r\n\r\n"); if( tmp ) { isBody = true; // 3. Parameter angepasst fwrite(tmp+4, 1, readBytes-((tmp+4)-buffer), downloadedFile); } } }
Ich hab übrigens auch schon header gehabt bei denen das Cookie alleine schon über 2000 bytes lang war.
-
DaRe schrieb:
recv liefert keinen '\0' am Ende des Strings... strlen sucht aber nach '\0':
wenn ich ne http response mit stringfunktionen bearbeite, sorge ich dafür dass der empfangspuffer stets nullterminiert ist:
char buf [ 1024 ] = { 0 }; int bytes; bytes = recv ( sock, buf, sizeof ( buf ) -1, 0 ); // -1: terminierung ist sichergestellt ...
denn sonst kann es passieren, dass strstr und co. bis zum abwinken suchen, wenn sie die 0 nicht finden.
gruß,
B.B.
-
Oh ja, langsam lern ich dieses '\0' hassen.
Wenn sein Header, wie er sagt nicht wirklich größer wird als 505 Byte oder sowas, dann ist die NULL-Prüfung auf strstr eigentlich nicht nötig... aber ich weiß nicht, was er da wirklich für nen Server am Start hat ^^
Aber na gut, die erste Variante sollte nicht verwendet werden.
-
Big Brother schrieb:
DaRe schrieb:
recv liefert keinen '\0' am Ende des Strings... strlen sucht aber nach '\0':
wenn ich ne http response mit stringfunktionen bearbeite, sorge ich dafür dass der empfangspuffer stets nullterminiert ist:
char buf [ 1024 ] = { 0 }; int bytes; bytes = recv ( sock, buf, sizeof ( buf ) -1, 0 ); // -1: terminierung ist sichergestellt ...
denn sonst kann es passieren, dass strstr und co. bis zum abwinken suchen, wenn sie die 0 nicht finden.
gruß,
B.B.Kommt immer drauf an, ob man nur
Content-Type: text/*
erwartet. Dann macht es Sinn
buf[bytes] = 0
zu setzen.
Alles andere wird komplizierter und braucht das nicht.
Du musst dir nur die verschiedenen Positionen in den Puffern merken, die Puffer evtl. neu allozieren, mit memcpy kopieren, von der letzten Suchposition (Suchwortlänge-1) abziehen wenn du optimieren willst, usw. usf.Noch komplizierter wird's bei HTTP 1.1 und Transfer-Encoding: chunked.
-
es ging mir hier um den http response header, nicht um den content.
hast du wohl nicht gemerkt.
-
Big Brother schrieb:
es ging mir hier um den http response header, nicht um den content.
hast du wohl nicht gemerkt.Ne, hab ich nicht gemerkt.
Wenn du 300 Byte Header und dann Binärdaten hast, die nach ein paar Bytes ein 0x00 enthalten - was macht es da für einen Sinn ein '\0' anzuhängen?
OP wollte auch Binärdateien runterladen, strlen ist hier einfach falsch.
strstr um das Headerende zu finden, dann mem*-Funktionen. So macht man das.EDIT: Ok, für strstr ist es notwendig (und ich mach es eigentlich auch grundsätzlich ;)), hab jetzt aber keine Lust meinen post noch zu ändern.