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;
    	}
    }
    

Anmelden zum Antworten