Sockets und das HTTP-Protokoll



  • joomoo schrieb:

    #ifdef linux
    #include <sys/socket.h> // socket(), connect()
    #include <arpa/inet.h> // sockaddr_in
    #else
    #include <winsock2.h>
    #endif
    

    Das ist ziemlich unportabel. Besser ist:

    /* all UNIX-like OSs (Linux, *BSD, MacOSX, Solaris, ...) */
    #if defined(unix) || defined(__unix) || defined(__unix__)
    #include <sys/socket.h>
    #include <arpa/inet.h>
    /* MS Windows */
    #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
    #include <winsock2.h>
    /* IBM OS/2 */
    #elif defined(OS2) || defined(_OS2) || defined(__OS2__) || defined(__TOS_OS2__)
    #include <sys/socket.h>
    #include <arpa/nameser.h>
    #else
    #error unsupported or unknown operating system.
    #endif
    


  • Fye schrieb:

    Besser ist...

    das ist ja schon mal ein anfang. kannst es ja noch ergänzen: http://predef.sourceforge.net/preos.html



  • Ich hab nicht gesagt, dass das vollständig ist. Ich mein halt, sich auf Windows und Linux zu beschränken und die armen BSD'ler und MacOSX'ler außen vor zu lassen ist nicht so toll. 😉

    BTW: Die Seite kenne ich. Was meinste, wo ich die Makros alle herhabe? 😉



  • Fye schrieb:

    Ich hab nicht gesagt, dass das vollständig ist. Ich mein halt, sich auf Windows und Linux zu beschränken und die armen BSD'ler und MacOSX'ler außen vor zu lassen ist nicht so toll. 😉

    aber os-halbe wird berücksichtigt. ein system dass seit 10 jahren kein schwein mehr verwendet 😉

    Fye schrieb:

    BTW: Die Seite kenne ich. Was meinste, wo ich die Makros alle herhabe? 😉

    selbst gebastelt? ich dachte das hast du aus irgendso'nem open source projekt rauskopiert...
    🙂



  • Müsste es nicht HTT-Protokoll und FT-Protokoll heißen?



  • void SendAll(int socket, const char* const buf, const size_t size)
    {
        size_t bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
        do
        {
            bytesSent += send(socket, buf + bytesSent, size, 0);
            if(bytesSent < 0) // Wenn send einen Wert < 0 zurück gibt deutet dies auf einen Fehler hin.
            {
                throw CreateSocketError();
            }
        } while(bytesSent < size);
    }
    

    Ich habe den Artikel nur kurz überflogen, aber das sieht falsch aus - muss der dritte Parameter nicht size - bytesSent sein? Sonst liest send() unter Umständen über den Puffer hinaus. Außerdem kann es nach dem ersten Schleifendurchlauf passieren, dass bytesSent schon einen positiven Wert hat, send() etwas negatives zurückgibt und bytesSent danach aber immer noch positiv ist.



  • ck schrieb:

    void SendAll(int socket, const char* const buf, const size_t size)
    {
        size_t bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
        do
        {
            bytesSent += send(socket, buf + bytesSent, size, 0);
            if(bytesSent < 0) // Wenn send einen Wert < 0 zurück gibt deutet dies auf einen Fehler hin.
            {
                throw CreateSocketError();
            }
        } while(bytesSent < size);
    }
    

    Ich habe den Artikel nur kurz überflogen, aber das sieht falsch aus - muss der dritte Parameter nicht size - bytesSent sein? Sonst liest send() unter Umständen über den Puffer hinaus. Außerdem kann es nach dem ersten Schleifendurchlauf passieren, dass bytesSent schon einen positiven Wert hat, send() etwas negatives zurückgibt und bytesSent danach aber immer noch positiv ist.

    glaube ich nicht, es muss wenn überahaut so heissen:

    bytesSent += send(socket, buf + bytesSent, size - bytesSent, 0);
    


  • Hallo ck und Parallan,

    Vielen Dank! Ihr habt natürlich Recht, ich wunder' mich warum ich so einen extremen Fehler machen konnte...
    Hier mal die überarbeite Version:

    void SendAll(int socket, const char* const buf, const size_t size)
    {
        size_t bytesSent = 0; // Anzahl Bytes die wir bereits vom Buffer gesendet haben
        do
        {
            size_t result = send(socket, buf + bytesSent, size - bytesSent, 0);
            if(result < 0) // Wenn send einen Wert < 0 zurück gibt deutet dies auf einen Fehler hin.
            {
                throw CreateSocketError();
            }
            bytesSent += result;
        } while(bytesSent < size);
    }
    

    Kann ich das so übernehmen?

    mfg.



  • Zumindest unter Linux gibt send nicht size_t sondern ssize_t zurück (und die MSDN sagt int, für Windows). size_t ist meines Wissens jedenfalls immer unsigned, deshalb ist die if(result < 0)-Abfrage nie falsch. Ich denke es reicht aus, für result einfach int zu nehmen. Sonst sieht es IMHO gut aus 🙂



  • ck schrieb:

    size_t ist meines Wissens jedenfalls immer unsigned, deshalb ist die if(result < 0)-Abfrage nie falsch.

    nie 'wahr' 😉
    aber hast recht, es muss ein signed-typ sein damit das funzt.
    übrigens: dieses 'throw' argument sollte besser 'SendError' oder so heissen 😉



  • Vielen Dank! Ihr habt Recht, ich weiß auch nicht wie ich auf size_t gekommen bin. Ich hab's jetzt schon im Artikel korrigiert, auf den FTP-Server komm ich gerade nicht drauf...

    Die CreateSocketError funktion hab ich so genannt, da sie ja noch nicht wirft sondern erstmal den SocketError erstellt.

    mfg.



  • #else
        int error = WSAGetLastError();
        char* msg;
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                      NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                      reinterpret_cast<char*>(&msg), 0, NULL);
        temp << "Socket-Fehler #" << error << ": " << msg;
    #endif
    

    Fehlt da nicht ein LocalFree? Und Leider kann auch FormatMessage fehl schlagen in welchem Fall das Program wahrscheinlich abstürzen würde. Dies passiert wenn kein Text gefunden wird was bei lokalisierten System leider hin und wieder passiert.



  • @joomoo

    Also weil's hier noch niemand getan hat: Vielen Dank für die tolle Anleitung. Solche Sachen geben immer einen Haufen Arbeit und das muss man auch mal loben 👍

    Gerade bei der Verarbeitung von 'chunked' Mode hilft mir dein Text jetzt weiter.

    Gruss, Peter



  • @Ben04

    Danke das hab ich vergessen! Ich kenn mich leider nicht so aus mit den Windows-Funktionen und hab's jetzt so abgeändert:

    int error = WSAGetLastError();
        temp << "Socket-Fehler #" << error;
        char* msg;
        if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                         NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                         reinterpret_cast<char*>(&msg), 0, NULL))
        {
            temp << ": " << msg;
            LocalFree(msg);
        }
    

    Ist das so in Ordnung?

    @Peter01

    Danke! Freut mich, dass es jemandem weiterhilft.

    mfg.



  • Ja das sollte so funktionieren, solange temp nicht auf "Ausnahme werfen" eingestellt ist.

    Ein

    try{
      temp << ": " << msg;
      LocalFree(msg);
    }catch(...){
      LocalFree(msg);
      throw;
    }
    

    wäre sicher.



  • Oder mit so einer Hilfsklasse:

    class local_free_on_block_exit
      : private noncopyable
    {
    public:
      // Constructor blocks all signals for the calling thread.
      explicit local_free_on_block_exit(void* p)
        : p_(p)
      {
      }
    
      // Destructor restores the previous signal mask.
      ~local_free_on_block_exit()
      {
        ::LocalFree(p_);
      }
    
    private:
      void* p_;
    };
    


  • temp würde doch nur werfen, wenn msg ungültig ist, und das passiert doch nur, wenn FormatMessage false zurück gibt, oder?

    mfg.



  • Erstmal vorweg: Klasse Artikel! ➡ 👍 . Vor allem da er einen perfekten Einstieg in Bezug auf die Unterschiede bzw. Differenzierung zwischen den verschiedenen OS's liefert und dabei das Hauptthema (das HTTP-Protokoll) immer im Auge behält. Echt gelungen!

    joomoo schrieb:

    Ich kenn mich leider nicht so aus mit den Windows-Funktionen und hab's jetzt so abgeändert:

    int error = WSAGetLastError();
        temp << "Socket-Fehler #" << error;
        char* msg;
        if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                         NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                         reinterpret_cast<char*>(&msg), 0, NULL))
        {
            temp << ": " << msg;
            LocalFree(msg);
        }
    

    Jupp, so ist okay. Allerdings ist der Cast IMHO überflüssig (Und da wo man Casts -gerade reinterpret_cast's- vermeiden kann, sollte man das auch tun 😉 ).

    joomoo schrieb:

    temp würde doch nur werfen, wenn msg ungültig ist, und das passiert doch nur, wenn FormatMessage false zurück gibt, oder?

    Genau, deshalb ist Dein Code vollkommen okay 😉 .
    Siehe:

    MSDN zu FormatMessage schrieb:

    Return Value

    If the function succeeds, the return value is the number of TCHARs stored in the output buffer, excluding the terminating null character.

    If the function fails, the return value is zero. To get extended error information, call GetLastError.

    Quelle ➡ FormatMessage (MSDN).

    PS: Ich würde allerdings den Link: http://predef.sourceforge.net/preos.html#sec22 noch in den Artikel mit aufnehmen, der ist echt gut 😉 .



  • joomoo schrieb:

    temp würde doch nur werfen, wenn msg ungültig ist, und das passiert doch nur, wenn FormatMessage false zurück gibt, oder?

    mfg.

    Oder der Stream aus irgendeinem Grund zusammenbricht. Das kann an sich immer passieren. Jede Schreiboperation auf einem String kann den badbit setzen.

    Allerdings ist der Cast IMHO überflüssig (Und da wo man Casts -gerade reinterpret_cast's- vermeiden kann, sollte man das auch tun 😉 ).

    Nö definitive nicht. Von char** kommt man nun einmal nur mit einem Cast auf char*.



  • @CodeFinder
    Vielen Dank fürs Lob!
    Den Link hab ich gleich mal aufgenommen.

    Oder der Stream aus irgendeinem Grund zusammenbricht. Das kann an sich immer passieren. Jede Schreiboperation auf einem String kann den badbit setzen.

    Okay, dann übernehm ich deinen Code noch.

    Hab den neuen Code jetzt hinzugefügt und werde alle Quelldateien aktualisieren (geht momentan irgendwie noch nicht...).

    mfg.


Anmelden zum Antworten