Importer:Error:WasEmpty



  • Heyho,

    Ich wejß jetzt nicht ob meine Frage wirklich mit dem Threadtitel zusammenhängt, aber ich versuchs mal:

    Nehmen wir mal an ich habe einen Client und der bekommt von einem Server folgendes Packet zugeschickt:
    <Länge von Text><Text>
    Also z.B:
    05 32 32 32 32 32

    Wie könnte ich das ganze nun im Client angehen um das auszuwerten?
    Ich hatte ja an sowas gedacht:

    struc packet
    {
      uint8_t length;
      char text [length];
    };
    

    So nun ist mir leider klar, dass ich das so nicht machen kann.
    Gibts da vielleicht irgend nen Trick um das doch noch so hinzubiegen :)?



  • RTMPHasser schrieb:

    Ich wejß jetzt nicht ob meine Frage wirklich mit dem Threadtitel zusammenhängt

    Welcher Threadtitel 😃

    Du musst das Format des geschickten Pakets kennen, also wo welches Byte mit welchem Typ steht. Dann kannst du entsprechend einzelne Bytes binär lesen.



  • Mein Threadtitel wurde gestohlen :<

    Ja ich kenne das das Packet ja.
    Ich weiß genau wo die Länge des Textes steht bzw. der Text beginnt, aber die Länge ist von einer anderen Membervariable abhängig.
    Jetzt will ich das ganze aber so bequem wie möglich auslesen können 🙂



  • std::vector<char> oder std::string



  • Im Idealfall würde ich das ja gerne so machen:

    struct sPacket
    {
      uint8_t length;
      char text [length]
    };
    
    ...
    sPacket *pPacket = (sPacket*)recvBuffer;
    std::cout << pPacket-> length << pPacket-> text;
    ...
    

    Da weiß ich gerade nicht wie ich std::vector bzw. std::string da reinpacken soll.



  • struct sPacket
    {
      uint8_t length;
      std::string text;
    };
    
    sPacket p;
    recv(&p.length, sizeof p.length);
    p.text.resize(p.length);
    recv(&p.text[0], p.text.length());
    

    Fehlerbehandlung kannste selbst einfügen.



  • cooky451 schrieb:

    struct sPacket
    {
      uint8_t length;
      std::string text;
    };
    
    sPacket p;
    recv(&p.length, sizeof p.length);
    p.text.resize(p.length);
    recv(&p.text[0], p.text.length());
    

    Fehlerbehandlung kannste selbst einfügen.

    Okay das wäre eine Lösung, aber eigentlich wollte ich das so unkompliziert wie möglich machen.

    Dass ich einfach nen Byte Array als Packetbuffer hab und den zu meiner sPacket struct casten kann. Wie das Beispiel oben halt ;O

    Interessiert mich nur, ob das irgendwie möglich ist.



  • Das zu casten ist standardkonform nie möglich, siehe alignment / strict aliasing. Mein Beispiel oben ist allerdings auch Quatsch, so geht es natürlich besser:

    std::uint8_t size;
    recv(&size, sizeof size);
    std::string s(size, ' ');
    recv(&s[0], s.size());
    

    Denn schließlich kennt der String ja bereits seine Größe. Viel unkomplizierter geht's wohl nicht. Außer vielleicht wenn du eine kleine Maximalgröße hast (etwas im Bereich von 255 Zeichen), und du ein paketorientiertes Protokoll benutzt:

    std::array<char, 256> buf = {}; // setzt jedes byte auf 0
    recv(&buf[0], buf.size() - 1);
    


  • Bei TCP/IP musst du auch noch beachten, dass im Puffer mehr als ein Telegramm vorhanden sein kann. Im Grunde musst du beim Datenempfang alles an einen Lesepuffer anhängen und dann gucken, wieviele Daten tatsächlich drin stehen.

    std::vector<char> GlobalBuffer;
    
    void on_receive( socket s )
    {
       char RecBuf[256];
       unsigned int BytesReceived = recv( s, RecBuf, 256 );
       while( BytesReceived > 0 )
       {
          GlobalBuffer.insert( GlobalBuffer.end(), RecBuf, RecBuf + BytesReceived );
          BytesReceived = recv( s, RecBuf, 256 );
       }
       // Schleife so oft durchlaufen, wie genügend Daten für den Telegrammkopf vorhanden sind
       while( GlobalBuffer.size() >= TELEGRAM_HEADER_SIZE )
       {
          TelegramHeader* Hdr = reinterpret_cast<TelegramHeader*>( &GlobalBuffer[0] );
          if( GlobalBuffer.size() >= Hdr->TelegramSize )
          {
             ...
             GlobalBuffer.erase(  GlobalBuffer.begin(), GlobalBuffer.begin() + Hdr->TelegramSize );
          }
          else
          {
             return;
          }
       }
    }
    

    TELEGRAM_HEADER_SIZE und TelegramHeader habe ich nicht näher spezifiziert, sollte aber klar sein, wozu sie da sind. Außerdem habe ich bei GlobalBuffer der Einfachheit halber std::vector benutzt, ein Ringpuffer ist da wahrscheinlich die bessere Wahl.

    Edit:
    Typos fixed



  • @RTMPHasser
    Ein konkretes Beispiel an welcher Stelle du dir etwas vereinfachen willst, inklusive wo die Daten herkommen bzw. der Empfangspuffer ("recvBuffer") gefüllt wird wäre hilfreich.

    Casten ist wie schon geschrieben wurde nicht OK, und zwar wegen Alignment. (Aliasing wäre kein Problem, zumindest nicht wenn der Empfangspuffer ein (unsigned) char Array ist.)

    Wenns wirklich nur um die gezeigte Struktur geht, und der Inhalt immer ein String ist, und die Daten immer schon vollständig im Empfangspuffer vorliegen, dann ist es ja relativ einfach:

    std::string GetStringFromBuffer(char const* buffer)
    {
        unsigned char length = reinterpret_cast<unsigned char const*>(buffer)[0]; // OK für unsigned char, wenns ein anderer Typ ist wie short/int/... dann müsste man memcpy() verwenden!
        return std::string(buffer + 1, buffer + 1 + length);
    }
    
    // ...
    
        std::string text = GetStringFromBuffer(recvBuffer);
        std::cout << "length: " << text.size() << ", text: " << text << "\n";
    

Log in to reply