HTTP Protokoll mhhh
-
PalimPalim,
ich hab mir ein kleines Programm geschrieben, das eine Datei via Socket und HTTP Protokoll von einer Seite auf den lokalen Rechner runterlädt. Also erst Verbindung, dann GET-anfrage und dann Übertragung mit recv(); . So nun kommt das Problem, es werden immer nur 32 KB übertragen, egal, wie groß die Datei ist. Woran kann das liegen? Außerdem: Wenn der Buffer meinetwegen 1024 Bytes groß ist, werden dann auch immer nur 1024 Bytes pro Schleifendruchlauf übertragen?
Gruß PillePalle
Hier der Code
int main() { //---------------------- // Initialize Winsock WSADATA wsaData; char command[250]; int bsend; int brevc; char buf[1025]; FILE *fp; int iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != NO_ERROR) printf("Error at WSAStartup()\n"); //---------------------- // Create a SOCKET for connecting to server SOCKET ConnectSocket; ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (ConnectSocket == INVALID_SOCKET) { printf("Error at socket(): %ld\n", WSAGetLastError()); WSACleanup(); system("pause"); return 0; } //---------------------- // The sockaddr_in structure specifies the address family, // IP address, and port of the server to be connected to. sockaddr_in clientService; clientService.sin_family = AF_INET; clientService.sin_addr.s_addr = inet_addr( "213.203.239.230" ); clientService.sin_port = htons( 80 ); //---------------------- // Connect to server. if ( connect( ConnectSocket, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR) { printf( "Failed to connect.\n" ); WSACleanup(); system("pause"); return 0; } sprintf(command,"GET /downloads/sonstiges/lichtschalter.zip HTTP/1.0\nHost: maggot.de\n\n"); bsend= send(ConnectSocket,command,strlen(command),0); if(bsend== -1) { printf( "Fehler bei send().\n" ); WSACleanup(); system("pause"); return 0; } fp=fopen("huhu.zip","w+b"); while((brevc=recv(ConnectSocket,buf,sizeof(buf)-1,0)) >0) { fputs(buf,fp); } fclose(fp); printf("\nConnected to server.\n"); WSACleanup(); system("pause"); return 0; }
-
fwrite
-
PillePalle schrieb:
Wenn der Buffer meinetwegen 1024 Bytes groß ist, werden dann auch immer nur 1024 Bytes pro Schleifendruchlauf übertragen?
Wenn man recv(ConnectSocket,buf,len,0) schreibt dann wird maximal len Bytes übertragen, wenn Datei >= len Byte.
Kann aber auch weniger sein, das wird Server-Seitig entschieden, der Server kann also auch weniger reinpacken. Wenn Datei > len Byte, dann leerst du ja auch den Puffer in einer Schleife und lässt ihn neu beschreiben, bis Datei zu ende gelesen ist:
while(( brevc = recv (ConnectSocket, buf, sizeof(buf), 0 )) >0 ) { fwrite( buf, sizeof(char), brevc, fp ) }oder:
while(( brevc = recv (ConnectSocket, buf, sizeof(buf), 0 )) >0 ) { fwrite( buf, brevc, 1, fp ) }
-
Dankeschön klappt wunderbar. Nochwas: Hat jemand eine Idee wie man einfach den Header herausnehmen kann? Kann man das mit den Stringfunktionen machen? einfach nach dem ersten\n\n suchen?
Gruß
-
einfach nach dem ersten\n\n suchen?
Ganz genau.
-
Sorry wegen oben, war zu voreilig. Nicht \n\n sondern \r\n\r\n.
-
Ist ein bisschen schwierig so zu suchen da die Zeichen ja über mehrere Buffer verteilt sein können.
-
Ja genau. Hatte ich auch festgestellt
. Aber wenn man will geht alles

Gruß
-
Hi,
so hab mal eine provisorische Lösung, ohne mit Berücksichtigung der zerteilten Buffer, gebastelt. Nur ein Problem: Das Programm stürzt ab wenn ich es starte. Woran liegt das?
while(( brevc = recv (ConnectSocket, buf, sizeof(buf), 0 )) >0 ) { for(i=0;i<strlen(buf);i+=1) { if(buf[i]=='\n' && buf[i+1]=='\n') { begin=1; } } if(begin==1) { fwrite( buf, brevc, 1, fp ); printf("\r%0.2f MB empfangen.",((float)FileSize("tmp.txt") /1024)/1024); } }
-
Der Buffer den du von recv bekommst ist nicht 0 terminiert.
-
Und deswegen belibt das Programm stehen?
Wie kann man das lösen?
-
Kuckuck.
Ich hatte mir auch mal son Ding getippselt, das Dateien runterladen kann.
Die meisten http-header, die ich als Antwort bekam, waren <512 Byte.
Nur ganz selten mal ein Server, der einen etwas längeren Header zurückgeschickt hat.Mit dem String-Ende-Zeichen '\0' würde ich gar nicht erst anfangen.
Weder künstlich anhängen, nach suchen, oder sonstwas.
Hatte ich erst auch so und mich dann gewundert, warum es mit Bild- und Videodateien
nicht geklappt hat.Die benötigte Länge steckt ja bereits in brevc.
for( i = 0; i < brevc - 1; i++ ) // -1 weil du buf[i+1] abfragst.
-
Aber warum will dann Windows ein Problembericht senden, wenn ich das Prog starte?
-
Rüdiger sagte es bereits. Der buffer den recv liefert ist nicht nullterminiert (wie auch, es könnte ja eine Datei übertragen werden, die Nullbytes enthält*). Deshalb schlägt strlen fehl, weil es das Nullbyte gottweisswo, nur nicht in Deinem buffer sucht. Aber zum Glück liefert ja recv die benötigten Informationen als Rückgabewert.
[OT] * oder 0xFF
(SCNR) [/OT]
-
Also ich baue mir das, was von recv() kommt erstmal in einen eigenen Buffer zusammen.
An diesen Buffer wird nach jedem Aufruf von recv() zusätzlich 0 angehängt, um mit strstr(Buffer, "Content-Length") die Länge der (Binär-)Daten zu ermitteln und mit strstr(Buffer, "\r\n\r\n") nach dem Ende des Headers (=Beginn Content / =Begin (Binär-)Daten) suchen zu können.
recv() == Fehler => Schleifenabbruch
recv() == 0 => "gracefully closed" (sollte nicht vorkommen) => Schleifenabbruch
"Content-Length":
Länge Buffer >= Beginn Content + Length Content => Schleifenabbruch
kein "Content-Length":
strstr(Buffer, "\r\n0\r\n\r\n) => Ende der Nicht-Binär-Daten => Schleifenabbruch
Zusätzlich können (HTML-)Texte "chunked" aufgebaut sein.
a\r\n <- Länge des nächsten Teilstücks im hex-Format + \r\n 0123456789\r\n <- Teilstück + \r\n 8\r\n 89012345\r\n 0\r\n\r\n <- Endekennzeichen "\r\n0\r\n\r\n"
-
Dieser Thread wurde von Moderator/in Tim aus dem Forum ANSI C in das Forum Rund um die Programmierung verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
Ich zeig dir mal wie ich das in C++ gemacht gemacht habe. Ist aber nicht so gut getestet. An die Funktion processChunk kannst du den Puffer den dir recv befüllt hat übergeben.
#include <iostream> #include <string> #include <cstring> #include <cassert> class LineBuffer { public: LineBuffer(std::size_t maximumLineLength) : maximumLineLength_(maximumLineLength) , isLineComplete_(false) { } std::size_t consumeChunk(const char* pBuffer, std::size_t bufferSize) { if(isLineComplete_) { return 0; } std::size_t newlineCharacterPosition; bool newlineCharacterFound = findNewlineCharacter(pBuffer, bufferSize, newlineCharacterPosition); std::size_t bytesToConsume = newlineCharacterFound ? newlineCharacterPosition + 1 : bufferSize; if(line_.length() + bytesToConsume > maximumLineLength_) { throw LineTooLongException(); } line_ += std::string(pBuffer, bytesToConsume); if(newlineCharacterFound) { isLineComplete_ = true; removeLineTerminator(); } return bytesToConsume; } bool isLineComplete() const { return isLineComplete_; } const std::string& getLine() const { return line_; } void reset() { line_.erase(); isLineComplete_ = false; } private: std::size_t getLineTerminatorLength() const { assert(isLineComplete()); assert(line_.length() >= 1 && line_[line_.length() - 1] == '\n'); if(line_.length() >= 2 && line_[line_.length() - 2] == '\r') { return 2; } else { return 1; } } void removeLineTerminator() { assert(isLineComplete()); line_.erase(line_.length() - getLineTerminatorLength(), std::string::npos); } bool findNewlineCharacter(const char* pBuffer, std::size_t bufferSize, std::size_t& newlineCharacterPosition) const { const char* pNewlineCharacter = static_cast<const char*>(std::memchr(pBuffer, '\n', bufferSize)); if(pNewlineCharacter == NULL) { return false; } else { newlineCharacterPosition = pNewlineCharacter - pBuffer; return true; } } private: std::string line_; bool isLineComplete_; const std::size_t maximumLineLength_; public: class LineTooLongException { }; }; LineBuffer lineBuffer(4096); enum State { HEADERS, BODY }; State state = HEADERS; void processLine(const std::string& line) { if(line.empty()) { state = BODY; } /*else { std::cout << "|" << line << "|" << std::endl; }*/ } std::size_t processLineChunk(const char* pBuffer, std::size_t bufferSize) { std::size_t bytesConsumed = lineBuffer.consumeChunk(pBuffer, bufferSize); if(lineBuffer.isLineComplete()) { processLine(lineBuffer.getLine()); lineBuffer.reset(); } return bytesConsumed; } std::size_t processBodyChunk(const char* pBuffer, std::size_t bufferSize) { std::cout << std::string(pBuffer, bufferSize); return bufferSize; } void processChunk(const char* pBuffer, std::size_t bufferSize) { std::size_t totalBytesConsumed = 0; while(totalBytesConsumed < bufferSize) { switch(state) { case HEADERS: totalBytesConsumed += processLineChunk(pBuffer + totalBytesConsumed, bufferSize - totalBytesConsumed); break; case BODY: totalBytesConsumed += processBodyChunk(pBuffer + totalBytesConsumed, bufferSize - totalBytesConsumed); break; } } } int main() { try { std::string input = "HTTP/1.1 200 OK\r\n" "Server: Apache\r\n" "Connection: close\r\n" "Content-Type: text/html\r\n" "\r\n" "<html>Hallo Welt</html>"; processChunk(input.data(), input.length()); } catch(LineBuffer::LineTooLongException&) { std::cout << "Line too long" << std::endl; } }