Protokoll schreiben



  • Du fängst an, indem du den Werten in deinem Protokoll eine Reihenfolge und eine Repräsentation gibst. Die Byte-Reihenfolge musst du auch festlegen.
    Das könnte der Aufbau sein:

    <32 bit> "dataID"
    <32 bit> Zeit
    <32 bit> payload Länge
    <N * 8 bit> payload
    


  • TyRoXx schrieb:

    Du fängst an, indem du den Werten in deinem Protokoll eine Reihenfolge und eine Repräsentation gibst. Die Byte-Reihenfolge musst du auch festlegen.
    Das könnte der Aufbau sein:

    <32 bit> "dataID"
    <32 bit> Zeit
    <32 bit> payload Länge
    <N * 8 bit> payload
    

    Ist es nun möglich via TCP eine solche Datenstruktur zu versenden? Außerdem wie sieht was du oben geschrieben hast unter c++ aus?
    Noch etwas: ich möchte als Payload-Typ mal ein double versenden und mal char* die variable Längen aufweisen wie kann ich dies realisieren?

    Eine Menge Fragen sorry dafür, aber ich steh voll auf Schlauch und habe auch keine Vorstellung wie es weitergehen soll 🙂



  • Erweitere das Ganze doch um ein Flag, das den Typ des Payloads anzeigt.

    <32 bit> "dataID"
    <32 bit> Zeit
    <32 bit> payload Länge
    <8 bit> Flag
    <N * 8 bit> payload
    

    Dann legst du z.b. fest, dass bei FLAG == 0x01 ein Double gesendet wird und bei 0x02 ein float etc.



  • protokollant schrieb:

    Erweitere das Ganze doch um ein Flag, das den Typ des Payloads anzeigt.

    <32 bit> "dataID"
    <32 bit> Zeit
    <32 bit> payload Länge
    <8 bit> Flag
    <N * 8 bit> payload
    

    Dann legst du z.b. fest, dass bei FLAG == 0x01 ein Double gesendet wird und bei 0x02 ein float etc.

    Ich bin jetzt davon ausgegangen, dass "dataID" den Typ des Paketes angeben soll. Im Payload kann abhängig von dem Typ natürlich alles Mögliche stehen.

    Ein Beispiel, wie man so ein Protokoll schreibend implementieren kann:

    #include <string>
    #include <cstring>
    #include <cassert>
    #include <cstdint>
    
    typedef std::string Buffer;
    
    struct BinaryWriter
    {
      Buffer &buffer;
    
      explicit BinaryWriter(Buffer &buffer)
        : buffer(buffer)
      {
      }
    
      void write(const char *data, size_t size)
      {
        buffer.append(data, data + size);
      }
    
      void overwrite(size_t position, const char *data, size_t size)
      {
        assert(position + size <= buffer.size());
        std::memcpy(&buffer[position], data, size);
      }
    
      template <class POD>
      void write(POD value)
      {
        write(reinterpret_cast<const char *>(&value), sizeof(value));
      }
    
      template <class POD>
      void overwrite(size_t position, POD value)
      {
        overwrite(position, reinterpret_cast<const char *>(&value), sizeof(value));
      }
    
      void writeString32(const std::string &str)
      {
        write(static_cast<std::uint32_t>(str.size()));
        write(str.data(), str.size());
      }
    };
    
    struct PacketWriter : BinaryWriter
    {
      size_t sizePosition;
      size_t payloadPosition;
    
      PacketWriter(Buffer &buffer, std::uint32_t id, std::uint32_t time)
        : BinaryWriter(buffer)
      {
        write(id);
        write(time);
    
        //die Position der Länge für spätere Korrektur speichern
        sizePosition = buffer.size();
        write(std::uint32_t(0));
    
        //um die Länge des Inhaltes auszurechnen
        payloadPosition = buffer.size();
      }
    
      void finish()
      {
        size_t currentPos = buffer.size();
        std::uint32_t length = static_cast<std::uint32_t>(currentPos - payloadPosition);
    
        overwrite(sizePosition, length);
      }
    };
    
    int main()
    {
      Buffer packet;
      PacketWriter writer(packet, 123, 456);
    
      std::string message = "hallo";
      writer.writeString32(message);
    
      writer.finish();
    
      //packet kann nun versendet werden..
    }
    


  • Mit TCP ist es möglich alles zu versenden was du willst 🙂

    Du kannst die Daten auch einfach als ASCII-Text senden, nach dem Muster "Parameter1=Wert1\n" usw.
    Das ist recht einfach zu schreiben, parsen und analysieren und recht flexibel was variable Länge angeht.



  • @TyRoXx Danke dir für den Code hast dir die Mühe gemacht. Ich werde versuchen dies in meinen Program bzw Projekt hinzuzunehmen und falls Fragen auftauchen müsste ich euch nochmals stören. Könntest du kurz beschreiben was da in groben passiert? Danke dir!

    @scrontch es mir klar, dass ich mit TCP alles versenden. Wir gesagt ich steh voll auf dem Schlauch kann auch daran liegen, dass ich keine Pause hatte 🙂

    @protokollant danke dir auch für diesen Hinweis ist mir nicht eingefallen ist aber eine Simple Lösung.



  • J.Wayne schrieb:

    @TyRoXx Danke dir für den Code hast dir die Mühe gemacht. Ich werde versuchen dies in meinen Program bzw Projekt hinzuzunehmen und falls Fragen auftauchen müsste ich euch nochmals stören. Könntest du kurz beschreiben was da in groben passiert? Danke dir!

    Kann ich schon, mach ich aber nicht.
    Hast du eine konkrete Frage?



  • template <class POD>
      void write(POD value)
      {
        write(reinterpret_cast<const char *>(&value), sizeof(value));
      }
    
      template <class POD>
      void overwrite(size_t position, POD value)
      {
        overwrite(position, reinterpret_cast<const char *>(&value), sizeof(value));
      }
    
      void writeString32(const std::string &str)
      {
        write(static_cast<std::uint32_t>(str.size()));
        write(str.data(), str.size());
      }
    

    Diesen Part habe ich nicht so ganz verstanden. Warum hier ein Template nötig ist und die Methoden(write u overwrite) doppelt vorkommen? Ich möchte dir nicht auf die Füsse tretten verstehe mich nicht falsch 🙂



  • write hängt einen Wert an und overwrite überschreibt den Wert an einer früheren Position.
    Das sind Templates, damit das mit allen primitiven Datentypen funktioniert.
    Die Methoden haben dann noch allgemeinere nicht-Template-Versionen, die beliebige Daten annehmen.
    Falls dir das syntaktisch nicht klar ist, beschäftige dich nochmal mit C++ an sich.



  • Hallo nochmals,

    das von dir (TyRoXx) implementierte Code hat folgenden Konstrukt:
    `<32 bit> "dataID"

    <32 bit> Zeit

    <32 bit> payload Länge

    <N * 8 bit> payload `

    Liege ich da richtig oder nicht? Was soviel heißt wie, mein Protokoll weist eine variable Länge auf.

    Danke im Voraus!



  • Ja, der Header ist wie angegeben, die Paketlänge ist variabel (wie denn sonst).



  • Hallo TyRoXx ,

    ich habe deinen Code eingetippt und wiederum verusucht die Standardwerte zu bekommen und bin daber auf ein paar kleine Fehler gestoßen. Nach deinem Code sind jeweils 32-Bit für die ID vergeben, das wiederum ein uint32 ist. Mit

    std::string id;
    id.append(packet,0,4);
    

    kann ich den String herauslesen. wie bekomme ich aus dieser wieder ein uint32 ?



  • TyRoXx ich hätte da noch eine Frage bzw. eine Bemerkung. Das Auslesen der Zahlenwerte für die ID funktioniert wunderbar in Bereich von 0-127 sobald die Zahl größer ist kommt Unfug heraus. Wie könnte dies behoben werden?



  • Beobachter01 schrieb:

    std::string id;
    id.append(packet,0,4);
    

    kann ich den String herauslesen. wie bekomme ich aus dieser wieder ein uint32 ?

    Interessante Idee, aber den string id kann man sich sparen.
    Du holst dir mit &packet[0] einen Zeiger auf den Speicher und kopierst mit memcpy in die Variablen hinein. Um die nächste Variable zu erhalten, einfach den Zeiger erhöhen:

    std::string incoming;
    const char *position = &incoming[0];
    const char * const end = position + incoming.size();
    
    std::uint32_t dataId;
    if (position + sizeof(dataId) > end)
    {
    	//unvollständiges Paket behandeln..
    }
    std::memcpy(&dataId, position, sizeof(dataId));
    position += sizeof(dataId);
    
    //nächster Wert genauso..
    

    J.Wayne schrieb:

    TyRoXx ich hätte da noch eine Frage bzw. eine Bemerkung. Das Auslesen der Zahlenwerte für die ID funktioniert wunderbar in Bereich von 0-127 sobald die Zahl größer ist kommt Unfug heraus. Wie könnte dies behoben werden?

    Keine Ahnung, wie liest du denn aus?



  • Ich dachte memcpy gilt nur string bzw char* :S naja danke dir TyRoXx.
    Wie liest man die Länge und den eingegebenen String aus. Mit Zeiger funktioniert es nicht so ganz!



  • Beobachter01 schrieb:

    Ich dachte memcpy gilt nur string bzw char* :S naja danke dir TyRoXx.
    Wie liest man die Länge und den eingegebenen String aus. Mit Zeiger funktioniert es nicht so ganz!

    Dann zeig mal deinen Ansatz.



  • uint32_t dataId;
    	uint32_t timed;
    	std::string payload;
    	memcpy(&dataId, position, sizeof(dataId));
    	position +=sizeof(dataId);
    	memcpy(&timed, position, sizeof(timed));
    	position +=sizeof(timed);
    	memcpy(&payload, position, sizeof(payload));
    

    Habe es so gemacht mit Zeiger und Co. Wie gesagt funktionieren die Ersten beiden Werte wunderbar, beim Dritten bekomme ich mit cout folgendes

    1730(erster Wert) 1024(zweiter Wert) hallo ===========²²²² und ganz viele komische Zeichen im Anschluss. zu guter Letzt kommt ein Debug- Fehler



  • Hallo,

    ich bekomme irgendwie es nicht hin nach der Versendung über TCP, dass packet wieder auszulesen. Bin irgendwie fürs programmieren nicht geeignet bzw der Umstieg von Java auf C++ war doch nicht so.



  • Die Komplettlösung für die Allgemeinheit:

    #include <string> 
    #include <cstring> 
    #include <cassert> 
    #include <cstdint> 
    
    typedef std::string Buffer; 
    
    struct BinaryWriter 
    { 
      Buffer &buffer; 
    
      explicit BinaryWriter(Buffer &buffer) 
        : buffer(buffer) 
      { 
      } 
    
      void write(const char *data, size_t size) 
      { 
        buffer.append(data, data + size); 
      } 
    
      void overwrite(size_t position, const char *data, size_t size) 
      { 
        assert(position + size <= buffer.size()); 
        std::memcpy(&buffer[position], data, size); 
      } 
    
      template <class POD> 
      void write(POD value) 
      { 
        write(reinterpret_cast<const char *>(&value), sizeof(value)); 
      } 
    
      template <class POD> 
      void overwrite(size_t position, POD value) 
      { 
        overwrite(position, reinterpret_cast<const char *>(&value), sizeof(value)); 
      } 
    
      void writeString32(const std::string &str) 
      { 
        write(static_cast<std::uint32_t>(str.size())); 
        write(str.data(), str.size()); 
      } 
    };
    
    struct PacketWriter : BinaryWriter 
    { 
      size_t sizePosition; 
      size_t payloadPosition; 
    
      PacketWriter(Buffer &buffer, std::uint32_t id, std::uint32_t time) 
        : BinaryWriter(buffer) 
      { 
        write(id); 
        write(time); 
    
        //die Position der Länge für spätere Korrektur speichern 
        sizePosition = buffer.size(); 
        write(std::uint32_t(0)); 
    
        //um die Länge des Inhaltes auszurechnen 
        payloadPosition = buffer.size(); 
      } 
    
      void finish() 
      { 
        size_t currentPos = buffer.size(); 
        std::uint32_t length = static_cast<std::uint32_t>(currentPos - payloadPosition); 
    
        overwrite(sizePosition, length); 
      } 
    };
    
    struct BinaryReader
    {
      const char *begin, *pos, *end;
    
      BinaryReader()
        : begin(0)
        , pos(0)
        , end(0)
      {
      }
    
      BinaryReader(const char *begin, const char *end)
        : begin(begin)
        , pos(begin)
        , end(end)
      {
      }
    
      size_t remaining() const
      {
        return (end - pos);
      }
    
      bool read(char *dest, size_t size)
      {
        if (size > remaining())
        {
          return false;
        }
    
        std::memcpy(dest, pos, size);
        pos += size;
        return true;
      }
    
      template <class POD>
      bool read(POD &value)
      {
        return read(reinterpret_cast<char *>(&value), sizeof(value));
      }
    
      bool readString32(std::string &dest)
      {
        std::uint32_t length;
        if (read(length))
        {
          dest.resize(length);
          if (length)
          {
            return read(&dest[0], length);
          }
    
          return true;
        }
    
        return false;
      }
    };
    
    struct PacketReader : BinaryReader
    {
      std::uint32_t id, time;
      BinaryReader body;
    
      PacketReader(const char *begin, const char *end)
        : BinaryReader(begin, end)
      {
      }
    
      bool parseHeader()
      {
        std::uint32_t length;
    
        if (
          read(id) &&
          read(time) &&
          read(length) &&
          (remaining() >= length))
        {
          body.begin = body.pos = this->pos;
          body.end = body.begin + length;
    
          //Inhalt überspringen
          this->pos += length;
          return true;
        }
    
        return false;
      }
    };
    
    #include <iostream>
    using namespace std;
    
    int main() 
    { 
      Buffer packet; 
    
      {
        PacketWriter writer(packet, 123, 456); 
    
        std::string message = "hallo"; 
        writer.writeString32(message); 
        writer.write<std::uint16_t>(42); //ja, hier können beliebige weitere Werte folgen
    
        writer.finish();
    
        //packet kann nun versendet werden.. 
      }
    
      //empfangen..
      {
        PacketReader reader(&packet[0], &packet[0] + packet.size());
        if (!reader.parseHeader())
        {
          //nach dem nächsten receive nochmal versuchen..
          cout << "Incomplete packet" << endl;
          return 1;
        }
    
        std::string message;
        reader.body.readString32(message); //Ergebnis ggf. prüfen
    
        cout << message << endl;
    
        std::uint16_t value;
        reader.body.read(value);
    
        cout << value << endl;
    
        //das gelesene Paket aus dem Puffer entfernen.
        //clear() ist ungeeignet, denn es können weitere Pakete im Puffer liegen.
        const size_t parsed = (reader.pos - reader.begin);
        packet.erase(
          packet.begin(),
          packet.begin() + parsed);
      }
    }
    

Anmelden zum Antworten