Mit Sockets größere Dateien downloaden
-
Bisher habe habe ich mit Sockets eine eigene Funktion implementiert um Dateien auszutauschen.
char* Socket::Read(int &iBytes) { // total size of data iBytes = atoi(Read1Block(32)); // number of packets to transmit data int iBlocks = atoi(Read1Block(32)); char *cData = new char[iBytes]; char *cBuffer; int iBlockSize = _SOCK_BLOCK_SIZE_; if(iBytes < _SOCK_BLOCK_SIZE_) iBlockSize = iBytes; for(int i = 0; i < iBlocks; i++) { cBuffer = Read1Block(iBlockSize); for(int j = 0; j < iBlockSize; j++) { cData[i*iBlockSize+j] = cBuffer[j]; } } return cData; }
Wie man sieht, muss man größere Dateien in Blöcke zerlegen, damit nicht die Übertragung mitten drin abbricht. Nun wollte ich nen kleinen web-downloader schreiben und denke mir, dass die gängigen Protokolle http oder ftp die ganze Sache leicht anders handlen als ich und wollte fragen wie man über http oder ftp größere Dateien downloaden kann.
-
Geht bei HTTP prinzipiell genauso.
Aufpassen musst du (beim Empfangen), wenn im HTTP-Header "transfer-encoding: chunked" gesetzt ist.
Dann sind die Daten in Blöcke aufgeteilt, denen jeweils eine Zeile mit der Länge des folgenden Blocks vorangeht.
-
Insgesamt ist aber davon abzuraten das selber zu programmieren. Es gibt eine fertige Bibliothek namens libcurl, die den Download über HTTP/FTP recht einfach macht.
-
haste nen exaktes beispiel für http.
-
denke ich habe was gefunden.
ghjghj schrieb:
Insgesamt ist aber davon abzuraten das selber zu programmieren. Es gibt eine fertige Bibliothek namens libcurl, die den Download über HTTP/FTP recht einfach macht.
ich mache das ganze nur aus spaß, um ne minimale library für windows und linux zu haben. habe ne socket-klasse geschrieben und eine abstrakte client- sowie server-klasse. damit soll man dann mit minimalen aufwand alle möglichen clients erstellen können. und ich denke in meine socket-klasse gehört noch ein ftp/http-request.
-
Die Frage kommt hier öfter
Wenn du die Bild URL hast musst du einfach eine TCP Verbindung auf Port 80 einrichten, eine HTTP GET Anfrage senden, dann empfangen bis "\r\n\r\n" gesendet wurde (HTTP-Header Ende) und dann alles was weiter empfangen wird in eine Datei schreiben. Solange du die GET Anfrage mit HTTP/1.0 machst, gibt's auch kein chunked zu beachten.
-
klingt relativ einfach und elegant
-
dgrat_ schrieb:
klingt relativ einfach und elegant
Ganz so einfach wie es sich anhört ist es nicht.
Angenommen du benutzt die einfache Variante, nämlich HTTP/1.0:
Um das Header-Ende zu finden musst du die empfangenen Daten solange an einen Puffer anhängen bis du "\r\n\r\n" gefunden hast (Ich habe schon header gehabt bei denen allein das Cookie über 2000 Bytes lang war).
Wenn es auch mit Binärdaten funktionieren soll, musst du memmove/memcpy/strstr benutzen. Dann musst du den Puffer u.U. auch dynamisch vergrößern und umkopieren.
Ab dem Moment wo du das Header-Ende gefunden hast kannst du munter drauflosspeichern - vorausgesetzt du hast ein 200 OK bekommen.
Edit:
Um effektiv zu suchen merkst du dir die Position im Puffer bis zu der du "\r\n\r\n" nicht gefunden hast, ziehst 3 Bytes ab und suchst nächstes Mal ab der Position.
-
EOP schrieb:
Dann musst du den Puffer u.U. auch dynamisch vergrößern und umkopieren.
Wenn einen die Daten im Endeffekt eh nicht interessieren, kann man auch einfach immer neu empfangen. Wenn man dann noch 100%tige Sicherheit haben will, muss man halt drauf achten, dass am Ende des Puffers kein "\r\n" (oder sowas) steht, aber das ist schon sehr unwahrscheinlich
-
cooky451 schrieb:
EOP schrieb:
Dann musst du den Puffer u.U. auch dynamisch vergrößern und umkopieren.
Wenn einen die Daten im Endeffekt eh nicht interessieren, kann man auch einfach immer neu empfangen. Wenn man dann noch 100%tige Sicherheit haben will, muss man halt drauf achten, dass am Ende des Puffers kein "\r\n" (oder sowas) steht, aber das ist schon sehr unwahrscheinlich
Ein minimales Interesse an den Header-Daten muss man schon haben: angenommen du bekommst kein 200 OK.
Oder dein Puffer ist kleiner als die Header-Größe. Was dann?
Edit:
Unwahrscheinlich heisst nicht unmöglich. Ein vernünftiges Programm fängt alle möglichen Fehler ab. Auch ohne 1000 try-catch-Blöcke.
-
[quote="EOP"]
cooky451 schrieb:
Ein minimales Interesse an den Header-Daten muss man schon haben: angenommen du bekommst kein 200 OK.
Oder dein Puffer ist kleiner als die Header-Größe. Was dann?
Nehmen wir doch einfach mal eine kleine Puffergröße von 256.
1. Solange empfangen bis "200 OK" geparst wurde. (Da dürfte einmal reichen :D)
2. Solange immer wieder neu empfangen bis "\r\n\r\n" gefunden wurde.fertig
Ich verstehe das große Problem nicht, da brauchste keine dynamische Speicherverwaltung.
-
[quote="cooky451"]
EOP schrieb:
cooky451 schrieb:
Ein minimales Interesse an den Header-Daten muss man schon haben: angenommen du bekommst kein 200 OK.
Oder dein Puffer ist kleiner als die Header-Größe. Was dann?
Nehmen wir doch einfach mal eine kleine Puffergröße von 256.
1. Solange empfangen bis "200 OK" geparst wurde. (Da dürfte einmal reichen :D)
2. Solange immer wieder neu empfangen bis "\r\n\r\n" gefunden wurde.fertig
Ich verstehe das große Problem nicht, da brauchste keine dynamische Speicherverwaltung.Mein Header ist 257, 258 oder 259 Bytes lang - wo findest du dann das Ende? Nie.
-
EOP schrieb:
Mein Header ist 257, 258 oder 259 Bytes lang - wo findest du dann das Ende? Nie.
Hä? Kennste Schleifen?^^
Vielleicht das Ganze mal in Pseudocode verdeutlichen:int rval = 0; char buf[0x100] = {0}; while (!FoundEnd(buf)) rval = recv(sock, buf, sizeof(buf) - 1, 0), buf[rval] = '\0';
Edit:
Denke ich habe dich falsch verstanden. FoundEnd() merkt sich natürlich wenn die letzten Zeichen '\r' || '\n' sind. Ist ja nicht das Problem
-
cooky451 schrieb:
EOP schrieb:
Mein Header ist 257, 258 oder 259 Bytes lang - wo findest du dann das Ende? Nie.
Hä? Kennste Schleifen?^^
Hä, kannste denken?
253 Bytes + "\r\n\r"
254 Bytes + "\r\n"
255 Bytes + "\r"Was hilft dir da deine Schleife?
-
Siehe edit
-
Auch Pseudocode:
if last == "\r\n\r\n" else if last == "\r\n\r" && first == "\n" else if last == "\r\n" && first == "\r\n" else if last == "\r" && first == "\n\r\n" else ...
Wo hast du denn gelernt so effektiv zu programmieren?
Oder noch eine Stufe besser:
if last == "\r\n\r\n" else if last == "\r\n\r" && first == "\n" else if last == "\r\n\r" && first != "\n" else if last == "\r\n" && first == "\r\n" else if last == "\r\n" && first != "\r\n" else if last == "\r" && first == "\n\r\n" else if last == "\r" && first != "\n\r\n" else ...
-
Ich dachte da mehr an sowas:
(Ungetestet, sollte aber funktionieren)const char* FindEnd(const char *buf) { static int last = 0; if (last) { while ((*buf == '\r' || *buf == '\n') && *buf) if (*buf++ == '\n') return buf; if (!*buf) return 0; } if (strstr(buf, "\n\r\n")) return strstr(buf, "\n\r\n") + 3; if (buf[strlen(buf) - 1] == '\n' || (buf[strlen(buf) - 1] == '\r' && buf[strlen(buf) - 2] == '\n')) last = 1; else last = 0; return 0; }
Wo hast du denn gelernt so effektiv zu programmieren?
In der Compilerschule
(Edit: Oups, eine kleine Änderung :D)